fancytree-rails 0.0.2 → 2.0.0.pre.6.pre.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9ebf252a72d7d58d7dd53ab117ede2a29014d8e3
4
- data.tar.gz: 944636f3e54a9828e6be915a29a445d68f4b54d6
3
+ metadata.gz: 5f3bd0723ab5734e8be4fb9538142c604bc0e103
4
+ data.tar.gz: 70027741a2c1b9432fac77556e7e7ba4d8238961
5
5
  SHA512:
6
- metadata.gz: 231206cf4884feddad76111302b6f155f65795c91de554f843568c9862c9610ce3b45a5e88c17661eacd724b7512b3d50f1ebe53dcb6d12afe2440f996d54acb
7
- data.tar.gz: c3f26333be2af6d342851dd0ea445ce800f68a94751864f81a21c6aa73f3e7c2b0ce97ef8df8b3a45e6ee4a085d0855da5d99e9f6ba55b8e3399d9205787e65a
6
+ metadata.gz: 755155660f79623e8c76001669dece17ecc56111fd9fd680e20dfd35b7dc51a8cfea71aca2f7923e4435c028d0be889d7cb3946549dc09ca45948c089e7d70fb
7
+ data.tar.gz: 7c2d1befb9f1661c6c0c91129064e3909fd2fd231ab8fa582892e8b2b2b9d501c75583e0f25be1b2beea14b3b52397a43ad469f6de8a35ae3ae17f810be2bfdd
@@ -1,3 +1,8 @@
1
+ ## v2.0.0-6-1
2
+
3
+ * align versions with FancyTree
4
+ * v2.0.0-6 of FancyTree
5
+
1
6
  ## v0.0.2
2
7
 
3
8
  * fix icons paths
data/README.md CHANGED
@@ -48,10 +48,10 @@ or if you want to use another style include it directly:
48
48
  *= require fancytree/skin-awesome/ui.fancytree
49
49
  ```
50
50
  Note that for awesome style (extension) to work you to:
51
- 1) Add ```font-awesome-rails``` gem (and include its style in
52
- application.css)
53
- 2) Enable ```awesome``` extension in Fancytree.
54
- 3) Include extension code (```//= require fancytree/jquery.fancytree.awesome```) to ```app/assets/javascripts/application.js```.
51
+
52
+ 1. Add ```font-awesome-rails``` gem (and include its style in application.css)
53
+ 2. Enable ```awesome``` extension in Fancytree.
54
+ 3. Include extension code (```//= require fancytree/jquery.fancytree.awesome```) to ```app/assets/javascripts/application.js```.
55
55
 
56
56
  ## Contributing
57
57
 
data/Rakefile CHANGED
@@ -24,7 +24,7 @@ namespace :fancytree do
24
24
  end
25
25
 
26
26
  #following files are not in the build yet:
27
- FileUtils.cp(['src/jquery.fancytree.gridnav.js', 'src/jquery.fancytree.awesome.js'], '../../vendor/assets/javascripts/fancytree')
27
+ FileUtils.cp(['src/jquery.fancytree.awesome.js'], '../../vendor/assets/javascripts/fancytree')
28
28
 
29
29
  end
30
30
  end
@@ -1,6 +1,6 @@
1
1
  module Fancytree
2
2
  module Rails
3
- VERSION = "0.0.2"
4
- FANCYTREE_VERSION="v2.0.0-5"
3
+ VERSION = "2.0.0-6-1"
4
+ FANCYTREE_VERSION="v2.0.0-6"
5
5
  end
6
6
  end
@@ -3,14 +3,18 @@
3
3
  * Dynamic tree view control, with support for lazy loading of branches.
4
4
  * https://github.com/mar10/fancytree/
5
5
  *
