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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: {