| import { isEqual, capitalize } from 'element-ui/src/utils/util'; | 
| import { isDef } from 'element-ui/src/utils/shared'; | 
|   | 
| let uid = 0; | 
|   | 
| export default class Node { | 
|   | 
|   constructor(data, config, parentNode) { | 
|     this.data = data; | 
|     this.config = config; | 
|     this.parent = parentNode || null; | 
|     this.level = !this.parent ? 1 : this.parent.level + 1; | 
|     this.uid = uid++; | 
|   | 
|     this.initState(); | 
|     this.initChildren(); | 
|   } | 
|   | 
|   initState() { | 
|     const { value: valueKey, label: labelKey } = this.config; | 
|   | 
|     this.value = this.data[valueKey]; | 
|     this.label = this.data[labelKey]; | 
|     this.pathNodes = this.calculatePathNodes(); | 
|     this.path = this.pathNodes.map(node => node.value); | 
|     this.pathLabels = this.pathNodes.map(node => node.label); | 
|   | 
|     // lazy load | 
|     this.loading = false; | 
|     this.loaded = false; | 
|   } | 
|   | 
|   initChildren() { | 
|     const { config } = this; | 
|     const childrenKey = config.children; | 
|     const childrenData = this.data[childrenKey]; | 
|     this.hasChildren = Array.isArray(childrenData); | 
|     this.children = (childrenData || []).map(child => new Node(child, config, this)); | 
|   } | 
|   | 
|   get isDisabled() { | 
|     const { data, parent, config } = this; | 
|     const disabledKey = config.disabled; | 
|     const { checkStrictly } = config; | 
|     return data[disabledKey] || | 
|       !checkStrictly && parent && parent.isDisabled; | 
|   } | 
|   | 
|   get isLeaf() { | 
|     const { data, loaded, hasChildren, children } = this; | 
|     const { lazy, leaf: leafKey } = this.config; | 
|     if (lazy) { | 
|       const isLeaf = isDef(data[leafKey]) | 
|         ? data[leafKey] | 
|         : (loaded ? !children.length : false); | 
|       this.hasChildren = !isLeaf; | 
|       return isLeaf; | 
|     } | 
|     return !hasChildren; | 
|   } | 
|   | 
|   calculatePathNodes() { | 
|     const nodes = [this]; | 
|     let parent = this.parent; | 
|   | 
|     while (parent) { | 
|       nodes.unshift(parent); | 
|       parent = parent.parent; | 
|     } | 
|   | 
|     return nodes; | 
|   } | 
|   | 
|   getPath() { | 
|     return this.path; | 
|   } | 
|   | 
|   getValue() { | 
|     return this.value; | 
|   } | 
|   | 
|   getValueByOption() { | 
|     return this.config.emitPath | 
|       ? this.getPath() | 
|       : this.getValue(); | 
|   } | 
|   | 
|   getText(allLevels, separator) { | 
|     return allLevels ? this.pathLabels.join(separator) : this.label; | 
|   } | 
|   | 
|   isSameNode(checkedValue) { | 
|     const value = this.getValueByOption(); | 
|     return this.config.multiple && Array.isArray(checkedValue) | 
|       ? checkedValue.some(val => isEqual(val, value)) | 
|       : isEqual(checkedValue, value); | 
|   } | 
|   | 
|   broadcast(event, ...args) { | 
|     const handlerName = `onParent${capitalize(event)}`; | 
|   | 
|     this.children.forEach(child => { | 
|       if (child) { | 
|         // bottom up | 
|         child.broadcast(event, ...args); | 
|         child[handlerName] && child[handlerName](...args); | 
|       } | 
|     }); | 
|   } | 
|   | 
|   emit(event, ...args) { | 
|     const { parent } = this; | 
|     const handlerName = `onChild${capitalize(event)}`; | 
|     if (parent) { | 
|       parent[handlerName] && parent[handlerName](...args); | 
|       parent.emit(event, ...args); | 
|     } | 
|   } | 
|   | 
|   onParentCheck(checked) { | 
|     if (!this.isDisabled) { | 
|       this.setCheckState(checked); | 
|     } | 
|   } | 
|   | 
|   onChildCheck() { | 
|     const { children } = this; | 
|     const validChildren = children.filter(child => !child.isDisabled); | 
|     const checked = validChildren.length | 
|       ? validChildren.every(child => child.checked) | 
|       : false; | 
|   | 
|     this.setCheckState(checked); | 
|   } | 
|   | 
|   setCheckState(checked) { | 
|     const totalNum = this.children.length; | 
|     const checkedNum = this.children.reduce((c, p) => { | 
|       const num = p.checked ? 1 : (p.indeterminate ? 0.5 : 0); | 
|       return c + num; | 
|     }, 0); | 
|   | 
|     this.checked = checked; | 
|     this.indeterminate = checkedNum !== totalNum && checkedNum > 0; | 
|   } | 
|   | 
|   syncCheckState(checkedValue) { | 
|     const value = this.getValueByOption(); | 
|     const checked = this.isSameNode(checkedValue, value); | 
|   | 
|     this.doCheck(checked); | 
|   } | 
|   | 
|   doCheck(checked) { | 
|     if (this.checked !== checked) { | 
|       if (this.config.checkStrictly) { | 
|         this.checked = checked; | 
|       } else { | 
|         // bottom up to unify the calculation of the indeterminate state | 
|         this.broadcast('check', checked); | 
|         this.setCheckState(checked); | 
|         this.emit('check'); | 
|       } | 
|     } | 
|   } | 
| } |