1 /**
  2  * @fileOverview abstraction of tree node ,root and other node will extend it
  3  * @author yiminghe@gmail.com
  4  */
  5 KISSY.add("tree/basenode", function (S, Node, Component, BaseNodeRender) {
  6     var $ = Node.all,
  7         KeyCodes = Node.KeyCodes;
  8 
  9 
 10     /**
 11      * @class
 12      * Tree Node.
 13      * xclass: 'tree-item'.
 14      * @name Node
 15      * @memberOf Tree
 16      * @extends Component.Controller
 17      */
 18     var BaseNode = Component.Controller.extend(
 19         /*
 20          * 可多继承从某个子节点开始装饰儿子组件
 21          */
 22         [Component.DecorateChild],
 23         /**
 24          * @lends Tree.Node#
 25          */
 26         {
 27             _keyNav:function (e) {
 28                 var self = this,
 29                     processed = true,
 30                     n,
 31                     children = self.get("children"),
 32                     keyCode = e.keyCode;
 33 
 34                 // 顺序统统为前序遍历顺序
 35                 switch (keyCode) {
 36                     // home
 37                     // 移到树的顶层节点
 38                     case KeyCodes.HOME:
 39                         n = self.get("tree");
 40                         break;
 41 
 42                     // end
 43                     // 移到最后一个可视节点
 44                     case KeyCodes.END:
 45                         n = self.get("tree").getLastVisibleDescendant();
 46                         break;
 47 
 48                     // 上
 49                     // 当前节点的上一个兄弟节点的最后一个可显示节点
 50                     case KeyCodes.UP:
 51                         n = self.getPreviousVisibleNode();
 52                         break;
 53 
 54                     // 下
 55                     // 当前节点的下一个可显示节点
 56                     case KeyCodes.DOWN:
 57                         n = self.getNextVisibleNode();
 58                         break;
 59 
 60                     // 左
 61                     // 选择父节点或 collapse 当前节点
 62                     case KeyCodes.LEFT:
 63                         if (self.get("expanded") && (children.length || self.get("isLeaf") === false)) {
 64                             self.set("expanded", false);
 65                         } else {
 66                             n = self.get("parent");
 67                         }
 68                         break;
 69 
 70                     // 右
 71                     // expand 当前节点
 72                     case KeyCodes.RIGHT:
 73                         if (children.length || self.get("isLeaf") === false) {
 74                             if (!self.get("expanded")) {
 75                                 self.set("expanded", true);
 76                             } else {
 77                                 children[0].select();
 78                             }
 79                         }
 80                         break;
 81 
 82                     default:
 83                         processed = false;
 84                         break;
 85 
 86                 }
 87                 if (n) {
 88                     n.select();
 89                 }
 90                 return processed;
 91             },
 92 
 93             getLastVisibleDescendant:function () {
 94                 var self = this, children = self.get("children");
 95                 // 没有展开或者根本没有儿子节点,可视的只有自己
 96                 if (!self.get("expanded") || !children.length) {
 97                     return self;
 98                 }
 99                 // 可视的最后一个子孙
100                 return children[children.length - 1].getLastVisibleDescendant();
101             },
102 
103             getNextVisibleNode:function () {
104                 var self = this,
105                     children = self.get("children"),
106                     parent = self.get("parent");
107                 if (self.get("expanded") && children.length) {
108                     return children[0];
109                 }
110                 // 没有展开或者根本没有儿子节点
111                 // 深度遍历的下一个
112                 var n = self.next();
113                 while (parent && !n) {
114                     n = parent.next();
115                     parent = parent.get("parent");
116                 }
117                 return n;
118             },
119 
120             getPreviousVisibleNode:function () {
121                 var self = this, prev = self.prev();
122                 if (!prev) {
123                     prev = self.get("parent");
124                 } else {
125                     prev = prev.getLastVisibleDescendant();
126                 }
127                 return prev;
128             },
129 
130             next:function () {
131                 var self = this, parent = self.get("parent");
132                 if (!parent) {
133                     return null;
134                 }
135                 var siblings = parent.get('children');
136                 var index = S.indexOf(self, siblings);
137                 if (index == siblings.length - 1) {
138                     return null;
139                 }
140                 return siblings[index + 1];
141             },
142 
143             prev:function () {
144                 var self = this, parent = self.get("parent");
145                 if (!parent) {
146                     return null;
147                 }
148                 var siblings = parent.get('children');
149                 var index = S.indexOf(self, siblings);
150                 if (index === 0) {
151                     return null;
152                 }
153                 return siblings[index - 1];
154             },
155 
156             /**
157              * Select current tree node.
158              */
159             select:function () {
160                 var self = this;
161                 self.get("tree").set("selectedItem", self);
162             },
163 
164             performActionInternal:function (e) {
165                 var self = this,
166                     target = $(e.target),
167                     tree = self.get("tree"),
168                     view = self.get("view");
169                 tree.get("el")[0].focus();
170                 if (target.equals(view.get("expandIconEl"))) {
171                     // 忽略双击
172                     if (e.type != 'dblclick') {
173                         self.set("expanded", !self.get("expanded"));
174                     }
175                 } else if (e.type == 'dblclick') {
176                     self.set("expanded", !self.get("expanded"));
177                 }
178                 else {
179                     self.select();
180                     tree.fire("click", {
181                         target:self
182                     });
183                 }
184             },
185 
186             // 默认 addChild,这里需要设置 tree 属性
187             decorateChildrenInternal:function (ui, c) {
188                 var self = this;
189                 self.addChild(new ui({
190                     srcNode:c,
191                     tree:self.get("tree"),
192                     prefixCls:self.get("prefixCls")
193                 }));
194             },
195 
196             addChild:function (c) {
197                 var self = this, tree = self.get("tree");
198                 c.__set("tree", tree);
199                 c.__set("depth", self.get('depth') + 1);
200                 BaseNode.superclass.addChild.call(self, c);
201                 self._updateRecursive();
202                 tree._register(c);
203                 S.each(c.get("children"), function (cc) {
204                     tree._register(cc);
205                 });
206             },
207 
208             /*
209              每次添加/删除节点,都检查自己以及自己子孙 class
210              每次 expand/collapse,都检查
211              */
212             _computeClass:function (cause) {
213                 var self = this, view = self.get("view");
214                 view._computeClass(self.get('children'), self.get("parent"), cause);
215             },
216 
217             _updateRecursive:function () {
218                 var self = this,
219                     len = self.get('children').length;
220                 self._computeClass("_updateRecursive");
221                 S.each(self.get("children"), function (c, index) {
222                     c._computeClass("_updateRecursive_children");
223                     c.get("view").set("ariaPosInSet", index + 1);
224                     c.get("view").set("ariaSize", len);
225                 });
226             },
227 
228             removeChild:function (c) {
229                 var self = this,
230                     tree = self.get("tree");
231                 tree._unRegister(c);
232                 S.each(c.get("children"), function (cc) {
233                     tree._unRegister(cc);
234                 });
235                 BaseNode.superclass.removeChild.apply(self, S.makeArray(arguments));
236                 self._updateRecursive();
237             },
238 
239             _uiSetExpanded:function (v) {
240                 var self = this,
241                     tree = self.get("tree");
242                 self._computeClass("expanded-" + v);
243                 if (v) {
244                     tree.fire("expand", {
245                         target:self
246                     });
247                 } else {
248                     tree.fire("collapse", {
249                         target:self
250                     });
251                 }
252             },
253 
254             /**
255              * Expand all descend nodes of current node
256              */
257             expandAll:function () {
258                 var self = this;
259                 self.set("expanded", true);
260                 S.each(self.get("children"), function (c) {
261                     c.expandAll();
262                 });
263             },
264 
265             /**
266              * Collapse all descend nodes of current node
267              */
268             collapseAll:function () {
269                 var self = this;
270                 self.set("expanded", false);
271                 S.each(self.get("children"), function (c) {
272                     c.collapseAll();
273                 });
274             }
275         },
276 
277         {
278             ATTRS:/**
279              * @lends Tree.Node#
280              */
281             {
282                 xrender:{
283                     value:BaseNodeRender
284                 },
285                 // 事件代理
286                 handleMouseEvents:{
287                     value:false
288                 },
289 
290                 /**
291                  * Only For Config.
292                  * Whether to force current tree node as a leaf.
293                  * Default:false.
294                  * It will change as children are added.
295                  * @type Boolean
296                  */
297                 isLeaf:{
298                     view:1
299                 },
300 
301                 /**
302                  * Element for expand icon.
303                  * @type {NodeList}
304                  */
305                 expandIconEl:{
306                     view:1
307                 },
308 
309                 /**
310                  * Element for icon.
311                  * @type {NodeList}
312                  */
313                 iconEl:{
314                     view:1
315                 },
316 
317                 /**
318                  * Whether current tree node is selected.
319                  * @type Boolean
320                  */
321                 selected:{
322                     view:1
323                 },
324 
325                 /**
326                  * Whether current tree node is expanded.
327                  */
328                 expanded:{
329                     view:1
330                 },
331 
332                 /**
333                  * Html title for current tree node.
334                  * @type String
335                  */
336                 tooltip:{
337                     view:1
338                 },
339 
340                 /**
341                  * Tree instance current tree node belongs to.
342                  * @type Tree
343                  */
344                 tree:{
345                 },
346 
347                 /**
348                  * depth of node.
349                  * @type Number
350                  */
351                 depth:{
352                     view:1
353                 },
354                 focusable:{
355                     value:false
356                 },
357                 decorateChildCls:{
358                     value:"ks-tree-children"
359                 }
360             }
361         }, {
362             xclass:'tree-item',
363             priority:10
364         });
365 
366     return BaseNode;
367 
368 }, {
369     requires:['node', 'component', './basenodeRender']
370 });