6
- * Copyright (c) 2006-2013, Martin Wendt (http://wwWendt.de)
6
+ * Copyright (c) 2006-2014, Martin Wendt (http://wwWendt.de)
7
7
  * Released under the MIT license
8
8
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
9
9
  *
10
- * @version 2.0.0-5
11
- * @date 2014-01-04T16:42
10
+ * @version 2.0.0-6
11
+ * @date 2014-02-10T10:52
12
12
  */
13
13
 
14
+ /** Core Fancytree module.
15
+ */
16
+
17
+
14
18
  // Start of local namespace
15
19
  ;(function($, window, document, undefined) {
16
20
  "use strict";
@@ -174,7 +178,7 @@ function _makeNodeTitleMatcher(s){
174
178
 
175
179
  var i,
176
180
  FT = null, // initialized below
177
- //Boolean attributes that can be set with equivalent class names in the LI tags
181
+ //boolean attributes that can be set with equivalent class names in the LI tags
178
182
  CLASS_ATTRS = "active expanded focus folder lazy selected unselectable".split(" "),
179
183
  CLASS_ATTR_MAP = {},
180
184
  // Top-level Fancytree node attributes, that can be set by dict
@@ -193,29 +197,29 @@ for(i=0; i<NODE_ATTRS.length; i++){ NODE_ATTR_MAP[NODE_ATTRS[i]] = true; }
193
197
 
194
198
 
195
199
  /**
196
- * Creates a new node
197
- * @class Represents the hierarchical data model and operations.
198
- * @name FancytreeNode
199
- * @constructor
200
+ * Creates a new FancytreeNode
201
+ *
202
+ * @class FancytreeNode
203
+ * @classdesc A FancytreeNode represents the hierarchical data model and operations.
204
+ *
200
205
  * @param {FancytreeNode} parent
201
- * @param {NodeData} data
206
+ * @param {NodeData} obj
202
207
  *
203
208
  * @property {Fancytree} tree
204
209
  * @property {FancytreeNode} parent Parent node
205
- * @property {String} key
206
- * @property {String} title
210
+ * @property {string} key
211
+ * @property {string} title
207
212
  * @property {object} data Contains all extra data that was passed on node creation
208
213
  * @property {FancytreeNode[] | null | undefined} children list of child nodes
209
- * @property {Boolean} isStatusNode
210
- * @property {Boolean} expanded
211
- * @property {Boolean} folder
212
- * @property {Boolean} href
213
- * @property {String} extraClasses
214
- * @property {Boolean} lazy
215
- * @property {Boolean} nolink OBSOLETE
216
- * @property {Boolean} selected
217
- * @property {String} target
218
- * @property {String} tooltip
214
+ * @property {boolean} isStatusNode
215
+ * @property {boolean} expanded
216
+ * @property {boolean} folder
217
+ * @property {string} extraClasses
218
+ * @property {boolean} lazy
219
+ * @property {boolean} selected
220
+ * @property {string} tooltip
221
+ * @property {string} data.href
222
+ * @property {string} data.target
219
223
  */
220
224
  function FancytreeNode(parent, obj){
221
225
  var i, l, name, cl;
@@ -266,7 +270,7 @@ function FancytreeNode(parent, obj){
266
270
  }
267
271
 
268
272
 
269
- FancytreeNode.prototype = /**@lends FancytreeNode*/{
273
+ FancytreeNode.prototype = /** @lends FancytreeNode# */{
270
274
  /* Return the direct child FancytreeNode with a given key, index. */
271
275
  _findDirectChild: function(ptr){
272
276
  var i, l,
@@ -301,16 +305,11 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
301
305
  * Append (or insert) a list of child nodes.
302
306
  *
303
307
  * @param {NodeData[]} children array of child node definitions (also single child accepted)
304
- * @param {FancytreeNode | String | Integer} [insertBefore] child node (or key or index of such).
308
+ * @param {FancytreeNode | string | Integer} [insertBefore] child node (or key or index of such).
305
309
  * If omitted, the new children are appended.
306
310
  * @returns {FancytreeNode} first child added
307
311
  *
308
- * @see applyPatch to modify existing child nodes.
309
- * @see FanctreeNode.applyPatch to modify existing child nodes.
310
- * @see FanctreeNode#applyPatch to modify existing child nodes.
311
- * @see applyPatch
312
- * @see FanctreeNode.applyPatch
313
- * @see FanctreeNode#applyPatch
312
+ * @see FancytreeNode#applyPatch
314
313
  */
315
314
  addChildren: function(children, insertBefore){
316
315
  var i, l, pos,
@@ -348,8 +347,10 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
348
347
  /**
349
348
  * Append or prepend a node, or append a child node.
350
349
  *
350
+ * This a convenience function that calls addChildren()
351
+ *
351
352
  * @param {NodeData} node node definition
352
- * @param {String} [mode] 'before', 'after', or 'child'
353
+ * @param {string} [mode=child] 'before', 'after', or 'child' ('over' is a synonym for 'child')
353
354
  * @returns {FancytreeNode} new node
354
355
  */
355
356
  addNode: function(node, mode){
@@ -368,10 +369,10 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
368
369
  _assert(false, "Invalid mode: " + mode);
369
370
  },
370
371
  /**
372
+ * Modify existing child nodes.
371
373
  *
372
374
  * @param {NodePatch} patch
373
375
  * @returns {$.Promise}
374
- * @see {@link applyPatch} to modify existing child nodes.
375
376
  * @see FancytreeNode#addChildren
376
377
  */
377
378
  applyPatch: function(patch) {
@@ -425,7 +426,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
425
426
  /** Copy this node as sibling or child of `node`.
426
427
  *
427
428
  * @param {FancytreeNode} node source node
428
- * @param {String} mode 'before' | 'after' | 'child'
429
+ * @param {string} mode 'before' | 'after' | 'child'
429
430
  * @param {Function} [map] callback function(NodeData) that could modify the new node
430
431
  * @returns {FancytreeNode} new
431
432
  */
@@ -434,7 +435,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
434
435
  },
435
436
  /** Count direct and indirect children.
436
437
  *
437
- * @param {Boolean} [deep=true] pass 'false' to only count direct children
438
+ * @param {boolean} [deep=true] pass 'false' to only count direct children
438
439
  * @returns {int} number of child nodes
439
440
  */
440
441
  countChildren: function(deep) {
@@ -471,7 +472,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
471
472
  // TODO: expand(flag)
472
473
  /**Find all nodes that contain `match` in the title.
473
474
  *
474
- * @param {String | function(node)} match string to search for, of a function that
475
+ * @param {string | function(node)} match string to search for, of a function that
475
476
  * returns `true` if a node is matched.
476
477
  * @returns {FancytreeNode[]} array of nodes (may be empty)
477
478
  * @see FancytreeNode#findAll
@@ -488,7 +489,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
488
489
  },
489
490
  /**Find first node that contains `match` in the title (not including self).
490
491
  *
491
- * @param {String | function(node)} match string to search for, of a function that
492
+ * @param {string | function(node)} match string to search for, of a function that
492
493
  * returns `true` if a node is matched.
493
494
  * @returns {FancytreeNode} matching node or null
494
495
  * @example
@@ -673,7 +674,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
673
674
  // return this.parent.children.indexOf(this);
674
675
  return $.inArray(this, this.parent.children); // indexOf doesn't work in IE7
675
676
  },
676
- /**@returns {String} hierarchical child index (1-based: '3.2.4').*/
677
+ /**@returns {string} hierarchical child index (1-based: '3.2.4').*/
677
678
  getIndexHier: function(separator) {
678
679
  separator = separator || ".";
679
680
  var res = [];
@@ -683,8 +684,8 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
683
684
  return res.join(separator);
684
685
  },
685
686
  /**
686
- * @param {Boolean} [excludeSelf=false]
687
- * @returns {String} parent keys separated by options.keyPathSeparator
687
+ * @param {boolean} [excludeSelf=false]
688
+ * @returns {string} parent keys separated by options.keyPathSeparator
688
689
  */
689
690
  getKeyPath: function(excludeSelf) {
690
691
  var path = [],
@@ -731,8 +732,8 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
731
732
  return this.parent;
732
733
  },
733
734
  /**
734
- * @param {Boolean} [includeRoot=false]
735
- * @param {Boolean} [includeSelf=false]
735
+ * @param {boolean} [includeRoot=false]
736
+ * @param {boolean} [includeSelf=false]
736
737
  * @returns {FancytreeNode[]}
737
738
  */
738
739
  getParentList: function(includeRoot, includeSelf) {
@@ -777,24 +778,24 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
777
778
  }
778
779
  return !!this.children;
779
780
  },
780
- /**@returns {Boolean} true, if node has keyboard focus*/
781
+ /**@returns {boolean} true, if node has keyboard focus*/
781
782
  hasFocus: function() {
782
783
  return (this.tree.hasFocus() && this.tree.focusNode === this);
783
784
  },
784
- /**@returns {Boolean} true, if node is active*/
785
+ /**@returns {boolean} true, if node is active*/
785
786
  isActive: function() {
786
787
  return (this.tree.activeNode === this);
787
788
  },
788
789
  /**
789
790
  * @param {FancytreeNode} otherNode
790
- * @returns {Boolean} true, if node is a direct child of otherNode
791
+ * @returns {boolean} true, if node is a direct child of otherNode
791
792
  */
792
793
  isChildOf: function(otherNode) {
793
794
  return (this.parent && this.parent === otherNode);
794
795
  },
795
796
  /**
796
797
  * @param {FancytreeNode} otherNode
797
- * @returns {Boolean} true, if node is a sub node of otherNode
798
+ * @returns {boolean} true, if node is a sub node of otherNode
798
799
  */
799
800
  isDescendantOf: function(otherNode) {
800
801
  if(!otherNode || otherNode.tree !== this.tree){
@@ -809,37 +810,37 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
809
810
  }
810
811
  return false;
811
812
  },
812
- /** @returns {Boolean} true, if node is expanded*/
813
+ /** @returns {boolean} true, if node is expanded*/
813
814
  isExpanded: function() {
814
815
  return !!this.expanded;
815
816
  },
816
- /** @returns {Boolean}*/
817
+ /** @returns {boolean}*/
817
818
  isFirstSibling: function() {
818
819
  var p = this.parent;
819
820
  return !p || p.children[0] === this;
820
821
  },
821
- /** @returns {Boolean}*/
822
+ /** @returns {boolean}*/
822
823
  isFolder: function() {
823
824
  return !!this.folder;
824
825
  },
825
- /** @returns {Boolean}*/
826
+ /** @returns {boolean}*/
826
827
  isLastSibling: function() {
827
828
  var p = this.parent;
828
829
  return !p || p.children[p.children.length-1] === this;
829
830
  },
830
- /** @returns {Boolean} true, if node is lazy (even if data was already loaded)*/
831
+ /** @returns {boolean} true, if node is lazy (even if data was already loaded)*/
831
832
  isLazy: function() {
832
833
  return !!this.lazy;
833
834
  },
834
- /** @returns {Boolean} true, if children are currently beeing loaded*/
835
+ /** @returns {boolean} true, if children are currently beeing loaded*/
835
836
  isLoading: function() {
836
837
  _raiseNotImplemented(); // TODO: implement
837
838
  },
838
- /**@returns {Boolean} true, if node is the (invisible) system root node*/
839
+ /**@returns {boolean} true, if node is the (invisible) system root node*/
839
840
  isRoot: function() {
840
841
  return (this.tree.rootNode === this);
841
842
  },
842
- /** @returns {Boolean} true, if node is selected (e.g. has a checkmark set)*/
843
+ /** @returns {boolean} true, if node is selected (e.g. has a checkmark set)*/
843
844
  isSelected: function() {
844
845
  return !!this.selected;
845
846
  },
@@ -872,7 +873,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
872
873
  },
873
874
  /** Move this node to targetNode.
874
875
  * @param {FancytreeNode} targetNode
875
- * @param {String} mode
876
+ * @param {string} mode
876
877
  * 'child': append this node as last child of targetNode.
877
878
  * This is the default. To be compatble with the D'n'd
878
879
  * hitMode, we also accept 'over'.
@@ -893,7 +894,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
893
894
  }else if( !this.parent ){
894
895
  throw "Cannot move system root";
895
896
  }else if( targetParent.isDescendantOf(this) ){
896
- throw "Cannot move a node to it's own descendant";
897
+ throw "Cannot move a node to its own descendant";
897
898
  }
898
899
  // Unlink this node from current parent
899
900
  if( this.parent.children.length === 1 ) {
@@ -963,10 +964,10 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
963
964
  }, true);
964
965
  }
965
966
 
966
- // A collaposed node won't re-render children, so we have to remove it manually
967
- if( !targetParent.expanded){
968
- prevParent.ul.removeChild(this.li);
969
- }
967
+ // A collaposed node won't re-render children, so we have to remove it manually
968
+ // if( !targetParent.expanded ){
969
+ // prevParent.ul.removeChild(this.li);
970
+ // }
970
971
 
971
972
  // Update HTML markup
972
973
  if( !prevParent.isDescendantOf(targetParent)) {
@@ -1031,6 +1032,12 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1031
1032
  function _goto(n){
1032
1033
  if( n ){
1033
1034
  n.makeVisible();
1035
+ // Node may still be hidden by a filter
1036
+ if( ! $(n.span).is(":visible") ) {
1037
+ n.debug("Navigate: skipping hidden node");
1038
+ n.navigate(where, activate);
1039
+ return;
1040
+ }
1034
1041
  return activate === false ? n.setFocus() : n.setActive();
1035
1042
  }
1036
1043
  }
@@ -1044,20 +1051,16 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1044
1051
  case KC.LEFT:
1045
1052
  if( this.expanded ) {
1046
1053
  this.setExpanded(false);
1047
- // tree.nodeSetFocus(ctx);
1048
1054
  _goto(this);
1049
1055
  } else if( this.parent && this.parent.parent ) {
1050
- // this.parent.setFocus();
1051
1056
  _goto(this.parent);
1052
1057
  }
1053
1058
  break;
1054
1059
  case KC.RIGHT:
1055
1060
  if( !this.expanded && (this.children || this.lazy) ) {
1056
1061
  this.setExpanded();
1057
- // tree.nodeSetFocus(ctx);
1058
1062
  _goto(this);
1059
1063
  } else if( this.children && this.children.length ) {
1060
- // this.children[0].setFocus();
1061
1064
  _goto(this.children[0]);
1062
1065
  }
1063
1066
  break;
@@ -1089,7 +1092,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1089
1092
  },
1090
1093
  /**
1091
1094
  * Discard and reload all children of a lazy node.
1092
- * @param {Boolean} [discard=false]
1095
+ * @param {boolean} [discard=false]
1093
1096
  * @returns $.Promise
1094
1097
  */
1095
1098
  lazyLoad: function(discard) {
@@ -1102,19 +1105,19 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1102
1105
  return this.tree._callHook("nodeLoadChildren", this, source);
1103
1106
  },
1104
1107
  /**
1105
- * @see Fancytree#nodeRender
1108
+ * @see Fancytree_Hooks#nodeRender
1106
1109
  */
1107
1110
  render: function(force, deep) {
1108
1111
  return this.tree._callHook("nodeRender", this, force, deep);
1109
1112
  },
1110
1113
  /**
1111
- * @see Fancytree#nodeRenderTitle
1114
+ * @see Fancytree_Hooks#nodeRenderTitle
1112
1115
  */
1113
1116
  renderTitle: function() {
1114
1117
  return this.tree._callHook("nodeRenderTitle", this);
1115
1118
  },
1116
1119
  /**
1117
- * @see Fancytree#nodeRenderStatus
1120
+ * @see Fancytree_Hooks#nodeRenderStatus
1118
1121
  */
1119
1122
  renderStatus: function() {
1120
1123
  return this.tree._callHook("nodeRenderStatus", this);
@@ -1165,7 +1168,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1165
1168
  },
1166
1169
  /**
1167
1170
  *
1168
- * @param {Boolean | PlainObject} [effects=false] animation options.
1171
+ * @param {boolean | PlainObject} [effects=false] animation options.
1169
1172
  * @param {FancytreeNode} [topNode=null] this node will remain visible in
1170
1173
  * any case, even if `this` is outside the scroll pane.
1171
1174
  * @returns $.Promise
@@ -1199,7 +1202,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1199
1202
  if(topNode){
1200
1203
  topNodeY = topNode ? $(topNode.span).position().top : 0;
1201
1204
  if((nodeY - topNodeY) > containerHeight){
1202
- newScrollTop = scrollTop + nodeY;
1205
+ newScrollTop = scrollTop + topNodeY;
1203
1206
  }
1204
1207
  }
1205
1208
  }
@@ -1236,19 +1239,22 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1236
1239
  },
1237
1240
 
1238
1241
  /**Activate this node.
1239
- * @param {Boolean} [flag=true] pass false to deactivate
1242
+ * @param {boolean} [flag=true] pass false to deactivate
1243
+ * @param {object} [opts] additional options. Defaults to {noEvents: false}
1240
1244
  */
1241
- setActive: function(flag){
1242
- return this.tree._callHook("nodeSetActive", this, flag);
1245
+ setActive: function(flag, opts){
1246
+ return this.tree._callHook("nodeSetActive", this, flag, opts);
1243
1247
  },
1244
- /**Expand this node.
1245
- * @param {Boolean} [flag=true] pass false to collapse
1248
+ /**Expand or collapse this node.
1249
+ * @param {boolean} [flag=true] pass false to collapse
1250
+ * @param {object} [opts] additional options. Defaults to {noAnimation: false, noEvents: false}
1251
+ * @returns {$.Promise} resolved, when lazy loading and animations are done
1246
1252
  */
1247
- setExpanded: function(flag){
1248
- return this.tree._callHook("nodeSetExpanded", this, flag);
1253
+ setExpanded: function(flag, opts){
1254
+ return this.tree._callHook("nodeSetExpanded", this, flag, opts);
1249
1255
  },
1250
1256
  /**Set keyboard focus to this node.
1251
- * @param {Boolean} [flag=true] pass false to blur
1257
+ * @param {boolean} [flag=true] pass false to blur
1252
1258
  * @see Fancytree#setFocus
1253
1259
  */
1254
1260
  setFocus: function(flag){
@@ -1256,7 +1262,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1256
1262
  },
1257
1263
  // TODO: setLazyNodeStatus
1258
1264
  /**Select this node.
1259
- * @param {Boolean} [flag=true] pass false to deselect
1265
+ * @param {boolean} [flag=true] pass false to deselect
1260
1266
  */
1261
1267
  setSelected: function(flag){
1262
1268
  return this.tree._callHook("nodeSetSelected", this, flag);
@@ -1269,8 +1275,8 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1269
1275
  this.renderTitle();
1270
1276
  },
1271
1277
  /**Sort child list by title.
1272
- * @param {function} [cmd] custom compare function.
1273
- * @param {Boolean} [deep] pass true to sort all descendant nodes
1278
+ * @param {function} [cmp] custom compare function(a, b) that returns -1, 0, or 1 (defaults to sort by title).
1279
+ * @param {boolean} [deep=false] pass true to sort all descendant nodes
1274
1280
  */
1275
1281
  sortChildren: function(cmp, deep) {
1276
1282
  var i,l,
@@ -1296,13 +1302,13 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1296
1302
  this.render();
1297
1303
  }
1298
1304
  },
1299
- /** Convert node (or whole branch) into a dictionary.
1305
+ /** Convert node (or whole branch) into a plain object.
1300
1306
  *
1301
1307
  * The result is compatible with node.addChildren().
1302
1308
  *
1303
- * @param {Boolean} recursive
1304
- * @param {function} callback callback(dict) is called for every dict (), in order to allow modifications
1305
- * @returns {NodePatch}
1309
+ * @param {boolean} recursive
1310
+ * @param {function} callback callback(dict) is called for every node, in order to allow modifications
1311
+ * @returns {NodeData}
1306
1312
  */
1307
1313
  toDict: function(recursive, callback) {
1308
1314
  var i, l, node,
@@ -1310,7 +1316,6 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1310
1316
  self = this;
1311
1317
 
1312
1318
  $.each(NODE_ATTRS, function(i, a){
1313
- // if(self[a] !== undefined && self[a] !== null){
1314
1319
  if(self[a] || self[a] === false){
1315
1320
  dict[a] = self[a];
1316
1321
  }
@@ -1354,8 +1359,8 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1354
1359
  * Skip current branch, if fn() returns 'skip'.
1355
1360
  * @param {function} fn the callback function.
1356
1361
  * Return false to stop iteration, return "skip" to skip this node and children only.
1357
- * @param {Boolean} [includeSelf=false]
1358
- * @returns {Boolean} false, if the iterator was stopped.
1362
+ * @param {boolean} [includeSelf=false]
1363
+ * @returns {boolean} false, if the iterator was stopped.
1359
1364
  */
1360
1365
  visit: function(fn, includeSelf) {
1361
1366
  var i, l,
@@ -1382,7 +1387,7 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1382
1387
  *
1383
1388
  * @param fn
1384
1389
  * @param includeSelf
1385
- * @returns {Boolean}
1390
+ * @returns {boolean}
1386
1391
  */
1387
1392
  visitParents: function(fn, includeSelf) {
1388
1393
  // Visit parent nodes (bottom up)
@@ -1413,10 +1418,12 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1413
1418
  * Fancytree
1414
1419
  */
1415
1420
  /**
1416
- * Construct a new tree.
1417
- * @class The controller behind a fancytree.
1418
- * @name Fancytree
1419
- * @constructor
1421
+ * Construct a new tree object.
1422
+ *
1423
+ * @class Fancytree
1424
+ * @classdesc A Fancytree is the controller behind a fancytree.
1425
+ * This class also contains 'hook methods': see {@link Fancytree_Hooks}.
1426
+ *
1420
1427
  * @param {Widget} widget
1421
1428
  *
1422
1429
  * @property {FancytreeOptions} options
@@ -1428,15 +1435,14 @@ FancytreeNode.prototype = /**@lends FancytreeNode*/{
1428
1435
  * @property {object} ext
1429
1436
  * @property {object} data
1430
1437
  * @property {object} options
1431
- * @property {String} _id
1432
- * @property {String} statusClassPropName
1433
- * @property {String} ariaPropName
1434
- * @property {String} nodeContainerAttrName
1435
- * @property {String} $container
1438
+ * @property {string} _id
1439
+ * @property {string} statusClassPropName
1440
+ * @property {string} ariaPropName
1441
+ * @property {string} nodeContainerAttrName
1442
+ * @property {string} $container
1436
1443
  * @property {FancytreeNode} lastSelectedNode
1437
1444
  */
1438
- function Fancytree(widget){
1439
- // TODO: rename widget to widget (it's not a jQuery object)
1445
+ function Fancytree(widget) {
1440
1446
  this.widget = widget;
1441
1447
  this.$div = widget.element;
1442
1448
  this.options = widget.options;
@@ -1463,7 +1469,8 @@ function Fancytree(widget){
1463
1469
  this.rootNode = new FancytreeNode(fakeParent, {
1464
1470
  title: "root",
1465
1471
  key: "root_" + this._id,
1466
- children: null
1472
+ children: null,
1473
+ expanded: true
1467
1474
  });
1468
1475
  this.rootNode.parent = null;
1469
1476
 
@@ -1488,8 +1495,8 @@ function Fancytree(widget){
1488
1495
  }
1489
1496
 
1490
1497
 
1491
- Fancytree.prototype = /**@lends Fancytree*/{
1492
- /** Return a context object that can be re-used for _callHook().
1498
+ Fancytree.prototype = /** @lends Fancytree# */{
1499
+ /* Return a context object that can be re-used for _callHook().
1493
1500
  * @param {Fancytree | FancytreeNode | EventData} obj
1494
1501
  * @param {Event} originalEvent
1495
1502
  * @param {Object} extra
@@ -1518,11 +1525,11 @@ Fancytree.prototype = /**@lends Fancytree*/{
1518
1525
  }
1519
1526
  return ctx;
1520
1527
  },
1521
- /** Trigger a hook function: funcName(ctx, [...]).
1528
+ /* Trigger a hook function: funcName(ctx, [...]).
1522
1529
  *
1523
- * @param {String} funcName
1530
+ * @param {string} funcName
1524
1531
  * @param {Fancytree|FancytreeNode|EventData} contextObject
1525
- * @param {any, ...} [_extraArgs] optional additional arguments
1532
+ * @param {any} [_extraArgs] optional additional arguments
1526
1533
  * @returns {any}
1527
1534
  */
1528
1535
  _callHook: function(funcName, contextObject, _extraArgs) {
@@ -1536,11 +1543,47 @@ Fancytree.prototype = /**@lends Fancytree*/{
1536
1543
  // this.debug("_hook", funcName, ctx.node && ctx.node.toString() || ctx.tree.toString(), args);
1537
1544
  return fn.apply(this, args);
1538
1545
  },
1539
- /** Activate node with a given key.
1546
+ /* Check if current extensions dependencies are met and throw an error if not.
1547
+ *
1548
+ * This method may be called inside the `treeInit` hook for custom extensions.
1549
+ *
1550
+ * @param {string} extension name of the required extension
1551
+ * @param {boolean} [required=true] pass `false` if the extension is optional, but we want to check for order if it is present
1552
+ * @param {boolean} [before] `true` if `name` must be included before this, `false` otherwise (use `null` if order doesn't matter)
1553
+ * @param {string} [message] optional error message (defaults to a descriptve error message)
1554
+ */
1555
+ _requireExtension: function(name, required, before, message) {
1556
+ before = !!before;
1557
+ var thisName = this._local.name,
1558
+ extList = this.options.extensions,
1559
+ isBefore = $.inArray(name, extList) < $.inArray(thisName, extList),
1560
+ isMissing = required && this.ext[name] == null,
1561
+ badOrder = !isMissing && before != null && (before !== isBefore);
1562
+
1563
+ _assert(thisName && thisName !== name);
1564
+
1565
+ if( isMissing || badOrder ){
1566
+ if( !message ){
1567
+ if( isMissing || required ){
1568
+ message = "'" + thisName + "' extension requires '" + name + "'";
1569
+ if( badOrder ){
1570
+ message += " to be registered " + (before ? "before" : "after") + " itself";
1571
+ }
1572
+ }else{
1573
+ message = "If used together, `" + name + "` must be registered " + (before ? "before" : "after") + " `" + thisName + "`";
1574
+ }
1575
+ }
1576
+ $.error(message);
1577
+ return false;
1578
+ }
1579
+ return true;
1580
+ },
1581
+ /** Activate node with a given key and fire focus and activate events.
1540
1582
  *
1541
1583
  * A prevously activated node will be deactivated.
1584
+ * If activeVisible option is set, all parents will be expanded as necessary.
1542
1585
  * Pass key = false, to deactivate the current node only.
1543
- * @param {String} key
1586
+ * @param {string} key
1544
1587
  * @returns {FancytreeNode} activated node (null, if not found)
1545
1588
  */
1546
1589
  activateKey: function(key) {
@@ -1552,7 +1595,7 @@ Fancytree.prototype = /**@lends Fancytree*/{
1552
1595
  }
1553
1596
  return node;
1554
1597
  },
1555
- /**
1598
+ /** (experimental)
1556
1599
  *
1557
1600
  * @param {Array} patchList array of [key, NodePatch] arrays
1558
1601
  * @returns {$.Promise} resolved, when all patches have been applied
@@ -1588,11 +1631,13 @@ Fancytree.prototype = /**@lends Fancytree*/{
1588
1631
  }
1589
1632
  },
1590
1633
  */
1591
- /** Return the number of child nodes. */
1634
+ /** Return the number of nodes.
1635
+ * @returns {integer}
1636
+ */
1592
1637
  count: function() {
1593
1638
  return this.rootNode.countChildren();
1594
1639
  },
1595
- /** Write to browser console if debugLevel >= 2 (prepending tree info)
1640
+ /** Write to browser console if debugLevel >= 2 (prepending tree name)
1596
1641
  *
1597
1642
  * @param {*} msg string or object or array of such
1598
1643
  */
@@ -1611,8 +1656,8 @@ Fancytree.prototype = /**@lends Fancytree*/{
1611
1656
  *
1612
1657
  * In selectMode 3 only the topmost selected nodes are considered.
1613
1658
  *
1614
- * @param {Boolean | String} [selected=true]
1615
- * @param {Boolean | String} [active=true]
1659
+ * @param {boolean | string} [selected=true]
1660
+ * @param {boolean | string} [active=true]
1616
1661
  */
1617
1662
  generateFormElements: function(selected, active) {
1618
1663
  // TODO: test case
@@ -1650,19 +1695,21 @@ Fancytree.prototype = /**@lends Fancytree*/{
1650
1695
  }
1651
1696
  },
1652
1697
  /**
1653
- * Return node that is active.
1698
+ * Return the currently active FancytreeNode or null.
1654
1699
  * @returns {FancytreeNode}
1655
1700
  */
1656
1701
  getActiveNode: function() {
1657
1702
  return this.activeNode;
1658
1703
  },
1659
- /** @returns {FancytreeNode | null}*/
1704
+ /** Return the first top level node if any (not the invisible root node).
1705
+ * @returns {FancytreeNode | null}
1706
+ */
1660
1707
  getFirstChild: function() {
1661
1708
  return this.rootNode.getFirstChild();
1662
1709
  },
1663
1710
  /**
1664
1711
  * Return node that has keyboard focus.
1665
- * @param {Boolean} [ifTreeHasFocus=false]
1712
+ * @param {boolean} [ifTreeHasFocus=false] (not yet implemented)
1666
1713
  * @returns {FancytreeNode}
1667
1714
  */
1668
1715
  getFocusNode: function(ifTreeHasFocus) {
@@ -1670,8 +1717,8 @@ Fancytree.prototype = /**@lends Fancytree*/{
1670
1717
  return this.focusNode;
1671
1718
  },
1672
1719
  /**
1673
- * Return node with a given key.
1674
- * @param {String} key
1720
+ * Return node with a given key or null if not found.
1721
+ * @param {string} key
1675
1722
  * @param {FancytreeNode} [searchRoot] only search below this node
1676
1723
  * @returns {FancytreeNode | null}
1677
1724
  */
@@ -1701,8 +1748,8 @@ Fancytree.prototype = /**@lends Fancytree*/{
1701
1748
  },
1702
1749
  // TODO: getRoot()
1703
1750
  /**
1704
- * Return a list of selected nodes.
1705
- * @param {Boolean} [stopOnParents=false] only return the topmost selected
1751
+ * Return an array of selected nodes.
1752
+ * @param {boolean} [stopOnParents=false] only return the topmost selected
1706
1753
  * node (useful with selectMode 3)
1707
1754
  * @returns {FancytreeNode[]}
1708
1755
  */
@@ -1718,14 +1765,13 @@ Fancytree.prototype = /**@lends Fancytree*/{
1718
1765
  });
1719
1766
  return nodeList;
1720
1767
  },
1721
- /**
1722
- * @returns {Boolean} true if the tree control has keyboard focus
1768
+ /** Return true if the tree control has keyboard focus
1769
+ * @returns {boolean}
1723
1770
  */
1724
1771
  hasFocus: function(){
1725
1772
  return !!this._hasFocus;
1726
1773
  },
1727
- /** Write to browser console if debugLevel >= 1 (prepending tree info)
1728
- *
1774
+ /** Write to browser console if debugLevel >= 1 (prepending tree name)
1729
1775
  * @param {*} msg string or object or array of such
1730
1776
  */
1731
1777
  info: function(msg){
@@ -1745,69 +1791,22 @@ Fancytree.prototype = /**@lends Fancytree*/{
1745
1791
  return ( this.phase=="userEvent" );
1746
1792
  },
1747
1793
  */
1748
- /**
1749
- * Expand all parents of one or more nodes.
1750
- * Calls
1751
- * @param {String | String[]} keyPath one or more key paths (e.g. '/3/2_1/7')
1752
- * @param {function} callback callbeck(mode) is called for every visited node
1753
- * @returns {$.Promise}
1754
- */
1755
- /*
1756
- _loadKeyPath: function(keyPath, callback) {
1757
- var tree = this.tree;
1758
- tree.logDebug("%s._loadKeyPath(%s)", this, keyPath);
1759
- if(keyPath === ""){
1760
- throw "Key path must not be empty";
1761
- }
1762
- var segList = keyPath.split(tree.options.keyPathSeparator);
1763
- if(segList[0] === ""){
1764
- throw "Key path must be relative (don't start with '/')";
1765
- }
1766
- var seg = segList.shift();
1767
-
1768
- for(var i=0, l=this.childList.length; i < l; i++){
1769
- var child = this.childList[i];
1770
- if( child.data.key === seg ){
1771
- if(segList.length === 0) {
1772
- // Found the end node
1773
- callback.call(tree, child, "ok");
1774
-
1775
- }else if(child.data.isLazy && (child.childList === null || child.childList === undefined)){
1776
- tree.logDebug("%s._loadKeyPath(%s) -> reloading %s...", this, keyPath, child);
1777
- var self = this;
1778
- child.reloadChildren(function(node, isOk){
1779
- // After loading, look for direct child with that key
1780
- if(isOk){
1781
- tree.logDebug("%s._loadKeyPath(%s) -> reloaded %s.", node, keyPath, node);
1782
- callback.call(tree, child, "loaded");
1783
- node._loadKeyPath(segList.join(tree.options.keyPathSeparator), callback);
1784
- }else{
1785
- tree.logWarning("%s._loadKeyPath(%s) -> reloadChildren() failed.", self, keyPath);
1786
- callback.call(tree, child, "error");
1787
- }
1788
- }); // Note: this line gives a JSLint warning (Don't make functions within a loop)
1789
- // we can ignore it, since it will only be exectuted once, the the loop is ended
1790
- // See also http://stackoverflow.com/questions/3037598/how-to-get-around-the-jslint-error-dont-make-functions-within-a-loop
1791
- } else {
1792
- callback.call(tree, child, "loaded");
1793
- // Look for direct child with that key
1794
- child._loadKeyPath(segList.join(tree.options.keyPathSeparator), callback);
1795
- }
1796
- return;
1797
- }
1798
- }
1799
- // Could not find key
1800
- tree.logWarning("Node not found: " + seg);
1801
- return;
1802
- },
1803
-
1804
- */
1805
1794
 
1806
1795
  /**
1807
- * Expand all parents of one or more nodes.
1808
- * Calls
1809
- * @param {String | String[]} keyPathList one or more key paths (e.g. '/3/2_1/7')
1810
- * @param {function} callback callbeck(mode) is called for every visited node ('loaded', 'ok', 'error')
1796
+ * Make sure that a node with a given ID is loaded, by traversing - and
1797
+ * loading - its parents. This method is ment for lazy hierarchies.
1798
+ * A callback is executed for every node as we go.
1799
+ * @example
1800
+ * tree.loadKeyPath("/_3/_23/_26/_27", function(node, status){
1801
+ * if(status === "loaded") {
1802
+ * console.log("loaded intermiediate node " + node);
1803
+ * }else if(status === "ok") {
1804
+ * node.activate();
1805
+ * }
1806
+ * });
1807
+ *
1808
+ * @param {string | string[]} keyPathList one or more key paths (e.g. '/3/2_1/7')
1809
+ * @param {function} callback callback(node, status) is called for every visited node ('loading', 'loaded', 'ok', 'error')
1811
1810
  * @returns {$.Promise}
1812
1811
  */
1813
1812
  loadKeyPath: function(keyPathList, callback, _rootNode) {
@@ -1881,6 +1880,110 @@ Fancytree.prototype = /**@lends Fancytree*/{
1881
1880
  // Return a promise that is resovled, when ALL paths were loaded
1882
1881
  return $.when.apply($, deferredList).promise();
1883
1882
  },
1883
+ /** Re-fire beforeActivate and activate events. */
1884
+ reactivate: function(setFocus) {
1885
+ var node = this.activeNode;
1886
+ if( node ) {
1887
+ this.activeNode = null; // Force re-activating
1888
+ node.setActive();
1889
+ if( setFocus ){
1890
+ node.setFocus();
1891
+ }
1892
+ }
1893
+ },
1894
+ /** Reload tree from source and return a promise.
1895
+ * @param [source] optional new source (defaults to initial source data)
1896
+ * @returns {$.Promise}
1897
+ */
1898
+ reload: function(source) {
1899
+ this._callHook("treeClear", this);
1900
+ return this._callHook("treeLoad", this, source);
1901
+ },
1902
+ /**Render tree (i.e. create DOM elements for all top-level nodes).
1903
+ * @param {boolean} [force=false] create DOM elemnts, even is parent is collapsed
1904
+ * @param {boolean} [deep=false]
1905
+ */
1906
+ render: function(force, deep) {
1907
+ return this.rootNode.render(force, deep);
1908
+ },
1909
+ // TODO: selectKey: function(key, select)
1910
+ // TODO: serializeArray: function(stopOnParents)
1911
+ /**
1912
+ * @param {boolean} [flag=true]
1913
+ */
1914
+ setFocus: function(flag) {
1915
+ return this._callHook("treeSetFocus", this, flag);
1916
+ },
1917
+ /**
1918
+ * Return all nodes as nested list of {@link NodeData}.
1919
+ *
1920
+ * @param {boolean} [includeRoot=false] Returns the hidden system root node (and its children)
1921
+ * @param {function} [callback(node)] Called for every node
1922
+ * @returns {Array | object}
1923
+ * @see FancytreeNode#toDict
1924
+ */
1925
+ toDict: function(includeRoot, callback){
1926
+ var res = this.rootNode.toDict(true, callback);
1927
+ return includeRoot ? res : res.children;
1928
+ },
1929
+ /* Implicitly called for string conversions.
1930
+ * @returns {string}
1931
+ */
1932
+ toString: function(){
1933
+ return "<Fancytree(#" + this._id + ")>";
1934
+ },
1935
+ /* _trigger a widget event with additional node ctx.
1936
+ * @see EventData
1937
+ */
1938
+ _triggerNodeEvent: function(type, node, originalEvent, extra) {
1939
+ // this.debug("_trigger(" + type + "): '" + ctx.node.title + "'", ctx);
1940
+ var ctx = this._makeHookContext(node, originalEvent, extra),
1941
+ res = this.widget._trigger(type, originalEvent, ctx);
1942
+ if(res !== false && ctx.result !== undefined){
1943
+ return ctx.result;
1944
+ }
1945
+ return res;
1946
+ },
1947
+ /* _trigger a widget event with additional tree data. */
1948
+ _triggerTreeEvent: function(type, originalEvent) {
1949
+ // this.debug("_trigger(" + type + ")", ctx);
1950
+ var ctx = this._makeHookContext(this, originalEvent),
1951
+ res = this.widget._trigger(type, originalEvent, ctx);
1952
+
1953
+ if(res !== false && ctx.result !== undefined){
1954
+ return ctx.result;
1955
+ }
1956
+ return res;
1957
+ },
1958
+ /** Call fn(node) for all nodes.
1959
+ *
1960
+ * @param {function} fn the callback function.
1961
+ * Return false to stop iteration, return "skip" to skip this node and children only.
1962
+ * @returns {boolean} false, if the iterator was stopped.
1963
+ */
1964
+ visit: function(fn) {
1965
+ return this.rootNode.visit(fn, false);
1966
+ },
1967
+ /** Write warning to browser console (prepending tree info)
1968
+ *
1969
+ * @param {*} msg string or object or array of such
1970
+ */
1971
+ warn: function(msg){
1972
+ Array.prototype.unshift.call(arguments, this.toString());
1973
+ consoleApply("warn", arguments);
1974
+ }
1975
+ };
1976
+
1977
+ /**
1978
+ * These additional methods of the {@link Fancytree} class are 'hook functions'
1979
+ * that can be used and overloaded by extensions.
1980
+ * (See <a href="https://github.com/mar10/fancytree/wiki/TutorialExtensions">writing extensions</a>.)
1981
+ * @mixin Fancytree_Hooks
1982
+ */
1983
+ $.extend(Fancytree.prototype,
1984
+ /** @lends Fancytree_Hooks# */
1985
+ {
1986
+
1884
1987
  /** _Default handling for mouse click events. */
1885
1988
  nodeClick: function(ctx) {
1886
1989
  // this.tree.logDebug("ftnode.onClick(" + event.type + "): ftnode:" + this + ", button:" + event.button + ", which: " + event.which);
@@ -1934,7 +2037,7 @@ Fancytree.prototype = /**@lends Fancytree*/{
1934
2037
  }
1935
2038
  // TODO: return promise?
1936
2039
  },
1937
- nodeCollapseSiblings: function(ctx) {
2040
+ nodeCollapseSiblings: function(ctx, callOpts) {
1938
2041
  // TODO: return promise?
1939
2042
  var ac, i, l,
1940
2043
  node = ctx.node;
@@ -1943,7 +2046,7 @@ Fancytree.prototype = /**@lends Fancytree*/{
1943
2046
  ac = node.parent.children;
1944
2047
  for (i=0, l=ac.length; i<l; i++) {
1945
2048
  if ( ac[i] !== node && ac[i].expanded ){
1946
- this._callHook("nodeSetExpanded", ac[i], false);
2049
+ this._callHook("nodeSetExpanded", ac[i], false, callOpts);
1947
2050
  }
1948
2051
  }
1949
2052
  }
@@ -2166,7 +2269,7 @@ Fancytree.prototype = /**@lends Fancytree*/{
2166
2269
  },
2167
2270
  /**
2168
2271
  * Remove a single direct child of ctx.node.
2169
- * @param ctx
2272
+ * @param {EventData} ctx
2170
2273
  * @param {FancytreeNode} childNode dircect child of ctx.node
2171
2274
  */
2172
2275
  nodeRemoveChild: function(ctx, childNode) {
@@ -2212,11 +2315,15 @@ Fancytree.prototype = /**@lends Fancytree*/{
2212
2315
  FT.debug("nodeRemoveChildMarkup()", node.toString());
2213
2316
  // TODO: Unlink attr.ftnode to support GC
2214
2317
  if(node.ul){
2215
- $(node.ul).remove();
2318
+ if( node.isRoot() ) {
2319
+ $(node.ul).empty();
2320
+ } else {
2321
+ $(node.ul).remove();
2322
+ node.ul = null;
2323
+ }
2216
2324
  node.visit(function(n){
2217
2325
  n.li = n.ul = null;
2218
2326
  });
2219
- node.ul = null;
2220
2327
  }
2221
2328
  },
2222
2329
  /**Remove all descendants of ctx.node.
@@ -2296,9 +2403,9 @@ Fancytree.prototype = /**@lends Fancytree*/{
2296
2403
  * </code>
2297
2404
  *
2298
2405
  * @param: {EventData} ctx
2299
- * @param: {Boolean} [force=false] re-render, even if html markup was already created
2300
- * @param: {Boolean} [deep=false] also render all descendants, even if parent is collapsed
2301
- * @param: {Boolean} [collapsed=false] force root node to be collapsed, so we can apply animated expand later
2406
+ * @param: {boolean} [force=false] re-render, even if html markup was already created
2407
+ * @param: {boolean} [deep=false] also render all descendants, even if parent is collapsed
2408
+ * @param: {boolean} [collapsed=false] force root node to be collapsed, so we can apply animated expand later
2302
2409
  */
2303
2410
  nodeRender: function(ctx, force, deep, collapsed, _recursive) {
2304
2411
  /* This method must take care of all cases where the current data mode
@@ -2310,7 +2417,7 @@ Fancytree.prototype = /**@lends Fancytree*/{
2310
2417
  * - children have been added
2311
2418
  * - childern have been removed
2312
2419
  */
2313
- var childLI, childNode1, childNode2, i, l, subCtx,
2420
+ var childLI, childNode1, childNode2, i, l, next, subCtx,
2314
2421
  node = ctx.node,
2315
2422
  tree = ctx.tree,
2316
2423
  opts = ctx.options,
@@ -2319,7 +2426,7 @@ Fancytree.prototype = /**@lends Fancytree*/{
2319
2426
  parent = node.parent,
2320
2427
  isRootNode = !parent,
2321
2428
  children = node.children;
2322
- // FT.debug("nodeRender(" + !!force + ", " + !!deep + ")", node.toString());
2429
+ FT.debug("nodeRender(" + !!force + ", " + !!deep + ")", node.toString());
2323
2430
 
2324
2431
  if( ! isRootNode && ! parent.ul ) {
2325
2432
  // issue #105: calling node.collapse on a deep, unrendered node
@@ -2327,6 +2434,14 @@ Fancytree.prototype = /**@lends Fancytree*/{
2327
2434
  }
2328
2435
  _assert(isRootNode || parent.ul, "parent UL must exist");
2329
2436
 
2437
+ // if(node.li && (force || (node.li.parentNode !== node.parent.ul) ) ){
2438
+ // if(node.li.parentNode !== node.parent.ul){
2439
+ // // alert("unlink " + node + " (must be child of " + node.parent + ")");
2440
+ // this.warn("unlink " + node + " (must be child of " + node.parent + ")");
2441
+ // }
2442
+ // // this.debug("nodeRemoveMarkup...");
2443
+ // this.nodeRemoveMarkup(ctx);
2444
+ // }
2330
2445
  // Render the node
2331
2446
  if( !isRootNode ){
2332
2447
  // Discard markup on force-mode, or if it is not linked to parent <ul>
@@ -2360,17 +2475,11 @@ Fancytree.prototype = /**@lends Fancytree*/{
2360
2475
  $(node.span).attr("aria-labelledby", "ftal_" + node.key);
2361
2476
  }
2362
2477
  node.li.appendChild(node.span);
2363
- // Note: we don't add the LI to the DOM know, but only after we
2364
- // added all sub elements (hoping that this performs better since
2365
- // the browser only have to render once)
2366
- // TODO: benchmarks to prove this
2367
- // parent.ul.appendChild(node.li);
2368
2478
 
2369
2479
  // Create inner HTML for the <span> (expander, checkbox, icon, and title)
2370
2480
  this.nodeRenderTitle(ctx);
2371
2481
 
2372
2482
  // Allow tweaking and binding, after node was created for the first time
2373
- // tree._triggerNodeEvent("createNode", ctx);
2374
2483
  if ( opts.createNode ){
2375
2484
  opts.createNode.call(tree, {type: "createNode"}, ctx);
2376
2485
  }
@@ -2378,7 +2487,6 @@ Fancytree.prototype = /**@lends Fancytree*/{
2378
2487
  // this.nodeRenderTitle(ctx);
2379
2488
  }
2380
2489
  // Allow tweaking after node state was rendered
2381
- // tree._triggerNodeEvent("renderNode", ctx);
2382
2490
  if ( opts.renderNode ){
2383
2491
  opts.renderNode.call(tree, {type: "renderNode"}, ctx);
2384
2492
  }
@@ -2408,8 +2516,20 @@ Fancytree.prototype = /**@lends Fancytree*/{
2408
2516
  subCtx = $.extend({}, ctx, {node: children[i]});
2409
2517
  this.nodeRender(subCtx, force, deep, false, true);
2410
2518
  }
2519
+ // Remove <li> if nodes have moved to another parent
2520
+ childLI = node.ul.firstChild;
2521
+ while( childLI ){
2522
+ childNode2 = childLI.ftnode;
2523
+ if( childNode2 && childNode2.parent !== node ) {
2524
+ node.debug("_fixParent: remove missing " + childNode2, childLI);
2525
+ next = childLI.nextSibling;
2526
+ childLI.parentNode.removeChild(childLI);
2527
+ childLI = next;
2528
+ }else{
2529
+ childLI = childLI.nextSibling;
2530
+ }
2531
+ }
2411
2532
  // Make sure, that <li> order matches node.children order.
2412
- // this.nodeFixOrder(ctx);
2413
2533
  childLI = node.ul.firstChild;
2414
2534
  for(i=0, l=children.length-1; i<l; i++) {
2415
2535
  childNode1 = children[i];
@@ -2421,7 +2541,6 @@ Fancytree.prototype = /**@lends Fancytree*/{
2421
2541
  childLI = childLI.nextSibling;
2422
2542
  }
2423
2543
  }
2424
- // TODO: need to check, if node.ul has <li>s, that are not in node.children[] ?
2425
2544
  }
2426
2545
  }else{
2427
2546
  // No children: remove markup if any
@@ -2439,14 +2558,13 @@ Fancytree.prototype = /**@lends Fancytree*/{
2439
2558
  parent.ul.appendChild(node.li);
2440
2559
  }
2441
2560
  }
2442
- return;
2443
2561
  },
2444
2562
  /** Create HTML for the node's outer <span> (expander, checkbox, icon, and title).
2445
2563
  * @param {EventData} ctx
2446
2564
  */
2447
2565
  nodeRenderTitle: function(ctx, title) {
2448
2566
  // set node connector images, links and text
2449
- var id, imageSrc, nodeTitle, role, tooltip,
2567
+ var id, imageSrc, nodeTitle, role, tabindex, tooltip,
2450
2568
  node = ctx.node,
2451
2569
  tree = ctx.tree,
2452
2570
  opts = ctx.options,
@@ -2513,16 +2631,13 @@ Fancytree.prototype = /**@lends Fancytree*/{
2513
2631
  tooltip = node.tooltip ? " title='" + node.tooltip.replace(/\"/g, "&quot;") + "'" : "";
2514
2632
  id = aria ? " id='ftal_" + node.key + "'" : "";
2515
2633
  role = aria ? " role='treeitem'" : "";
2516
- // href = node.data.href || "#";
2517
- // if( opts.nolink || node.nolink ) {
2518
- // nodeTitle = "<span role='treeitem' tabindex='-1' class='fancytree-title'" + id + tooltip + ">" + node.title + "</span>";
2519
- nodeTitle = "<span " + role + " class='fancytree-title'" + id + tooltip + ">" + node.title + "</span>";
2520
- // } else {
2521
- // nodeTitle = "<a href='" + href + "' tabindex='-1' class='fancytree-title'" + tooltip + ">" + node.title + "</a>";
2522
- // }
2634
+ tabindex = opts.titlesTabbable ? " tabindex='0'" : "";
2635
+
2636
+ nodeTitle = "<span " + role + " class='fancytree-title'" + id + tooltip + tabindex + ">" + node.title + "</span>";
2523
2637
  }
2524
2638
  ares.push(nodeTitle);
2525
2639
  // Note: this will trigger focusout, if node had the focus
2640
+ //$(node.span).html(ares.join("")); // it will cleanup the jQuery data currently associated with SPAN (if any), but it executes more slowly
2526
2641
  node.span.innerHTML = ares.join("");
2527
2642
  },
2528
2643
  /** Update element classes according to node state.
@@ -2639,16 +2754,19 @@ Fancytree.prototype = /**@lends Fancytree*/{
2639
2754
  * If flag is true, the node is activated (must be a synchronous operation)
2640
2755
  * If flag is false, the node is deactivated (must be a synchronous operation)
2641
2756
  * @param {EventData} ctx
2642
- * @param {Boolean} [flag=true]
2757
+ * @param {boolean} [flag=true]
2758
+ * @param {object} [opts] additional options. Defaults to {}
2643
2759
  */
2644
- nodeSetActive: function(ctx, flag) {
2760
+ nodeSetActive: function(ctx, flag, callOpts) {
2645
2761
  // Handle user click / [space] / [enter], according to clickFolderMode.
2762
+ callOpts = callOpts || {};
2646
2763
  var subCtx,
2647
2764
  node = ctx.node,
2648
2765
  tree = ctx.tree,
2649
2766
  opts = ctx.options,
2650
2767
  // userEvent = !!ctx.originalEvent,
2651
2768
  isActive = (node === tree.activeNode);
2769
+
2652
2770
  // flag defaults to true
2653
2771
  flag = (flag !== false);
2654
2772
  node.debug("nodeSetActive", flag);
@@ -2684,22 +2802,24 @@ Fancytree.prototype = /**@lends Fancytree*/{
2684
2802
  /** Expand or collapse node, return Deferred.promise.
2685
2803
  *
2686
2804
  * @param {EventData} ctx
2687
- * @param {Boolean} [flag=true]
2805
+ * @param {boolean} [flag=true]
2806
+ * @param {object} [opts] additional options. Defaults to {noAnimation: false}
2688
2807
  * @returns {$.Promise} The deferred will be resolved as soon as the (lazy)
2689
2808
  * data was retrieved, rendered, and the expand animation finshed.
2690
2809
  */
2691
- nodeSetExpanded: function(ctx, flag) {
2810
+ nodeSetExpanded: function(ctx, flag, callOpts) {
2811
+ callOpts = callOpts || {};
2692
2812
  var _afterLoad, dfd, i, l, parents, prevAC,
2693
2813
  node = ctx.node,
2694
2814
  tree = ctx.tree,
2695
- opts = ctx.options;
2815
+ opts = ctx.options,
2816
+ noAnimation = callOpts.noAnimation === true;
2817
+
2696
2818
  // flag defaults to true
2697
2819
  flag = (flag !== false);
2698
2820
 
2699
2821
  node.debug("nodeSetExpanded(" + flag + ")");
2700
- // TODO: !!node.expanded is nicer, but doesn't pass jshint
2701
- // https://github.com/jshint/jshint/issues/455
2702
- // if( !!node.expanded === !!flag){
2822
+
2703
2823
  if((node.expanded && flag) || (!node.expanded && !flag)){
2704
2824
  // Nothing to do
2705
2825
  node.debug("nodeSetExpanded(" + flag + "): nothing to do");
@@ -2714,7 +2834,11 @@ Fancytree.prototype = /**@lends Fancytree*/{
2714
2834
  // Callback returned false
2715
2835
  return _getRejectedPromise(node, ["rejected"]);
2716
2836
  }
2717
- //
2837
+ // If this node inside a collpased node, no animation and scrolling is needed
2838
+ if( !noAnimation && !node.isVisible() ) {
2839
+ noAnimation = callOpts.noAnimation = true;
2840
+ }
2841
+
2718
2842
  dfd = new $.Deferred();
2719
2843
 
2720
2844
  // Auto-collapse mode: collapse all siblings
@@ -2725,7 +2849,7 @@ Fancytree.prototype = /**@lends Fancytree*/{
2725
2849
  opts.autoCollapse = false;
2726
2850
  for(i=0, l=parents.length; i<l; i++){
2727
2851
  // TODO: should return promise?
2728
- this._callHook("nodeCollapseSiblings", parents[i]);
2852
+ this._callHook("nodeCollapseSiblings", parents[i], callOpts);
2729
2853
  }
2730
2854
  }finally{
2731
2855
  opts.autoCollapse = prevAC;
@@ -2734,7 +2858,7 @@ Fancytree.prototype = /**@lends Fancytree*/{
2734
2858
  // Trigger expand/collapse after expanding
2735
2859
  dfd.done(function(){
2736
2860
  ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
2737
- if(opts.autoScroll){
2861
+ if( opts.autoScroll && !noAnimation ) {
2738
2862
  // Scroll down to last child, but keep current node visible
2739
2863
  node.getLastChild().scrollIntoView(true, node);
2740
2864
  }
@@ -2765,8 +2889,10 @@ Fancytree.prototype = /**@lends Fancytree*/{
2765
2889
  isExpanded = !!node.expanded;
2766
2890
  if ( isVisible === isExpanded ) {
2767
2891
  node.warn("nodeSetExpanded: UL.style.display already set");
2768
- } else if ( !opts.fx ) {
2892
+
2893
+ } else if ( !opts.fx || noAnimation ) {
2769
2894
  node.ul.style.display = ( node.expanded || !parent ) ? "" : "none";
2895
+
2770
2896
  } else {
2771
2897
  duration = opts.fx.duration || 200;
2772
2898
  easing = opts.fx.easing;
@@ -2816,7 +2942,7 @@ Fancytree.prototype = /**@lends Fancytree*/{
2816
2942
  },
2817
2943
  /**
2818
2944
  * @param {EventData} ctx
2819
- * @param {Boolean} [flag=true]
2945
+ * @param {boolean} [flag=true]
2820
2946
  */
2821
2947
  nodeSetFocus: function(ctx, flag) {
2822
2948
  ctx.node.debug("nodeSetFocus(" + flag + ")");
@@ -2861,7 +2987,7 @@ Fancytree.prototype = /**@lends Fancytree*/{
2861
2987
  /** (De)Select node, return new status (sync).
2862
2988
  *
2863
2989
  * @param {EventData} ctx
2864
- * @param {Boolean} [flag=true]
2990
+ * @param {boolean} [flag=true]
2865
2991
  */
2866
2992
  nodeSetSelected: function(ctx, flag) {
2867
2993
  var node = ctx.node,
@@ -3078,121 +3204,33 @@ Fancytree.prototype = /**@lends Fancytree*/{
3078
3204
  this.$container.toggleClass("fancytree-treefocus", flag);
3079
3205
  this._triggerTreeEvent(flag ? "focusTree" : "blurTree");
3080
3206
  }
3081
- },
3082
- /** Re-fire beforeActivate and activate events. */
3083
- reactivate: function(setFocus) {
3084
- var node = this.activeNode;
3085
- if( node ) {
3086
- this.activeNode = null; // Force re-activating
3087
- node.setActive();
3088
- if( setFocus ){
3089
- node.setFocus();
3090
- }
3091
- }
3092
- },
3093
- // TODO: redraw()
3094
- /** Reload tree from source and return a promise.
3095
- * @param source
3096
- * @returns {$.Promise}
3097
- */
3098
- reload: function(source) {
3099
- this._callHook("treeClear", this);
3100
- return this._callHook("treeLoad", this, source);
3101
- },
3102
- /**Render tree (i.e. all top-level nodes).
3103
- * @param {Boolean} [force=false]
3104
- * @param {Boolean} [deep=false]
3105
- */
3106
- render: function(force, deep) {
3107
- return this.rootNode.render(force, deep);
3108
- },
3109
- // TODO: selectKey: function(key, select)
3110
- // TODO: serializeArray: function(stopOnParents)
3111
- /**
3112
- * @param {Boolean} [flag=true]
3113
- */
3114
- setFocus: function(flag) {
3115
- // _assert(false, "Not implemented");
3116
- return this._callHook("treeSetFocus", this, flag);
3117
- },
3118
- /**
3119
- * Return all nodes as nested list of {@link NodeData}.
3120
- *
3121
- * @param {Boolean} [includeRoot=false] Returns the hidden system root node (and it's children)
3122
- * @param {function} [callback] Called for every node
3123
- * @returns {Array | object}
3124
- * @see FancytreeNode#toDict
3125
- */
3126
- toDict: function(includeRoot, callback){
3127
- var res = this.rootNode.toDict(true, callback);
3128
- return includeRoot ? res : res.children;
3129
- },
3130
- /**Implicitly called for string conversions.
3131
- * @returns {String}
3132
- */
3133
- toString: function(){
3134
- return "<Fancytree(#" + this._id + ")>";
3135
- },
3136
- /** _trigger a widget event with additional node ctx.
3137
- * @see EventData
3138
- */
3139
- _triggerNodeEvent: function(type, node, originalEvent, extra) {
3140
- // this.debug("_trigger(" + type + "): '" + ctx.node.title + "'", ctx);
3141
- var ctx = this._makeHookContext(node, originalEvent, extra),
3142
- res = this.widget._trigger(type, originalEvent, ctx);
3143
- if(res !== false && ctx.result !== undefined){
3144
- return ctx.result;
3145
- }
3146
- return res;
3147
- },
3148
- /** _trigger a widget event with additional tree data. */
3149
- _triggerTreeEvent: function(type, originalEvent) {
3150
- // this.debug("_trigger(" + type + ")", ctx);
3151
- var ctx = this._makeHookContext(this, originalEvent),
3152
- res = this.widget._trigger(type, originalEvent, ctx);
3153
-
3154
- if(res !== false && ctx.result !== undefined){
3155
- return ctx.result;
3156
- }
3157
- return res;
3158
- },
3159
- /** Call fn(node) for all nodes.
3160
- *
3161
- * @param {function} fn the callback function.
3162
- * Return false to stop iteration, return "skip" to skip this node and children only.
3163
- * @returns {Boolean} false, if the iterator was stopped.
3164
- */
3165
- visit: function(fn) {
3166
- return this.rootNode.visit(fn, false);
3167
- },
3168
- /** Write warning to browser console (prepending tree info)
3169
- *
3170
- * @param {*} msg string or object or array of such
3171
- */
3172
- warn: function(msg){
3173
- Array.prototype.unshift.call(arguments, this.toString());
3174
- consoleApply("warn", arguments);
3175
3207
  }
3176
- };
3208
+ });
3177
3209
 
3178
3210
 
3179
3211
  /* ******************************************************************************
3180
3212
  * jQuery UI widget boilerplate
3181
- * @ name ui_fancytree
3182
- * @ class The jQuery.ui.fancytree widget
3183
3213
  */
3184
- /* * @namespace ui */
3185
- /* * @namespace ui.fancytree */
3186
- /** @namespace $.ui.fancytree */
3214
+ /**
3215
+ * This constructor is not called directly. Use `$(selector).fancytre({})`
3216
+ * to initialize the plugin instead.
3217
+ *
3218
+ * @class ui.fancytree
3219
+ * @classdesc The plugin (derrived from <a href=" http://api.jqueryui.com/jQuery.widget/">jQuery.Widget</a>).<br>
3220
+ * <pre class="sh_javascript sunlight-highlight-javascript">// Access instance methods and members:
3221
+ * var tree = $(selector).fancytree("getTree");
3222
+ * // Access static members:
3223
+ * alert($.ui.fancytree.version);
3224
+ * </pre>
3225
+ */
3187
3226
  $.widget("ui.fancytree",
3188
- /** @lends $.ui.fancytree.prototype */
3227
+ /** @lends ui.fancytree# */
3189
3228
  {
3190
3229
  /**These options will be used as defaults
3191
3230
  * @type {FancytreeOptions}
3192
3231
  */
3193
3232
  options:
3194
3233
  {
3195
- /** @type {Boolean} Make sure, active nodes are visible (expanded). */
3196
3234
  activeVisible: true,
3197
3235
  ajax: {
3198
3236
  type: "GET",
@@ -3225,6 +3263,7 @@ $.widget("ui.fancytree",
3225
3263
  loadError: "Load error!"
3226
3264
  },
3227
3265
  tabbable: true,
3266
+ titlesTabbable: false,
3228
3267
  _classNames: {
3229
3268
  node: "fancytree-node",
3230
3269
  folder: "fancytree-folder",
@@ -3439,7 +3478,7 @@ $.widget("ui.fancytree",
3439
3478
  return this.tree.activeNode;
3440
3479
  },
3441
3480
  /**
3442
- * @param {String} key
3481
+ * @param {string} key
3443
3482
  * @returns {FancytreeNode} the matching node or null
3444
3483
  */
3445
3484
  getNodeByKey: function(key) {
@@ -3458,19 +3497,19 @@ $.widget("ui.fancytree",
3458
3497
  // $.ui.fancytree was created by the widget factory. Create a local shortcut:
3459
3498
  FT = $.ui.fancytree;
3460
3499
 
3461
- /**
3500
+ /*
3462
3501
  * Static members in the `$.ui.fancytree` namespace.
3463
- * @ name $.ui.fancytree
3502
+ *
3464
3503
  * @example:
3465
3504
  * alert(""version: " + $.ui.fancytree.version);
3466
- * var node = $.ui.fancytree.()
3505
+ * var node = $.ui.fancytree.getNode(element);
3467
3506
  */
3468
3507
  $.extend($.ui.fancytree,
3469
- /** @lends $.ui.fancytree */
3508
+ /** @lends ui.fancytree */
3470
3509
  {
3471
- /** @type {String} */
3472
- version: "2.0.0-5",
3473
- /** @type {String} */
3510
+ /** @type {string} */
3511
+ version: "2.0.0-6",
3512
+ /** @type {string} */
3474
3513
  buildType: "release",
3475
3514
  /** @type {int} */
3476
3515
  debugLevel: 1, // used by $.ui.fancytree.debug() and as default for tree.options.debugLevel
@@ -3503,7 +3542,7 @@ $.extend($.ui.fancytree,
3503
3542
  *
3504
3543
  * @static
3505
3544
  * @param {Event} event Mouse event, e.g. click, ...
3506
- * @returns {String} 'title' | 'prefix' | 'expander' | 'checkbox' | 'icon' | undefined
3545
+ * @returns {string} 'title' | 'prefix' | 'expander' | 'checkbox' | 'icon' | undefined
3507
3546
  */
3508
3547
  getEventTargetType: function(event){
3509
3548
  return this.getEventTarget(event).type;
@@ -3678,11 +3717,12 @@ $.extend($.ui.fancytree,
3678
3717
  },
3679
3718
  /** Add Fancytree extension definition to the list of globally available extensions.
3680
3719
  *
3681
- * @param name
3682
- * @param definition
3720
+ * @param {Object} definition
3683
3721
  */
3684
- registerExtension: function(name, definition){
3685
- $.ui.fancytree._extensions[name] = definition;
3722
+ registerExtension: function(definition){
3723
+ _assert(definition.name != null, "extensions must have a `name` property.");
3724
+ _assert(definition.version != null, "extensions must have a `version` property.");
3725
+ $.ui.fancytree._extensions[definition.name] = definition;
3686
3726
  },
3687
3727
  warn: function(msg){
3688
3728
  consoleApply("warn", arguments);
@@ -3714,168 +3754,19 @@ if( typeof define === "function" && define.amd ) {
3714
3754
  */
3715
3755
  }(jQuery, window, document));
3716
3756
 
3717
- /*!
3718
- * jquery.fancytree.columnview.js
3719
- *
3720
- * Render tree like a Mac Finder's column view.
3721
- * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
3722
- *
3723
- * Copyright (c) 2013, Martin Wendt (http://wwWendt.de)
3724
- *
3725
- * Released under the MIT license
3726
- * https://github.com/mar10/fancytree/wiki/LicenseInfo
3727
- *
3728
- * @version 2.0.0-5
3729
- * @date 2014-01-04T16:42
3730
- */
3731
-
3732
- ;(function($, window, document, undefined) {
3733
-
3734
- "use strict";
3735
-
3736
- // prevent duplicate loading
3737
- // if ( $.ui.fancytree && $.ui.fancytree.version ) {
3738
- // $.ui.fancytree.warn("Fancytree: duplicate include");
3739
- // return;
3740
- // }
3741
-
3742
-
3743
- /*******************************************************************************
3744
- * Private functions and variables
3745
- */
3746
- /*
3747
- function _assert(cond, msg){
3748
- msg = msg || "";
3749
- if(!cond){
3750
- $.error("Assertion failed " + msg);
3751
- }
3752
- }
3753
- */
3754
-
3755
- /*******************************************************************************
3756
- * Private functions and variables
3757
- */
3758
- $.ui.fancytree.registerExtension("columnview", {
3759
- version: "0.0.1",
3760
- // Default options for this extension.
3761
- options: {
3762
- },
3763
- // Overide virtual methods for this extension.
3764
- // `this` : is this extension object
3765
- // `this._base` : the Fancytree instance
3766
- // `this._super`: the virtual function that was overriden (member of prev. extension or Fancytree)
3767
- treeInit: function(ctx){
3768
- var $tdFirst, $ul,
3769
- tree = ctx.tree,
3770
- $table = tree.widget.element;
3771
-
3772
- tree.tr = $("tbody tr", $table)[0];
3773
- tree.columnCount = $(">td", tree.tr).length;
3774
- // Perform default behavior
3775
- this._super(ctx);
3776
- // Standard Fancytree created a root <ul>. Now move this into first table cell
3777
- $ul = $(tree.rootNode.ul);
3778
- $tdFirst = $(">td", tree.tr).eq(0);
3779
-
3780
- $ul.removeClass("fancytree-container");
3781
- $ul.removeAttr("tabindex");
3782
- tree.$container = $table;
3783
- $table.addClass("fancytree-container fancytree-ext-columnview");
3784
- $table.attr("tabindex", "0");
3785
-
3786
- $tdFirst.empty();
3787
- $ul.detach().appendTo($tdFirst);
3788
-
3789
- // Force some required options
3790
- tree.widget.options.autoCollapse = true;
3791
- // tree.widget.options.autoActivate = true;
3792
- tree.widget.options.fx = false;
3793
- tree.widget.options.clickFolderMode = 1;
3794
-
3795
- // Make sure that only active path is expanded when a node is activated:
3796
- $table.bind("fancytreeactivate", function(e, data){
3797
- var i, tdList,
3798
- node = data.node,
3799
- tree = data.tree,
3800
- level = node.getLevel();
3801
-
3802
- tree._callHook("nodeCollapseSiblings", node);
3803
- // Clear right neighbours
3804
- if(level <= tree.columnCount){
3805
- tdList = $(">td", tree.tr);
3806
- for(i=level; i<tree.columnCount; i++){
3807
- tdList.eq(i).empty();
3808
- }
3809
- }
3810
- // Expand nodes on activate, so we populate the right neighbor cell
3811
- if(!node.expanded && (node.children || node.lazy)) {
3812
- node.setExpanded();
3813
- }
3814
- // Adjust keyboard behaviour:
3815
- }).bind("fancytreekeydown", function(e, data){
3816
- var next = null;
3817
- switch(e.which){
3818
- case $.ui.keyCode.DOWN:
3819
- next = data.node.getNextSibling();
3820
- if( next ){
3821
- next.setFocus();
3822
- }
3823
- return false;
3824
- case $.ui.keyCode.LEFT:
3825
- next = data.node.getParent();
3826
- if( next ){
3827
- next.setFocus();
3828
- }
3829
- return false;
3830
- case $.ui.keyCode.UP:
3831
- next = data.node.getPrevSibling();
3832
- if( next ){
3833
- next.setFocus();
3834
- }
3835
- return false;
3836
- }
3837
- });
3838
- },
3839
- nodeRender: function(ctx, force, deep, collapsed, _recursive) {
3840
- // Render standard nested <ul> - <li> hierarchy
3841
- this._super(ctx, force, deep, collapsed, _recursive);
3842
- // Remove expander and add a trailing triangle instead
3843
- var level, $tdChild, $ul,
3844
- tree = ctx.tree,
3845
- node = ctx.node,
3846
- $span = $(node.span);
3847
-
3848
- $span.find("span.fancytree-expander").remove();
3849
- if(node.hasChildren() !== false && !$span.find("span.fancytree-cv-right").length){
3850
- $span.append($("<span class='fancytree-icon fancytree-cv-right'>"));
3851
- }
3852
- // Move <ul> with children into the appropriate <td>
3853
- if(node.ul){
3854
- node.ul.style.display = ""; // might be hidden if RIGHT was pressed
3855
- level = node.getLevel();
3856
- if(level < tree.columnCount){
3857
- $tdChild = $(">td", tree.tr).eq(level);
3858
- $ul = $(node.ul).detach();
3859
- $tdChild.empty().append($ul);
3860
- }
3861
- }
3862
- }
3863
- });
3864
- }(jQuery, window, document));
3865
-
3866
3757
  /*!
3867
3758
  * jquery.fancytree.dnd.js
3868
3759
  *
3869
- * Drag'N'drop support.
3760
+ * Drag-and-drop support.
3870
3761
  * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
3871
3762
  *
3872
- * Copyright (c) 2013, Martin Wendt (http://wwWendt.de)
3763
+ * Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
3873
3764
  *
3874
3765
  * Released under the MIT license
3875
3766
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
3876
3767
  *
3877
- * @version 2.0.0-5
3878
- * @date 2014-01-04T16:42
3768
+ * @version 2.0.0-6
3769
+ * @date 2014-02-10T10:52
3879
3770
  */
3880
3771
 
3881
3772
  ;(function($, window, document, undefined) {
@@ -4068,12 +3959,10 @@ function _registerDnd() {
4068
3959
  /* *****************************************************************************
4069
3960
  *
4070
3961
  */
4071
- /** @namespace $.ui.fancytree.ext.dnd */
4072
- $.ui.fancytree.registerExtension("dnd",
4073
- /** @scope ui_fancytree
4074
- * @lends $.ui.fancytree.ext.dnd.prototype
4075
- */
3962
+
3963
+ $.ui.fancytree.registerExtension(
4076
3964
  {
3965
+ name: "dnd",
4077
3966
  version: "0.0.1",
4078
3967
  // Default options for this extension.
4079
3968
  options: {
@@ -4104,7 +3993,7 @@ $.ui.fancytree.registerExtension("dnd",
4104
3993
  if( event.which === $.ui.keyCode.ESCAPE) {
4105
3994
  this._local._cancelDrag();
4106
3995
  }
4107
- this._super(ctx);
3996
+ return this._super(ctx);
4108
3997
  },
4109
3998
  /* Display drop marker according to hitMode ('after', 'before', 'over', 'out', 'start', 'stop'). */
4110
3999
  _setDndStatus: function(sourceNode, targetNode, helper, hitMode, accept) {
@@ -4402,19 +4291,339 @@ $.ui.fancytree.registerExtension("dnd",
4402
4291
  });
4403
4292
  }(jQuery, window, document));
4404
4293
 
4294
+ /*!
4295
+ * jquery.fancytree.edit.js
4296
+ *
4297
+ * Make node titles editable.
4298
+ * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
4299
+ *
4300
+ * Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
4301
+ *
4302
+ * Released under the MIT license
4303
+ * https://github.com/mar10/fancytree/wiki/LicenseInfo
4304
+ *
4305
+ * @version 2.0.0-6
4306
+ * @date 2014-02-10T10:52
4307
+ */
4308
+ /**
4309
+ * @module fancytree/edit
4310
+ */
4311
+
4312
+ ;(function($, window, document, undefined) {
4313
+
4314
+ "use strict";
4315
+
4316
+
4317
+ /*******************************************************************************
4318
+ * Private functions and variables
4319
+ */
4320
+
4321
+ var isMac = /Mac/.test(navigator.platform)
4322
+ // modifiers = {shift: "shiftKey", ctrl: "ctrlKey", alt: "altKey", meta: "metaKey"},
4323
+ // specialKeys = {
4324
+ // 8: "backspace", 9: "tab", 10: "return", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
4325
+ // 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
4326
+ // 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
4327
+ // 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
4328
+ // 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
4329
+ // 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
4330
+ // 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 186: ";", 191: "/",
4331
+ // 220: "\\", 222: "'", 224: "meta"
4332
+ // },
4333
+ // shiftNums = {
4334
+ // "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
4335
+ // "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
4336
+ // ".": ">", "/": "?", "\\": "|"
4337
+ // }
4338
+ ;
4339
+
4340
+ // $.ui.fancytree.isKeydownEvent = function(e, code){
4341
+ // var i, part, partmap, partlist = code.split("+"), len = parts.length;
4342
+ // var c = String.fromCharCode(e.which).toLowerCase();
4343
+ // for( i = 0; i < len; i++ ) {
4344
+ // }
4345
+ // alert (parts.unshift());
4346
+ // alert (parts.unshift());
4347
+ // alert (parts.unshift());
4348
+ // };
4349
+
4350
+
4351
+ /**
4352
+ * [ext-edit] Start inline editing of current node title.
4353
+ *
4354
+ * @alias FancytreeNode#editStart
4355
+ * @requires Fancytree
4356
+ */
4357
+ $.ui.fancytree._FancytreeNodeClass.prototype.editStart = function(){
4358
+ var $input,
4359
+ node = this,
4360
+ tree = this.tree,
4361
+ local = tree.ext.edit,
4362
+ prevTitle = node.title,
4363
+ instOpts = tree.options.edit,
4364
+ $title = $(".fancytree-title", node.span),
4365
+ eventData = {node: node, tree: tree, options: tree.options};
4366
+
4367
+ if( instOpts.beforeEdit.call(node, {type: "beforeEdit"}, eventData) === false){
4368
+ return false;
4369
+ }
4370
+ // beforeEdit may want to modify the title before editing
4371
+ prevTitle = node.title;
4372
+
4373
+ node.debug("editStart");
4374
+ // Disable standard Fancytree mouse- and key handling
4375
+ tree.widget._unbind();
4376
+ // #116: ext-dnd prevents the blur event, so we have to catch outer clicks
4377
+ $(document).on("mousedown.fancytree-edit", function(event){
4378
+ if( ! $(event.target).hasClass("fancytree-edit-input") ){
4379
+ node.editEnd(true, event);
4380
+ }
4381
+ });
4382
+
4383
+ // Replace node with <input>
4384
+ $input = $("<input />", {
4385
+ "class": "fancytree-edit-input",
4386
+ value: prevTitle
4387
+ });
4388
+ if ( instOpts.adjustWidthOfs != null ) {
4389
+ $input.width($title.width() + instOpts.adjustWidthOfs);
4390
+ }
4391
+ if ( instOpts.inputCss != null ) {
4392
+ $input.css(instOpts.inputCss);
4393
+ }
4394
+ eventData.input = $input;
4395
+
4396
+ $title.html($input);
4397
+
4398
+ $.ui.fancytree.assert(!local.currentNode, "recursive edit");
4399
+ local.currentNode = this;
4400
+ // Focus <input> and bind keyboard handler
4401
+ $input
4402
+ .focus()
4403
+ .change(function(event){
4404
+ $input.addClass("fancytree-edit-dirty");
4405
+ }).keydown(function(event){
4406
+ switch( event.which ) {
4407
+ case $.ui.keyCode.ESCAPE:
4408
+ node.editEnd(false, event);
4409
+ break;
4410
+ case $.ui.keyCode.ENTER:
4411
+ node.editEnd(true, event);
4412
+ return false; // so we don't start editmode on Mac
4413
+ }
4414
+ }).blur(function(event){
4415
+ return node.editEnd(true, event);
4416
+ });
4417
+
4418
+ instOpts.edit.call(node, {type: "edit"}, eventData);
4419
+ };
4420
+
4421
+
4422
+ /**
4423
+ * [ext-edit] Stop inline editing.
4424
+ * @param {Boolean} [applyChanges=false]
4425
+ * @alias FancytreeNode#editEnd
4426
+ * @requires jquery.fancytree.edit.js
4427
+ */
4428
+ $.ui.fancytree._FancytreeNodeClass.prototype.editEnd = function(applyChanges, _event){
4429
+ var node = this,
4430
+ tree = this.tree,
4431
+ local = tree.ext.edit,
4432
+ instOpts = tree.options.edit,
4433
+ $title = $(".fancytree-title", node.span),
4434
+ $input = $title.find("input.fancytree-edit-input"),
4435
+ newVal = $input.val(),
4436
+ dirty = $input.hasClass("fancytree-edit-dirty"),
4437
+ doSave = (applyChanges || (dirty && applyChanges !== false)) && (newVal !== node.title),
4438
+ eventData = {
4439
+ node: node, tree: tree, options: tree.options, originalEvent: _event,
4440
+ dirty: dirty,
4441
+ save: doSave,
4442
+ input: $input,
4443
+ value: newVal
4444
+ };
4445
+
4446
+ if( instOpts.beforeClose.call(node, {type: "beforeClose"}, eventData) === false){
4447
+ return false;
4448
+ }
4449
+ if( doSave && instOpts.save.call(node, {type: "save"}, eventData) === false){
4450
+ return false;
4451
+ }
4452
+ $input
4453
+ .removeClass("fancytree-edit-dirty")
4454
+ .unbind();
4455
+ // Unbind outer-click handler
4456
+ $(document).off(".fancytree-edit");
4457
+
4458
+ if( doSave ) {
4459
+ node.setTitle( newVal );
4460
+ }else{
4461
+ node.renderTitle();
4462
+ }
4463
+ // Re-enable mouse and keyboard handling
4464
+ tree.widget._bind();
4465
+ local.currentNode = null;
4466
+ node.setFocus();
4467
+ // Set keyboard focus, even if setFocus() claims 'nothing to do'
4468
+ $(tree.$container).focus();
4469
+ eventData.input = null;
4470
+ instOpts.close.call(node, {type: "close"}, eventData);
4471
+ return true;
4472
+ };
4473
+
4474
+
4475
+ $.ui.fancytree._FancytreeNodeClass.prototype.startEdit = function(){
4476
+ this.warn("FancytreeNode.startEdit() is deprecated. Use .editStart() instead.");
4477
+ return this.editStart.apply(this, arguments);
4478
+ };
4479
+
4480
+
4481
+ $.ui.fancytree._FancytreeNodeClass.prototype.endEdit = function(){
4482
+ this.warn("FancytreeNode.endEdit() is deprecated. Use .editEnd() instead.");
4483
+ return this.editEnd.apply(this, arguments);
4484
+ };
4485
+
4486
+
4487
+ ///**
4488
+ // * Create a new child or sibling node.
4489
+ // *
4490
+ // * @param {String} [mode] 'before', 'after', or 'child'
4491
+ // * @lends FancytreeNode.prototype
4492
+ // * @requires jquery.fancytree.edit.js
4493
+ // */
4494
+ //$.ui.fancytree._FancytreeNodeClass.prototype.editCreateNode = function(mode){
4495
+ // var newNode,
4496
+ // node = this,
4497
+ // tree = this.tree,
4498
+ // local = tree.ext.edit,
4499
+ // instOpts = tree.options.edit,
4500
+ // $title = $(".fancytree-title", node.span),
4501
+ // $input = $title.find("input.fancytree-edit-input"),
4502
+ // newVal = $input.val(),
4503
+ // dirty = $input.hasClass("fancytree-edit-dirty"),
4504
+ // doSave = (applyChanges || (dirty && applyChanges !== false)) && (newVal !== node.title),
4505
+ // eventData = {
4506
+ // node: node, tree: tree, options: tree.options, originalEvent: _event,
4507
+ // dirty: dirty,
4508
+ // save: doSave,
4509
+ // input: $input,
4510
+ // value: newVal
4511
+ // };
4512
+ //
4513
+ // node.debug("editCreate");
4514
+ //
4515
+ // if( instOpts.beforeEdit.call(node, {type: "beforeCreateNode"}, eventData) === false){
4516
+ // return false;
4517
+ // }
4518
+ // newNode = this.addNode({title: "Neuer Knoten"}, mode);
4519
+ //
4520
+ // newNode.editStart();
4521
+ //};
4522
+
4523
+
4524
+ /**
4525
+ * [ext-edit] Check if any node in this tree in edit mode.
4526
+ *
4527
+ * @returns {FancytreeNode | null}
4528
+ * @lends Fancytree.prototype
4529
+ * @requires jquery.fancytree.edit.js
4530
+ */
4531
+ $.ui.fancytree._FancytreeClass.prototype.isEditing = function(){
4532
+ return this.ext.edit.currentNode;
4533
+ };
4534
+
4535
+
4536
+ /**
4537
+ * [ext-edit] Check if this node is in edit mode.
4538
+ * @returns {Boolean} true if node is currently beeing edited
4539
+ * @lends FancytreeNode.prototype
4540
+ * @requires jquery.fancytree.edit.js
4541
+ */
4542
+ $.ui.fancytree._FancytreeNodeClass.prototype.isEditing = function(){
4543
+ return this.tree.ext.edit.currentNode === this;
4544
+ };
4545
+
4546
+
4547
+ /*******************************************************************************
4548
+ * Extension code
4549
+ */
4550
+ $.ui.fancytree.registerExtension({
4551
+ name: "edit",
4552
+ version: "0.1.0",
4553
+ // Default options for this extension.
4554
+ options: {
4555
+ adjustWidthOfs: 4, // null: don't adjust input size to content
4556
+ inputCss: {minWidth: "3em"},
4557
+ triggerCancel: ["esc", "tab", "click"],
4558
+ // triggerStart: ["f2", "dblclick", "shift+click", "mac+enter"],
4559
+ triggerStart: ["f2", "shift+click", "mac+enter"],
4560
+ beforeClose: $.noop, // Return false to prevent cancel/save (data.input is available)
4561
+ beforeEdit: $.noop, // Return false to prevent edit mode
4562
+ close: $.noop, // Editor was removed
4563
+ edit: $.noop, // Editor was opened (available as data.input)
4564
+ // keypress: $.noop, // Not yet implemented
4565
+ save: $.noop // Save data.input.val() or return false to keep editor open
4566
+ },
4567
+ // Local attributes
4568
+ currentNode: null,
4569
+
4570
+ // Override virtual methods for this extension.
4571
+ // `this` : the Fancytree instance
4572
+ // `this._local`: the namespace that contains extension attributes and private methods (same as this.ext.EXTNAME)
4573
+ // `this._super`: the virtual function that was overridden (member of previous extension or Fancytree)
4574
+ treeInit: function(ctx){
4575
+ this._super(ctx);
4576
+ this.$container.addClass("fancytree-ext-edit");
4577
+ },
4578
+ nodeClick: function(ctx) {
4579
+ if( $.inArray("shift+click", ctx.options.edit.triggerStart) >= 0 ){
4580
+ if( ctx.originalEvent.shiftKey ){
4581
+ ctx.node.editStart();
4582
+ return false;
4583
+ }
4584
+ }
4585
+ this._super(ctx);
4586
+ },
4587
+ nodeDblclick: function(ctx) {
4588
+ if( $.inArray("dblclick", ctx.options.edit.triggerStart) >= 0 ){
4589
+ ctx.node.editStart();
4590
+ return false;
4591
+ }
4592
+ return this._super(ctx);
4593
+ },
4594
+ nodeKeydown: function(ctx) {
4595
+ switch( ctx.originalEvent.which ) {
4596
+ case 113: // [F2]
4597
+ if( $.inArray("f2", ctx.options.edit.triggerStart) >= 0 ){
4598
+ ctx.node.editStart();
4599
+ return false;
4600
+ }
4601
+ break;
4602
+ case $.ui.keyCode.ENTER:
4603
+ if( $.inArray("mac+enter", ctx.options.edit.triggerStart) >= 0 && isMac ){
4604
+ ctx.node.editStart();
4605
+ return false;
4606
+ }
4607
+ break;
4608
+ }
4609
+ return this._super(ctx);
4610
+ }
4611
+ });
4612
+ }(jQuery, window, document));
4613
+
4405
4614
  /*!
4406
4615
  * jquery.fancytree.filter.js
4407
4616
  *
4408
4617
  * Remove or highlight tree nodes, based on a filter.
4409
4618
  * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
4410
4619
  *
4411
- * Copyright (c) 2013, Martin Wendt (http://wwWendt.de)
4620
+ * Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
4412
4621
  *
4413
4622
  * Released under the MIT license
4414
4623
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
4415
4624
  *
4416
- * @version 2.0.0-5
4417
- * @date 2014-01-04T16:42
4625
+ * @version 2.0.0-6
4626
+ * @date 2014-02-10T10:52
4418
4627
  */
4419
4628
 
4420
4629
  ;(function($, window, document, undefined) {
@@ -4499,7 +4708,8 @@ $.ui.fancytree._FancytreeClass.prototype.clearFilter = function(){
4499
4708
  /*******************************************************************************
4500
4709
  * Extension code
4501
4710
  */
4502
- $.ui.fancytree.registerExtension("filter", {
4711
+ $.ui.fancytree.registerExtension({
4712
+ name: "filter",
4503
4713
  version: "0.0.1",
4504
4714
  // Default options for this extension.
4505
4715
  options: {
@@ -4558,157 +4768,143 @@ $.ui.fancytree.registerExtension("filter", {
4558
4768
  }(jQuery, window, document));
4559
4769
 
4560
4770
  /*!
4561
- * jquery.fancytree.menu.js
4771
+ * jquery.fancytree.gridnav.js
4562
4772
  *
4563
- * Enable jQuery UI Menu as context menu for tree nodes.
4773
+ * Support keyboard navigation for trees with embedded input controls.
4564
4774
  * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
4565
4775
  *
4566
- * @see http://api.jqueryui.com/menu/
4567
- *
4568
- * Copyright (c) 2013, Martin Wendt (http://wwWendt.de)
4776
+ * Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
4569
4777
  *
4570
4778
  * Released under the MIT license
4571
4779
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
4572
4780
  *
4573
- * @version 2.0.0-5
4574
- * @date 2014-01-04T16:42
4781
+ * @version 2.0.0-6
4782
+ * @date 2014-02-10T10:52
4575
4783
  */
4576
4784
 
4577
4785
  ;(function($, window, document, undefined) {
4578
4786
 
4579
4787
  "use strict";
4580
4788
 
4581
- // prevent duplicate loading
4582
- // if ( $.ui.fancytree && $.ui.fancytree.version ) {
4583
- // $.ui.fancytree.warn("Fancytree: duplicate include");
4584
- // return;
4585
- // }
4586
4789
 
4587
- $.ui.fancytree.registerExtension("menu", {
4790
+ /*******************************************************************************
4791
+ * Private functions and variables
4792
+ */
4793
+
4794
+ // Allow these navigation keys even when input controls are focused
4795
+
4796
+ var KC = $.ui.keyCode,
4797
+ // which keys are *not* handled by embedded control, but passed to tree
4798
+ // navigation handler:
4799
+ NAV_KEYS = {
4800
+ "text": [KC.UP, KC.DOWN],
4801
+ "checkbox": [KC.UP, KC.DOWN, KC.LEFT, KC.RIGHT],
4802
+ "radiobutton": [KC.UP, KC.DOWN, KC.LEFT, KC.RIGHT],
4803
+ "select-one": [KC.LEFT, KC.RIGHT],
4804
+ "select-multiple": [KC.LEFT, KC.RIGHT]
4805
+ };
4806
+
4807
+
4808
+ function findNeighbourTd($target, keyCode){
4809
+ var $td = $target.closest("td");
4810
+ switch( keyCode ){
4811
+ case KC.LEFT:
4812
+ return $td.prev();
4813
+ case KC.RIGHT:
4814
+ return $td.next();
4815
+ case KC.UP:
4816
+ return $td.parent().prevAll(":visible").first().find("td").eq($td.index());
4817
+ case KC.DOWN:
4818
+ return $td.parent().nextAll(":visible").first().find("td").eq($td.index());
4819
+ }
4820
+ return null;
4821
+ }
4822
+
4823
+ /*******************************************************************************
4824
+ * Extension code
4825
+ */
4826
+ $.ui.fancytree.registerExtension({
4827
+ name: "gridnav",
4588
4828
  version: "0.0.1",
4589
4829
  // Default options for this extension.
4590
4830
  options: {
4591
- enable: true,
4592
- selector: null, //
4593
- position: {}, //
4594
- // Events:
4595
- create: $.noop, //
4596
- beforeOpen: $.noop, //
4597
- open: $.noop, //
4598
- focus: $.noop, //
4599
- select: $.noop, //
4600
- close: $.noop //
4831
+ autofocusInput: false, // Focus first embedded input if node gets activated
4832
+ handleCursorKeys: true // Allow UP/DOWN in inputs to move to prev/next node
4601
4833
  },
4602
- // Override virtual methods for this extension.
4603
- // `this` : is this extension object
4604
- // `this._base` : the Fancytree instance
4605
- // `this._super`: the virtual function that was overridden (member of prev. extension or Fancytree)
4606
- treeInit: function(ctx){
4607
- var opts = ctx.options,
4608
- tree = ctx.tree;
4609
4834
 
4835
+ treeInit: function(ctx){
4836
+ // gridnav requires the table extension to be loaded before itself
4837
+ this._requireExtension("table", true, true);
4610
4838
  this._super(ctx);
4611
4839
 
4612
- // Prepare an object that will be passed with menu events
4613
- tree.ext.menu.data = {
4614
- tree: tree,
4615
- node: null,
4616
- $menu: null,
4617
- menuId: null
4618
- };
4840
+ this.$container.addClass("fancytree-ext-gridnav");
4619
4841
 
4620
- // tree.$container[0].oncontextmenu = function() {return false;};
4621
- // Replace the standard browser context menu with out own
4622
- tree.$container.delegate("span.fancytree-node", "contextmenu", function(event) {
4623
- var node = $.ui.fancytree.getNode(event),
4624
- ctx = {node: node, tree: node.tree, originalEvent: event, options: tree.options};
4625
- tree.ext.menu._openMenu(ctx);
4626
- return false;
4627
- });
4842
+ // Activate node if embedded input gets focus (due to a click)
4843
+ this.$container.on("focusin", function(event){
4844
+ var ctx2,
4845
+ node = $.ui.fancytree.getNode(event.target);
4628
4846
 
4629
- // Use jquery.ui.menu
4630
- $(opts.menu.selector).menu({
4631
- create: function(event, ui){
4632
- tree.ext.menu.data.$menu = $(this).menu("widget");
4633
- var data = $.extend({}, tree.ext.menu.data);
4634
- opts.menu.create.call(tree, event, data);
4635
- },
4636
- focus: function(event, ui){
4637
- var data = $.extend({}, tree.ext.menu.data, {
4638
- menuItem: ui.item,
4639
- menuId: ui.item.find(">a").attr("href")
4640
- });
4641
- opts.menu.focus.call(tree, event, data);
4642
- },
4643
- select: function(event, ui){
4644
- var data = $.extend({}, tree.ext.menu.data, {
4645
- menuItem: ui.item,
4646
- menuId: ui.item.find(">a").attr("href")
4647
- });
4648
- if( opts.menu.select.call(tree, event, data) !== false){
4649
- tree.ext.menu._closeMenu(ctx);
4650
- }
4847
+ if( node && !node.isActive() ){
4848
+ // Call node.setActive(), but also pass the event
4849
+ ctx2 = ctx.tree._makeHookContext(node, event);
4850
+ ctx.tree._callHook("nodeSetActive", ctx2, true);
4651
4851
  }
4652
- }).hide();
4653
- },
4654
- treeDestroy: function(ctx){
4655
- this._super(ctx);
4852
+ });
4656
4853
  },
4657
- _openMenu: function(ctx){
4658
- var data,
4659
- tree = ctx.tree,
4660
- opts = ctx.options,
4661
- $menu = $(opts.menu.selector);
4854
+ nodeSetActive: function(ctx, flag) {
4855
+ var $outer,
4856
+ opts = ctx.options.gridnav,
4857
+ node = ctx.node,
4858
+ event = ctx.originalEvent || {},
4859
+ triggeredByInput = $(event.target).is(":input");
4662
4860
 
4663
- tree.ext.menu.data.node = ctx.node;
4664
- data = $.extend({}, tree.ext.menu.data);
4861
+ flag = (flag !== false);
4665
4862
 
4666
- if( opts.menu.beforeOpen.call(tree, ctx.originalEvent, data) === false){
4667
- return;
4668
- }
4863
+ this._super(ctx, flag);
4669
4864
 
4670
- $(document).bind("keydown.fancytree", function(event){
4671
- if( event.which === $.ui.keyCode.ESCAPE ){
4672
- tree.ext.menu._closeMenu(ctx);
4673
- }
4674
- }).bind("mousedown.fancytree", function(event){
4675
- // Close menu when clicked outside menu
4676
- if( $(event.target).closest(".ui-menu-item").length === 0 ){
4677
- tree.ext.menu._closeMenu(ctx);
4865
+ if( flag ){
4866
+ if( ctx.options.titlesTabbable ){
4867
+ if( !triggeredByInput ) {
4868
+ $(node.span).find("span.fancytree-title").focus();
4869
+ node.setFocus();
4870
+ }
4871
+ // If one node is tabbable, the container no longer needs to be
4872
+ ctx.tree.$container.attr("tabindex", "-1");
4873
+ // ctx.tree.$container.removeAttr("tabindex");
4874
+ } else if( opts.autofocusInput && !triggeredByInput ){
4875
+ // Set focus to input sub input (if node was clicked, but not
4876
+ // when TAB was pressed )
4877
+ $outer = $(node.tr || node.span);
4878
+ $outer.find(":input:enabled:first").focus();
4678
4879
  }
4679
- });
4680
- // $menu.position($.extend({my: "left top", at: "left bottom", of: event}, opts.menu.position));
4681
- $menu
4682
- .css("position", "absolute")
4683
- .show()
4684
- .position({my: "left top", at: "right top", of: ctx.originalEvent, collision: "fit"})
4685
- .focus();
4686
-
4687
- opts.menu.open.call(tree, ctx.originalEvent, data);
4880
+ }
4688
4881
  },
4689
- _closeMenu: function(ctx){
4690
- var $menu,
4691
- tree = ctx.tree,
4692
- opts = ctx.options,
4693
- data = $.extend({}, tree.ext.menu.data);
4694
- if( opts.menu.close.call(tree, ctx.originalEvent, data) === false){
4695
- return;
4882
+ nodeKeydown: function(ctx) {
4883
+ var inputType, handleKeys, $td,
4884
+ opts = ctx.options.gridnav,
4885
+ event = ctx.originalEvent,
4886
+ $target = $(event.target);
4887
+
4888
+ // jQuery
4889
+ inputType = $target.is(":input:enabled") ? $target.prop("type") : null;
4890
+ ctx.tree.debug("ext-gridnav nodeKeydown", event, inputType);
4891
+
4892
+ if( inputType && opts.handleCursorKeys ){
4893
+ handleKeys = NAV_KEYS[inputType];
4894
+ if( handleKeys && $.inArray(event.which, handleKeys) >= 0 ){
4895
+ $td = findNeighbourTd($target, event.which);
4896
+ // ctx.node.debug("ignore keydown in input", event.which, handleKeys);
4897
+ if( $td && $td.length ) {
4898
+ $td.find(":input:enabled").focus();
4899
+ // Prevent Fancytree default navigation
4900
+ return false;
4901
+ }
4902
+ }
4903
+ return true;
4696
4904
  }
4697
- $menu = $(opts.menu.selector);
4698
- $(document).unbind("keydown.fancytree, mousedown.fancytree");
4699
- $menu.hide();
4700
- tree.ext.menu.data.node = null;
4905
+ ctx.tree.debug("ext-gridnav NOT HANDLED", event, inputType);
4906
+ return this._super(ctx);
4701
4907
  }
4702
- // ,
4703
- // nodeClick: function(ctx) {
4704
- // var event = ctx.originalEvent;
4705
- // if(event.which === 2 || (event.which === 1 && event.ctrlKey)){
4706
- // event.preventDefault();
4707
- // ctx.tree.ext.menu._openMenu(ctx);
4708
- // return false;
4709
- // }
4710
- // this._super(ctx);
4711
- // }
4712
4908
  });
4713
4909
  }(jQuery, window, document));
4714
4910
 
@@ -4720,13 +4916,13 @@ $.ui.fancytree.registerExtension("menu", {
4720
4916
  *
4721
4917
  * @depends: jquery.cookie.js
4722
4918
  *
4723
- * Copyright (c) 2013, Martin Wendt (http://wwWendt.de)
4919
+ * Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
4724
4920
  *
4725
4921
  * Released under the MIT license
4726
4922
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
4727
4923
  *
4728
- * @version 2.0.0-5
4729
- * @date 2014-01-04T16:42
4924
+ * @version 2.0.0-6
4925
+ * @date 2014-02-10T10:52
4730
4926
  */
4731
4927
 
4732
4928
  ;(function($, window, document, undefined) {
@@ -4803,7 +4999,8 @@ $.ui.fancytree._FancytreeClass.prototype.getPersistData = function(){
4803
4999
  /* *****************************************************************************
4804
5000
  * Extension code
4805
5001
  */
4806
- $.ui.fancytree.registerExtension("persist", {
5002
+ $.ui.fancytree.registerExtension({
5003
+ name: "persist",
4807
5004
  version: "0.0.1",
4808
5005
  // Default options for this extension.
4809
5006
  options: {
@@ -4927,11 +5124,11 @@ $.ui.fancytree.registerExtension("persist", {
4927
5124
  // treeDestroy: function(ctx){
4928
5125
  // this._super(ctx);
4929
5126
  // },
4930
- nodeSetActive: function(ctx, flag) {
5127
+ nodeSetActive: function(ctx, flag, opts) {
4931
5128
  var instData = this._local,
4932
5129
  instOpts = this.options.persist;
4933
5130
 
4934
- this._super(ctx, flag);
5131
+ this._super(ctx, flag, opts);
4935
5132
 
4936
5133
  if(instData.storeActive){
4937
5134
  $.cookie(instData.cookiePrefix + ACTIVE,
@@ -4939,15 +5136,17 @@ $.ui.fancytree.registerExtension("persist", {
4939
5136
  instOpts.cookie);
4940
5137
  }
4941
5138
  },
4942
- nodeSetExpanded: function(ctx, flag) {
4943
- var node = ctx.node,
5139
+ nodeSetExpanded: function(ctx, flag, opts) {
5140
+ var res,
5141
+ node = ctx.node,
4944
5142
  instData = this._local;
4945
5143
 
4946
- this._super(ctx, flag);
5144
+ res = this._super(ctx, flag, opts);
4947
5145
 
4948
5146
  if(instData.storeExpanded){
4949
5147
  instData._setKey(EXPANDED, node.key, flag);
4950
5148
  }
5149
+ return res;
4951
5150
  },
4952
5151
  nodeSetFocus: function(ctx) {
4953
5152
  var instData = this._local,
@@ -4980,13 +5179,13 @@ $.ui.fancytree.registerExtension("persist", {
4980
5179
  * Render tree as table (aka 'treegrid', 'tabletree').
4981
5180
  * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
4982
5181
  *
4983
- * Copyright (c) 2013, Martin Wendt (http://wwWendt.de)
5182
+ * Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
4984
5183
  *
4985
5184
  * Released under the MIT license
4986
5185
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
4987
5186
  *
4988
- * @version 2.0.0-5
4989
- * @date 2014-01-04T16:42
5187
+ * @version 2.0.0-6
5188
+ * @date 2014-02-10T10:52
4990
5189
  */
4991
5190
 
4992
5191
  ;(function($, window, document, undefined) {
@@ -5047,8 +5246,9 @@ function findPrevRowNode(node){
5047
5246
  }
5048
5247
 
5049
5248
 
5050
- $.ui.fancytree.registerExtension("table", {
5051
- version: "0.0.1",
5249
+ $.ui.fancytree.registerExtension({
5250
+ name: "table",
5251
+ version: "0.1.0",
5052
5252
  // Default options for this extension.
5053
5253
  options: {
5054
5254
  indentation: 16, // indent every node level by 16px
@@ -5132,16 +5332,16 @@ $.ui.fancytree.registerExtension("table", {
5132
5332
  node = ctx.node,
5133
5333
  opts = ctx.options,
5134
5334
  isRootNode = !node.parent;
5135
- // firstTime = false;
5335
+
5136
5336
  if( !_recursive ){
5137
5337
  ctx.hasCollapsedParents = node.parent && !node.parent.expanded;
5138
5338
  }
5339
+ $.ui.fancytree.debug("*** nodeRender " + node + ", isRoot=" + isRootNode);
5139
5340
  if( !isRootNode ){
5140
5341
  if(!node.tr){
5141
5342
  // Create new <tr> after previous row
5142
5343
  newRow = tree.rowFragment.firstChild.cloneNode(true);
5143
5344
  prevNode = findPrevRowNode(node);
5144
- // firstTime = true;
5145
5345
  // $.ui.fancytree.debug("*** nodeRender " + node + ": prev: " + prevNode.key);
5146
5346
  _assert(prevNode);
5147
5347
  if(collapsed === true && _recursive){
@@ -5178,7 +5378,7 @@ $.ui.fancytree.registerExtension("table", {
5178
5378
  }
5179
5379
  } else {
5180
5380
  // Set icon, link, and title (normally this is only required on initial render)
5181
- this.nodeRenderTitle(ctx);
5381
+ //this.nodeRenderTitle(ctx);
5182
5382
  }
5183
5383
  }
5184
5384
  // Allow tweaking after node state was rendered
@@ -5203,10 +5403,10 @@ $.ui.fancytree.registerExtension("table", {
5203
5403
  // Iterate over all descendants
5204
5404
  node.visit(function(n){
5205
5405
  if(n.tr){
5206
- if(!node.expanded && !isRootNode && n.tr.style.display !== "none"){
5207
- // fix after a node was dropped over a sibling.
5208
- // In this case it must be hidden
5406
+ if(!n.parent.expanded && n.tr.style.display !== "none"){
5407
+ // fix after a node was dropped over a collapsed
5209
5408
  n.tr.style.display = "none";
5409
+ setChildRowVisibility(n, false);
5210
5410
  }
5211
5411
  if(n.tr.previousSibling !== prevTr){
5212
5412
  node.debug("_fixOrder: mismatch at node: " + n);
@@ -5221,12 +5421,6 @@ $.ui.fancytree.registerExtension("table", {
5221
5421
  if(!isRootNode){
5222
5422
  this.nodeRenderStatus(ctx);
5223
5423
  }
5224
- // Finally add the whole structure to the DOM, so the browser can render
5225
- // if(firstTime){
5226
- // parent.ul.appendChild(node.li);
5227
- // }
5228
- // TODO: just for debugging
5229
- // this._super(ctx);
5230
5424
  },
5231
5425
  nodeRenderTitle: function(ctx, title) {
5232
5426
  var $cb,
@@ -5234,12 +5428,12 @@ $.ui.fancytree.registerExtension("table", {
5234
5428
  opts = ctx.options;
5235
5429
 
5236
5430
  this._super(ctx);
5237
- // move checkbox to custom column
5431
+ // Move checkbox to custom column
5238
5432
  if(opts.checkbox && opts.table.checkboxColumnIdx != null){
5239
5433
  $cb = $("span.fancytree-checkbox", node.span).detach();
5240
5434
  $(node.tr).find("td:first").html($cb);
5241
5435
  }
5242
- // let user code write column content
5436
+ // Let user code write column content
5243
5437
  // ctx.tree._triggerNodeEvent("renderColumns", node);
5244
5438
  if ( opts.renderColumns ){
5245
5439
  opts.renderColumns.call(ctx.tree, {type: "renderColumns"}, ctx);
@@ -5251,16 +5445,15 @@ $.ui.fancytree.registerExtension("table", {
5251
5445
  opts = ctx.options;
5252
5446
 
5253
5447
  this._super(ctx);
5448
+
5254
5449
  $(node.tr).removeClass("fancytree-node");
5255
5450
  // indent
5256
5451
  indent = (node.getLevel() - 1) * opts.table.indentation;
5257
- if(indent){
5258
- $(node.span).css({marginLeft: indent + "px"});
5259
- }
5452
+ $(node.span).css({marginLeft: indent + "px"});
5260
5453
  },
5261
5454
  /* Expand node, return Deferred.promise. */
5262
- nodeSetExpanded: function(ctx, flag) {
5263
- return this._super(ctx, flag).always(function () {
5455
+ nodeSetExpanded: function(ctx, flag, opts) {
5456
+ return this._super(ctx, flag, opts).always(function () {
5264
5457
  flag = (flag !== false);
5265
5458
  setChildRowVisibility(ctx.node, flag);
5266
5459
  });
@@ -5291,13 +5484,13 @@ $.ui.fancytree.registerExtension("table", {
5291
5484
  *
5292
5485
  * @see http://jqueryui.com/themeroller/
5293
5486
  *
5294
- * Copyright (c) 2013, Martin Wendt (http://wwWendt.de)
5487
+ * Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
5295
5488
  *
5296
5489
  * Released under the MIT license
5297
5490
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
5298
5491
  *
5299
- * @version 2.0.0-5
5300
- * @date 2014-01-04T16:42
5492
+ * @version 2.0.0-6
5493
+ * @date 2014-02-10T10:52
5301
5494
  */
5302
5495
 
5303
5496
  ;(function($, window, document, undefined) {
@@ -5307,7 +5500,8 @@ $.ui.fancytree.registerExtension("table", {
5307
5500
  /*******************************************************************************
5308
5501
  * Extension code
5309
5502
  */
5310
- $.ui.fancytree.registerExtension("themeroller", {
5503
+ $.ui.fancytree.registerExtension({
5504
+ name: "themeroller",
5311
5505
  version: "0.0.1",
5312
5506
  // Default options for this extension.
5313
5507
  options: {