fancytree-rails 2.0.0.pre.11.pre.1 → 2.1.0.pre.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/3th-party/jquery-ui.License.txt +2 -0
  3. data/README.md +7 -1
  4. data/Rakefile +21 -5
  5. data/{vendor → app}/assets/images/fancytree/icons.gif +0 -0
  6. data/{vendor → app}/assets/images/fancytree/loading.gif +0 -0
  7. data/app/assets/images/fancytree/skin-lion/icons.gif +0 -0
  8. data/{vendor → app}/assets/images/fancytree/skin-lion/loading.gif +0 -0
  9. data/app/assets/images/fancytree/skin-themeroller/icons.gif +0 -0
  10. data/{vendor → app}/assets/images/fancytree/skin-themeroller/loading.gif +0 -0
  11. data/app/assets/images/fancytree/skin-vista/icons.gif +0 -0
  12. data/{vendor → app}/assets/images/fancytree/skin-vista/loading.gif +0 -0
  13. data/app/assets/images/fancytree/skin-win7/icons.gif +0 -0
  14. data/{vendor → app}/assets/images/fancytree/skin-win7/loading.gif +0 -0
  15. data/{vendor → app}/assets/images/fancytree/skin-win8-xxl/icons.gif +0 -0
  16. data/{vendor → app}/assets/images/fancytree/skin-win8-xxl/loading.gif +0 -0
  17. data/app/assets/images/fancytree/skin-win8/icons.gif +0 -0
  18. data/{vendor → app}/assets/images/fancytree/skin-win8/loading.gif +0 -0
  19. data/{vendor → app}/assets/images/fancytree/skin-xp/icons-rtl.gif +0 -0
  20. data/{vendor → app}/assets/images/fancytree/skin-xp/icons.gif +0 -0
  21. data/{vendor → app}/assets/images/fancytree/skin-xp/loading.gif +0 -0
  22. data/{vendor → app}/assets/images/fancytree/skin-xp/vline-rtl.gif +0 -0
  23. data/{vendor → app}/assets/images/fancytree/skin-xp/vline.gif +0 -0
  24. data/{vendor → app}/assets/javascripts/fancytree.js +0 -0
  25. data/{vendor → app}/assets/javascripts/fancytree/MIT-LICENSE.txt +0 -0
  26. data/{vendor → app}/assets/javascripts/fancytree/jquery.fancytree-all.js +346 -333
  27. data/app/assets/javascripts/fancytree/jquery.fancytree-custom.min.js +41 -0
  28. data/{vendor → app}/assets/javascripts/fancytree/jquery.fancytree.js +109 -66
  29. data/app/assets/javascripts/fancytree/jquery.fancytree.min.js +14 -0
  30. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.childcounter.js +3 -3
  31. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.clones.js +36 -6
  32. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.columnview.js +2 -2
  33. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.debug.js +2 -2
  34. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.dnd.js +35 -51
  35. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.edit.js +5 -9
  36. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.filter.js +54 -66
  37. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.glyph.js +6 -9
  38. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.gridnav.js +2 -2
  39. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.js +109 -66
  40. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.menu.js +2 -2
  41. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.persist.js +106 -112
  42. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.table.js +23 -9
  43. data/{vendor → app}/assets/javascripts/fancytree/src/jquery.fancytree.themeroller.js +3 -6
  44. data/{vendor/assets/stylesheets/fancytree/skin-awesome/ui.fancytree.css → app/assets/stylesheets/fancytree/skin-awesome/ui.fancytree.css.erb} +30 -5
  45. data/app/assets/stylesheets/fancytree/skin-awesome/ui.fancytree.min.css.erb +6 -0
  46. data/{vendor/assets/stylesheets/fancytree/skin-bootstrap/ui.fancytree.css → app/assets/stylesheets/fancytree/skin-bootstrap/ui.fancytree.css.erb} +31 -6
  47. data/app/assets/stylesheets/fancytree/skin-bootstrap/ui.fancytree.min.css.erb +6 -0
  48. data/{vendor/assets/stylesheets/fancytree/skin-lion/ui.fancytree.css → app/assets/stylesheets/fancytree/skin-lion/ui.fancytree.css.erb} +35 -14
  49. data/app/assets/stylesheets/fancytree/skin-lion/ui.fancytree.min.css.erb +6 -0
  50. data/{vendor/assets/stylesheets/fancytree/skin-themeroller/ui.fancytree.css → app/assets/stylesheets/fancytree/skin-themeroller/ui.fancytree.css.erb} +1 -1
  51. data/{vendor/assets/stylesheets/fancytree/skin-themeroller/ui.fancytree.min.css → app/assets/stylesheets/fancytree/skin-themeroller/ui.fancytree.min.css.erb} +0 -0
  52. data/{vendor/assets/stylesheets/fancytree/skin-vista/ui.fancytree.css → app/assets/stylesheets/fancytree/skin-vista/ui.fancytree.css.erb} +35 -14
  53. data/app/assets/stylesheets/fancytree/skin-vista/ui.fancytree.min.css.erb +6 -0
  54. data/{vendor/assets/stylesheets/fancytree/skin-win7/ui.fancytree.css → app/assets/stylesheets/fancytree/skin-win7/ui.fancytree.css.erb} +35 -14
  55. data/app/assets/stylesheets/fancytree/skin-win7/ui.fancytree.min.css.erb +6 -0
  56. data/{vendor/assets/stylesheets/fancytree/skin-win8-xxl/ui.fancytree.css → app/assets/stylesheets/fancytree/skin-win8-xxl/ui.fancytree.css.erb} +37 -16
  57. data/app/assets/stylesheets/fancytree/skin-win8-xxl/ui.fancytree.min.css.erb +11 -0
  58. data/{vendor/assets/stylesheets/fancytree/skin-win8/ui.fancytree.css → app/assets/stylesheets/fancytree/skin-win8/ui.fancytree.css.erb} +37 -16
  59. data/app/assets/stylesheets/fancytree/skin-win8/ui.fancytree.min.css.erb +6 -0
  60. data/{vendor/assets/stylesheets/fancytree/skin-xp/ui.fancytree.css → app/assets/stylesheets/fancytree/skin-xp/ui.fancytree.css.erb} +35 -14
  61. data/app/assets/stylesheets/fancytree/skin-xp/ui.fancytree.min.css.erb +6 -0
  62. data/{vendor → app}/assets/stylesheets/ui.fancytree.css +0 -0
  63. data/lib/fancytree/rails/version.rb +2 -2
  64. metadata +77 -76
  65. data/vendor/assets/images/fancytree/skin-lion/icons.gif +0 -0
  66. data/vendor/assets/images/fancytree/skin-themeroller/icons.gif +0 -0
  67. data/vendor/assets/images/fancytree/skin-vista/icons.gif +0 -0
  68. data/vendor/assets/images/fancytree/skin-win7/icons.gif +0 -0
  69. data/vendor/assets/images/fancytree/skin-win8/icons.gif +0 -0
  70. data/vendor/assets/javascripts/fancytree/jquery.fancytree-custom.min.js +0 -41
  71. data/vendor/assets/javascripts/fancytree/jquery.fancytree.min.js +0 -14
  72. data/vendor/assets/stylesheets/fancytree/skin-awesome/ui.fancytree.min.css +0 -6
  73. data/vendor/assets/stylesheets/fancytree/skin-bootstrap/ui.fancytree.min.css +0 -6
  74. data/vendor/assets/stylesheets/fancytree/skin-lion/ui.fancytree.min.css +0 -6
  75. data/vendor/assets/stylesheets/fancytree/skin-vista/ui.fancytree.min.css +0 -6
  76. data/vendor/assets/stylesheets/fancytree/skin-win7/ui.fancytree.min.css +0 -6
  77. data/vendor/assets/stylesheets/fancytree/skin-win8-xxl/ui.fancytree.min.css +0 -11
  78. data/vendor/assets/stylesheets/fancytree/skin-win8/ui.fancytree.min.css +0 -6
  79. data/vendor/assets/stylesheets/fancytree/skin-xp/ui.fancytree.min.css +0 -6
@@ -9,8 +9,8 @@
9
9
  * Released under the MIT license
10
10
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
11
11
  *
12
- * @version 2.0.0-11
13
- * @date 2014-04-27T22:28
12
+ * @version 2.1.0
13
+ * @date 2014-05-29T16:44
14
14
  */
15
15
 
16
16
  ;(function($, window, document, undefined) {
@@ -27,7 +27,7 @@ function _getIcon(opts, type){
27
27
 
28
28
  $.ui.fancytree.registerExtension({
29
29
  name: "glyph",
30
- version: "0.0.2",
30
+ version: "0.1.0",
31
31
  // Default options for this extension.
32
32
  options: {
33
33
  prefix: "icon-",
@@ -41,19 +41,15 @@ $.ui.fancytree.registerExtension({
41
41
  error: "icon-exclamation-sign",
42
42
  expanderClosed: "icon-caret-right",
43
43
  expanderLazy: "icon-angle-right",
44
- // expanderLazy: "icon-refresh icon-spin",
45
44
  expanderOpen: "icon-caret-down",
46
45
  folder: "icon-folder-close-alt",
47
46
  folderOpen: "icon-folder-open-alt",
48
47
  loading: "icon-refresh icon-spin",
49
- // loading: "icon-spinner icon-spin"
50
48
  noExpander: ""
51
49
  },
52
50
  icon: null // TODO: allow callback here
53
51
  },
54
- // Overide virtual methods for this extension.
55
- // `this` : is this extension object
56
- // `this._super`: the virtual function that was overriden (member of prev. extension or Fancytree)
52
+
57
53
  treeInit: function(ctx){
58
54
  var tree = ctx.tree;
59
55
  this._super(ctx);
@@ -117,7 +113,8 @@ $.ui.fancytree.registerExtension({
117
113
  if(node.parent){
118
114
  span = $("span.fancytree-expander", node.span).get(0);
119
115
  }else{
120
- span = $(".fancytree-statusnode-wait, .fancytree-statusnode-error", node[this.nodeContainerAttrName]).find("span.fancytree-expander").get(0);
116
+ span = $(".fancytree-statusnode-wait, .fancytree-statusnode-error", node[this.nodeContainerAttrName])
117
+ .find("span.fancytree-expander").get(0);
121
118
  }
122
119
  if( status === "loading"){
123
120
  // $("span.fancytree-expander", ctx.node.span).addClass(_getIcon(opts, "loading"));
@@ -9,8 +9,8 @@
9
9
  * Released under the MIT license
10
10
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
11
11
  *
12
- * @version 2.0.0-11
13
- * @date 2014-04-27T22:28
12
+ * @version 2.1.0
13
+ * @date 2014-05-29T16:44
14
14
  */
15
15
 
16
16
  ;(function($, window, document, undefined) {
@@ -7,8 +7,8 @@
7
7
  * Released under the MIT license
8
8
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
9
9
  *
10
- * @version 2.0.0-11
11
- * @date 2014-04-27T22:28
10
+ * @version 2.1.0
11
+ * @date 2014-05-29T16:44
12
12
  */
13
13
 
14
14
  /** Core Fancytree module.
@@ -61,6 +61,11 @@ function consoleApply(method, args){
61
61
  }
62
62
  }
63
63
 
64
+ /*Return true if x is a FancytreeNode.*/
65
+ function _isNode(x){
66
+ return !!(x.tree && x.statusNodeType !== undefined);
67
+ }
68
+
64
69
  /** Return true if dotted version string is equal or higher than requested version.
65
70
  *
66
71
  * See http://jsfiddle.net/mar10/FjSAN/
@@ -275,6 +280,8 @@ function FancytreeNode(parent, obj){
275
280
  } else {
276
281
  this.key = "_" + (FT._nextNodeKey++);
277
282
  }
283
+ } else {
284
+ this.key = "" + this.key; // Convert to string (#217)
278
285
  }
279
286
 
280
287
  // Fix tree.activeNode
@@ -683,9 +690,8 @@ FancytreeNode.prototype = /** @lends FancytreeNode# */{
683
690
  // recursively set children and render
684
691
  this.removeChildren();
685
692
  this.addChildren(dict.children);
686
- }else{
687
- this.renderTitle();
688
693
  }
694
+ this.renderTitle();
689
695
  /*
690
696
  var children = dict.children;
691
697
  if(children === undefined){
@@ -1350,79 +1356,93 @@ FancytreeNode.prototype = /** @lends FancytreeNode# */{
1350
1356
  /**
1351
1357
  *
1352
1358
  * @param {boolean | PlainObject} [effects=false] animation options.
1353
- * @param {FancytreeNode} [topNode=null] this node will remain visible in
1359
+ * @param {object} [options=null] {topNode: null, effects: ..., parent: ...} this node will remain visible in
1354
1360
  * any case, even if `this` is outside the scroll pane.
1355
1361
  * @returns {$.Promise}
1356
1362
  */
1357
- scrollIntoView: function(effects, topNode) {
1358
- effects = (effects === true) ? {duration: 200, queue: false} : effects;
1359
- var topNodeY,
1363
+ scrollIntoView: function(effects, options) {
1364
+ if( options !== undefined && _isNode(options) ) {
1365
+ this.warn("scrollIntoView() with 'topNode' option is deprecated since 2014-05-08. Use 'options.topNode' instead.");
1366
+ options = {topNode: options};
1367
+ }
1368
+ // this.$scrollParent = (this.options.scrollParent === "auto") ? $ul.scrollParent() : $(this.options.scrollParent);
1369
+ // this.$scrollParent = this.$scrollParent.length ? this.$scrollParent || this.$container;
1370
+
1371
+ var topNodeY, nodeY, horzScrollbarHeight, containerOffsetTop,
1372
+ opts = $.extend({
1373
+ effects: (effects === true) ? {duration: 200, queue: false} : effects,
1374
+ scrollOfs: this.tree.options.scrollOfs,
1375
+ scrollParent: this.tree.options.scrollParent || this.tree.$container,
1376
+ topNode: null
1377
+ }, options),
1360
1378
  dfd = new $.Deferred(),
1361
1379
  that = this,
1362
- nodeY = $(this.span).position().top,
1363
1380
  nodeHeight = $(this.span).height(),
1364
- $container = this.tree.$container,
1365
- scrollTop = $container[0].scrollTop,
1366
- horzScrollHeight = Math.max(0, ($container.innerHeight() - $container[0].clientHeight)),
1367
- // containerHeight = $container.height(),
1368
- containerHeight = $container.height() - horzScrollHeight,
1381
+ $container = $(opts.scrollParent),
1382
+ topOfs = opts.scrollOfs.top || 0,
1383
+ bottomOfs = opts.scrollOfs.bottom || 0,
1384
+ containerHeight = $container.height(),// - topOfs - bottomOfs,
1385
+ scrollTop = $container.scrollTop(),
1386
+ $animateTarget = $container,
1387
+ isParentWindow = $container[0] === window,
1388
+ topNode = opts.topNode || null,
1369
1389
  newScrollTop = null;
1370
1390
 
1371
- // console.log("horzScrollHeight: " + horzScrollHeight);
1372
- // console.log("$container[0].scrollTop: " + $container[0].scrollTop);
1373
- // console.log("$container[0].scrollHeight: " + $container[0].scrollHeight);
1374
- // console.log("$container[0].clientHeight: " + $container[0].clientHeight);
1375
- // console.log("$container.innerHeight(): " + $container.innerHeight());
1376
- // console.log("$container.height(): " + $container.height());
1377
-
1378
- if(nodeY < 0){
1379
- newScrollTop = scrollTop + nodeY;
1380
- }else if((nodeY + nodeHeight) > containerHeight){
1381
- newScrollTop = scrollTop + nodeY - containerHeight + nodeHeight;
1391
+ // this.debug("scrollIntoView(), scrollTop=", scrollTop, opts.scrollOfs);
1392
+ _assert($(this.span).is(":visible"), "scrollIntoView node is invisible"); // otherwise we cannot calc offsets
1393
+
1394
+ if( isParentWindow ) {
1395
+ nodeY = $(this.span).offset().top;
1396
+ topNodeY = topNode ? $(topNode.span).offset().top : 0;
1397
+ $animateTarget = $("html,body");
1398
+
1399
+ } else {
1400
+ _assert($container[0] !== document && $container[0] !== document.body, "scrollParent should be an simple element or `window`, not document or body.");
1401
+
1402
+ containerOffsetTop = $container.offset().top,
1403
+ nodeY = $(this.span).offset().top - containerOffsetTop + scrollTop; // relative to scroll parent
1404
+ topNodeY = topNode ? $(topNode.span).offset().top - containerOffsetTop + scrollTop : 0;
1405
+ horzScrollbarHeight = Math.max(0, ($container.innerHeight() - $container[0].clientHeight));
1406
+ containerHeight -= horzScrollbarHeight;
1407
+ }
1408
+
1409
+ // this.debug(" scrollIntoView(), nodeY=", nodeY, "containerHeight=", containerHeight);
1410
+ if( nodeY < (scrollTop + topOfs) ){
1411
+ // Node is above visible container area
1412
+ newScrollTop = nodeY - topOfs;
1413
+ // this.debug(" scrollIntoView(), UPPER newScrollTop=", newScrollTop);
1414
+
1415
+ }else if((nodeY + nodeHeight) > (scrollTop + containerHeight - bottomOfs)){
1416
+ newScrollTop = nodeY + nodeHeight - containerHeight + bottomOfs;
1417
+ // this.debug(" scrollIntoView(), LOWER newScrollTop=", newScrollTop);
1382
1418
  // If a topNode was passed, make sure that it is never scrolled
1383
1419
  // outside the upper border
1384
1420
  if(topNode){
1385
- topNodeY = topNode ? $(topNode.span).position().top : 0;
1386
- if((nodeY - topNodeY) > containerHeight){
1387
- newScrollTop = scrollTop + topNodeY;
1421
+ _assert($(topNode.span).is(":visible"));
1422
+ if( topNodeY < newScrollTop ){
1423
+ newScrollTop = topNodeY - topOfs;
1424
+ // this.debug(" scrollIntoView(), TOP newScrollTop=", newScrollTop);
1388
1425
  }
1389
1426
  }
1390
1427
  }
1428
+
1391
1429
  if(newScrollTop !== null){
1392
- if(effects){
1393
- // TODO: resolve dfd after animation
1394
- // var that = this;
1395
- effects.complete = function(){
1430
+ // this.debug(" scrollIntoView(), SET newScrollTop=", newScrollTop);
1431
+ if(opts.effects){
1432
+ opts.effects.complete = function(){
1396
1433
  dfd.resolveWith(that);
1397
1434
  };
1398
- $container.animate({
1435
+ $animateTarget.stop(true).animate({
1399
1436
  scrollTop: newScrollTop
1400
- }, effects);
1437
+ }, opts.effects);
1401
1438
  }else{
1402
- $container[0].scrollTop = newScrollTop;
1439
+ $animateTarget[0].scrollTop = newScrollTop;
1403
1440
  dfd.resolveWith(this);
1404
1441
  }
1405
1442
  }else{
1406
1443
  dfd.resolveWith(this);
1407
1444
  }
1408
1445
  return dfd.promise();
1409
- /* from jQuery.menu:
1410
- var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
1411
- if ( this._hasScroll() ) {
1412
- borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
1413
- paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
1414
- offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
1415
- scroll = this.activeMenu.scrollTop();
1416
- elementHeight = this.activeMenu.height();
1417
- itemHeight = item.height();
1418
-
1419
- if ( offset < 0 ) {
1420
- this.activeMenu.scrollTop( scroll + offset );
1421
- } else if ( offset + itemHeight > elementHeight ) {
1422
- this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
1423
- }
1424
- }
1425
- */
1426
1446
  },
1427
1447
 
1428
1448
  /**Activate this node.
@@ -2817,8 +2837,8 @@ $.extend(Fancytree.prototype,
2817
2837
  // folder or doctype icon
2818
2838
  role = aria ? " role='img'" : "";
2819
2839
  if ( icon && typeof icon === "string" ) {
2820
- imageSrc = (icon.charAt(0) === "/") ? icon : (opts.imagePath + icon);
2821
- ares.push("<img src='" + imageSrc + "' alt='' />");
2840
+ imageSrc = (icon.charAt(0) === "/") ? icon : ((opts.imagePath || "") + icon);
2841
+ ares.push("<img src='" + imageSrc + "' class='fancytree-icon' alt='' />");
2822
2842
  } else if ( node.data.iconclass ) {
2823
2843
  // TODO: review and test and document
2824
2844
  ares.push("<span " + role + " class='fancytree-custom-icon" + " " + node.data.iconclass + "'></span>");
@@ -2834,7 +2854,6 @@ $.extend(Fancytree.prototype,
2834
2854
  nodeTitle = opts.renderTitle.call(tree, {type: "renderTitle"}, ctx) || "";
2835
2855
  }
2836
2856
  if(!nodeTitle){
2837
- // TODO: escape tooltip string
2838
2857
  tooltip = node.tooltip ? " title='" + FT.escapeHtml(node.tooltip) + "'" : "";
2839
2858
  id = aria ? " id='ftal_" + node.key + "'" : "";
2840
2859
  role = aria ? " role='treeitem'" : "";
@@ -2979,7 +2998,6 @@ $.extend(Fancytree.prototype,
2979
2998
  node = ctx.node,
2980
2999
  tree = ctx.tree,
2981
3000
  opts = ctx.options,
2982
- // userEvent = !!ctx.originalEvent,
2983
3001
  noEvents = (callOpts.noEvents === true),
2984
3002
  isActive = (node === tree.activeNode);
2985
3003
 
@@ -3003,7 +3021,7 @@ $.extend(Fancytree.prototype,
3003
3021
  }
3004
3022
  if(opts.activeVisible){
3005
3023
  // tree.nodeMakeVisible(ctx);
3006
- node.makeVisible();
3024
+ node.makeVisible({scrollIntoView: false}); // nodeSetFocus will scroll
3007
3025
  }
3008
3026
  tree.activeNode = node;
3009
3027
  tree.nodeRenderStatus(ctx);
@@ -3080,9 +3098,9 @@ $.extend(Fancytree.prototype,
3080
3098
  }
3081
3099
  // Trigger expand/collapse after expanding
3082
3100
  dfd.done(function(){
3083
- if( opts.autoScroll && !noAnimation ) {
3101
+ if( flag && opts.autoScroll && !noAnimation ) {
3084
3102
  // Scroll down to last child, but keep current node visible
3085
- node.getLastChild().scrollIntoView(true, node).always(function(){
3103
+ node.getLastChild().scrollIntoView(true, {topNode: node}).always(function(){
3086
3104
  if( !noEvents ) {
3087
3105
  ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
3088
3106
  }
@@ -3093,7 +3111,6 @@ $.extend(Fancytree.prototype,
3093
3111
  }
3094
3112
  }
3095
3113
  });
3096
-
3097
3114
  // vvv Code below is executed after loading finished:
3098
3115
  _afterLoad = function(callback){
3099
3116
  var duration, easing, isVisible, isExpanded;
@@ -3201,7 +3218,7 @@ $.extend(Fancytree.prototype,
3201
3218
  this._callHook("treeSetFocus", ctx, true, true);
3202
3219
  }
3203
3220
  // this.nodeMakeVisible(ctx);
3204
- node.makeVisible();
3221
+ node.makeVisible({scrollIntoView: false});
3205
3222
  tree.focusNode = node;
3206
3223
  // node.debug("FOCUS...");
3207
3224
  // $(node.span).find(".fancytree-title").focus();
@@ -3512,6 +3529,8 @@ $.widget("ui.fancytree",
3512
3529
  keyboard: true,
3513
3530
  keyPathSeparator: "/",
3514
3531
  minExpandLevel: 1,
3532
+ scrollOfs: {top: 0, bottom: 0},
3533
+ scrollParent: null,
3515
3534
  selectMode: 2,
3516
3535
  strings: {
3517
3536
  loading: "Loading&#8230;",
@@ -3772,7 +3791,7 @@ $.extend($.ui.fancytree,
3772
3791
  /** @lends Fancytree_Static# */
3773
3792
  {
3774
3793
  /** @type {string} */
3775
- version: "2.0.0-11", // Set to semver by 'grunt release'
3794
+ version: "2.1.0", // Set to semver by 'grunt release'
3776
3795
  /** @type {string} */
3777
3796
  buildType: "production", // Set to 'production' by 'grunt build'
3778
3797
  /** @type {int} */
@@ -3800,6 +3819,29 @@ $.extend($.ui.fancytree,
3800
3819
  assert: function(cond, msg){
3801
3820
  return _assert(cond, msg);
3802
3821
  },
3822
+ /** Return a function that executes *fn* at most every *timeout* ms.
3823
+ * @param {integer} timeout
3824
+ * @param {function} fn
3825
+ * @param {boolean} [invokeAsap=false]
3826
+ * @param {any} [ctx]
3827
+ */
3828
+ debounce : function(timeout, fn, invokeAsap, ctx) {
3829
+ var timer;
3830
+ if(arguments.length === 3 && typeof invokeAsap !== "boolean") {
3831
+ ctx = invokeAsap;
3832
+ invokeAsap = false;
3833
+ }
3834
+ return function() {
3835
+ var args = arguments;
3836
+ ctx = ctx || this;
3837
+ invokeAsap && !timer && fn.apply(ctx, args);
3838
+ clearTimeout(timer);
3839
+ timer = setTimeout(function() {
3840
+ invokeAsap || fn.apply(ctx, args);
3841
+ timer = null;
3842
+ }, timeout);
3843
+ };
3844
+ },
3803
3845
  /** Write message to console if debugLevel >= 2
3804
3846
  * @param {string} msg
3805
3847
  */
@@ -3850,8 +3892,7 @@ $.extend($.ui.fancytree,
3850
3892
  getEventTarget: function(event){
3851
3893
  var tcn = event && event.target ? event.target.className : "",
3852
3894
  res = {node: this.getNode(event.target), type: undefined};
3853
- // tcn may contains UI themeroller or Font Awesome classes, so we use
3854
- // a fast version of $(res.node).hasClass()
3895
+ // We use a fast version of $(res.node).hasClass()
3855
3896
  // See http://jsperf.com/test-for-classname/2
3856
3897
  if( /\bfancytree-title\b/.test(tcn) ){
3857
3898
  res.type = "title";
@@ -3862,8 +3903,10 @@ $.extend($.ui.fancytree,
3862
3903
  }else if( /\bfancytree-icon\b/.test(tcn) ){
3863
3904
  res.type = "icon";
3864
3905
  }else if( /\bfancytree-node\b/.test(tcn) ){
3865
- // TODO: (http://code.google.com/p/dynatree/issues/detail?id=93)
3866
- // res.type = this._getTypeForOuterNodeEvent(event);
3906
+ // Somewhere near the title
3907
+ res.type = "title";
3908
+ }else if( event && event.target && $(event.target).closest(".fancytree-title").length ) {
3909
+ // #228: clicking an embedded element inside a title
3867
3910
  res.type = "title";
3868
3911
  }
3869
3912
  return res;
@@ -11,8 +11,8 @@
11
11
  * Released under the MIT license
12
12
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
13
13
  *
14
- * @version 2.0.0-11
15
- * @date 2014-04-27T22:28
14
+ * @version 2.1.0
15
+ * @date 2014-05-29T16:44
16
16
  */
17
17
 
18
18
  ;(function($, window, document, undefined) {
@@ -11,8 +11,8 @@
11
11
  * Released under the MIT license
12
12
  * https://github.com/mar10/fancytree/wiki/LicenseInfo
13
13
  *
14
- * @version 2.0.0-11
15
- * @date 2014-04-27T22:28
14
+ * @version 2.1.0
15
+ * @date 2014-05-29T16:44
16
16
  */
17
17
 
18
18
  ;(function($, window, document, undefined) {
@@ -39,15 +39,13 @@ var ACTIVE = "active",
39
39
  /* Recursively load lazy nodes
40
40
  * @param {string} mode 'load', 'expand', false
41
41
  */
42
- function _loadLazyNodes(tree, instData, keyList, mode, dfd) {
42
+ function _loadLazyNodes(tree, local, keyList, mode, dfd) {
43
43
  var i, key, l, node,
44
44
  foundOne = false,
45
45
  deferredList = [],
46
- // lazyNodeList = [],
47
- missingKeyList = []; //keyList.slice(0),
46
+ missingKeyList = [];
48
47
 
49
48
  keyList = keyList || [];
50
- // expand = expand !== false;
51
49
  dfd = dfd || $.Deferred();
52
50
 
53
51
  for( i=0, l=keyList.length; i<l; i++ ) {
@@ -55,7 +53,6 @@ function _loadLazyNodes(tree, instData, keyList, mode, dfd) {
55
53
  node = tree.getNodeByKey(key);
56
54
  if( node ) {
57
55
  if( mode && node.isUndefined() ) {
58
- // lazyNodeList.push(node);
59
56
  foundOne = true;
60
57
  tree.debug("_loadLazyNodes: " + node + " is lazy: loading...");
61
58
  if( mode === "expand" ) {
@@ -66,8 +63,6 @@ function _loadLazyNodes(tree, instData, keyList, mode, dfd) {
66
63
  } else {
67
64
  tree.debug("_loadLazyNodes: " + node + " already loaded.");
68
65
  node.setExpanded();
69
- // node.expanded = true;
70
- // node.render();
71
66
  }
72
67
  } else {
73
68
  missingKeyList.push(key);
@@ -79,13 +74,13 @@ function _loadLazyNodes(tree, instData, keyList, mode, dfd) {
79
74
  // All lazy-expands have finished
80
75
  if( foundOne && missingKeyList.length > 0 ) {
81
76
  // If we read new nodes from server, try to resolve yet-missing keys
82
- _loadLazyNodes(tree, instData, missingKeyList, mode, dfd);
77
+ _loadLazyNodes(tree, local, missingKeyList, mode, dfd);
83
78
  } else {
84
79
  if( missingKeyList.length ) {
85
80
  tree.warn("_loadLazyNodes: could not load those keys: ", missingKeyList);
86
81
  for( i=0, l=missingKeyList.length; i<l; i++ ) {
87
82
  key = keyList[i];
88
- instData._setKey(EXPANDED, keyList[i], false);
83
+ local._appendKey(EXPANDED, keyList[i], false);
89
84
  }
90
85
  }
91
86
  dfd.resolve();
@@ -104,26 +99,21 @@ function _loadLazyNodes(tree, instData, keyList, mode, dfd) {
104
99
  * @requires jquery.fancytree.persist.js
105
100
  */
106
101
  $.ui.fancytree._FancytreeClass.prototype.clearCookies = function(types){
107
- var inst = this.ext.persist,
108
- cookiePrefix = inst.cookiePrefix;
102
+ var local = this.ext.persist,
103
+ prefix = local.cookiePrefix;
109
104
 
110
105
  types = types || "active expanded focus selected";
111
- // TODO: optimize
112
106
  if(types.indexOf(ACTIVE) >= 0){
113
- // $.cookie(cookiePrefix + ACTIVE, null);
114
- $.removeCookie(cookiePrefix + ACTIVE);
107
+ local._data(prefix + ACTIVE, null);
115
108
  }
116
109
  if(types.indexOf(EXPANDED) >= 0){
117
- // $.cookie(cookiePrefix + EXPANDED, null);
118
- $.removeCookie(cookiePrefix + EXPANDED);
110
+ local._data(prefix + EXPANDED, null);
119
111
  }
120
112
  if(types.indexOf(FOCUS) >= 0){
121
- // $.cookie(cookiePrefix + FOCUS, null);
122
- $.removeCookie(cookiePrefix + FOCUS);
113
+ local._data(prefix + FOCUS, null);
123
114
  }
124
115
  if(types.indexOf(SELECTED) >= 0){
125
- // $.cookie(cookiePrefix + SELECTED, null);
126
- $.removeCookie(cookiePrefix + SELECTED);
116
+ local._data(prefix + SELECTED, null);
127
117
  }
128
118
  };
129
119
 
@@ -138,15 +128,15 @@ $.ui.fancytree._FancytreeClass.prototype.clearCookies = function(types){
138
128
  * @requires jquery.fancytree.persist.js
139
129
  */
140
130
  $.ui.fancytree._FancytreeClass.prototype.getPersistData = function(){
141
- var inst = this.ext.persist,
142
- instOpts= this.options.persist,
143
- delim = instOpts.cookieDelimiter,
131
+ var local = this.ext.persist,
132
+ prefix = local.cookiePrefix,
133
+ delim = local.cookieDelimiter,
144
134
  res = {};
145
135
 
146
- res[ACTIVE] = $.cookie(inst.cookiePrefix + ACTIVE);
147
- res[EXPANDED] = ($.cookie(inst.cookiePrefix + EXPANDED) || "").split(delim);
148
- res[SELECTED] = ($.cookie(inst.cookiePrefix + SELECTED) || "").split(delim);
149
- res[FOCUS] = $.cookie(inst.cookiePrefix + FOCUS);
136
+ res[ACTIVE] = local._data(prefix + ACTIVE);
137
+ res[EXPANDED] = (local._data(prefix + EXPANDED) || "").split(delim);
138
+ res[SELECTED] = (local._data(prefix + SELECTED) || "").split(delim);
139
+ res[FOCUS] = local._data(prefix + FOCUS);
150
140
  return res;
151
141
  };
152
142
 
@@ -156,10 +146,9 @@ $.ui.fancytree._FancytreeClass.prototype.getPersistData = function(){
156
146
  */
157
147
  $.ui.fancytree.registerExtension({
158
148
  name: "persist",
159
- version: "0.2.0",
149
+ version: "0.3.0",
160
150
  // Default options for this extension.
161
151
  options: {
162
- // appendRequestInfo: false,
163
152
  cookieDelimiter: "~",
164
153
  cookiePrefix: undefined, // 'fancytree-<treeId>-' by default
165
154
  cookie: {
@@ -169,89 +158,96 @@ $.ui.fancytree.registerExtension({
169
158
  domain: "",
170
159
  secure: false
171
160
  },
172
- expandLazy: false, // true: recursively expand and load lazy nodes
173
- overrideSource: false, // true: cookie takes precedence over `source` data attributes.
161
+ expandLazy: false, // true: recursively expand and load lazy nodes
162
+ overrideSource: false, // true: cookie takes precedence over `source` data attributes.
163
+ store: "auto", // 'cookie': force cookie, 'local': force localStore, 'session': force sessionStore
174
164
  types: "active expanded focus selected"
175
165
  },
176
166
 
167
+ /* Generic read/write string data to cookie, sessionStorage or localStorage. */
168
+ _data: function(key, value){
169
+ var ls = this._local.localStorage; // null, sessionStorage, or localStorage
170
+
171
+ if( value === undefined ) {
172
+ return ls ? ls.getItem(key) : $.cookie(key);
173
+ } else if ( value === null ) {
174
+ if( ls ) {
175
+ ls.removeItem(key);
176
+ } else {
177
+ $.removeCookie(key);
178
+ }
179
+ } else {
180
+ if( ls ) {
181
+ ls.setItem(key, value);
182
+ } else {
183
+ $.cookie(key, value, this.options.persist.cookie);
184
+ }
185
+ }
186
+ },
187
+
177
188
  /* Append `key` to a cookie. */
178
- _setKey: function(type, key, flag){
189
+ _appendKey: function(type, key, flag){
179
190
  key = "" + key; // #90
180
- var instData = this._local,
191
+ var local = this._local,
181
192
  instOpts = this.options.persist,
182
- cookieName = instData.cookiePrefix + type,
183
- cookie = $.cookie(cookieName),
184
- cookieList = cookie ? cookie.split(instOpts.cookieDelimiter) : [],
185
- idx = $.inArray(key, cookieList);
193
+ delim = instOpts.cookieDelimiter,
194
+ cookieName = local.cookiePrefix + type,
195
+ data = local._data(cookieName),
196
+ keyList = data ? data.split(delim) : [],
197
+ idx = $.inArray(key, keyList);
186
198
  // Remove, even if we add a key, so the key is always the last entry
187
199
  if(idx >= 0){
188
- cookieList.splice(idx, 1);
200
+ keyList.splice(idx, 1);
189
201
  }
190
202
  // Append key to cookie
191
203
  if(flag){
192
- cookieList.push(key);
204
+ keyList.push(key);
193
205
  }
194
- $.cookie(cookieName, cookieList.join(instOpts.cookieDelimiter), instOpts.cookie);
206
+ local._data(cookieName, keyList.join(delim));
195
207
  },
196
- // Overide virtual methods for this extension.
197
- // `this` : is this Fancytree object
198
- // `this._super`: the virtual function that was overridden (member of prev. extension or Fancytree)
208
+
199
209
  treeInit: function(ctx){
200
210
  var tree = ctx.tree,
201
211
  opts = ctx.options,
202
- instData = this._local,
212
+ local = this._local,
203
213
  instOpts = this.options.persist;
204
214
 
205
- _assert($.cookie, "Missing required plugin for 'persist' extension: jquery.cookie.js");
215
+ // For 'auto' or 'cookie' mode, the cookie plugin must be available
216
+ _assert(instOpts.store === "localStore" || $.cookie, "Missing required plugin for 'persist' extension: jquery.cookie.js");
206
217
 
207
- instData.cookiePrefix = instOpts.cookiePrefix || ("fancytree-" + tree._id + "-");
208
- instData.storeActive = instOpts.types.indexOf(ACTIVE) >= 0;
209
- instData.storeExpanded = instOpts.types.indexOf(EXPANDED) >= 0;
210
- instData.storeSelected = instOpts.types.indexOf(SELECTED) >= 0;
211
- instData.storeFocus = instOpts.types.indexOf(FOCUS) >= 0;
218
+ local.cookiePrefix = instOpts.cookiePrefix || ("fancytree-" + tree._id + "-");
219
+ local.storeActive = instOpts.types.indexOf(ACTIVE) >= 0;
220
+ local.storeExpanded = instOpts.types.indexOf(EXPANDED) >= 0;
221
+ local.storeSelected = instOpts.types.indexOf(SELECTED) >= 0;
222
+ local.storeFocus = instOpts.types.indexOf(FOCUS) >= 0;
223
+ if( instOpts.store === "cookie" || !window.localStorage ) {
224
+ local.localStorage = null;
225
+ } else {
226
+ local.localStorage = (instOpts.store === "local") ? window.localStorage : window.sessionStorage;
227
+ }
212
228
 
213
229
  // Bind init-handler to apply cookie state
214
230
  tree.$div.bind("fancytreeinit", function(event){
215
231
  var cookie, dfd, i, keyList, node,
216
- prevFocus = $.cookie(instData.cookiePrefix + FOCUS); // record this before node.setActive() overrides it;
232
+ prevFocus = $.cookie(local.cookiePrefix + FOCUS); // record this before node.setActive() overrides it;
217
233
 
218
234
  tree.debug("COOKIE " + document.cookie);
219
235
 
220
- cookie = $.cookie(instData.cookiePrefix + EXPANDED);
236
+ cookie = local._data(local.cookiePrefix + EXPANDED);
221
237
  keyList = cookie && cookie.split(instOpts.cookieDelimiter);
222
238
 
223
- if( instData.storeExpanded ) {
239
+ if( local.storeExpanded ) {
224
240
  // Recursively load nested lazy nodes if expandLazy is 'expand' or 'load'
225
241
  // Also remove expand-cookies for unmatched nodes
226
- dfd = _loadLazyNodes(tree, instData, keyList, instOpts.expandLazy ? "expand" : false , null);
242
+ dfd = _loadLazyNodes(tree, local, keyList, instOpts.expandLazy ? "expand" : false , null);
227
243
  } else {
228
244
  // nothing to do
229
245
  dfd = new $.Deferred().resolve();
230
246
  }
231
247
 
232
248
  dfd.done(function(){
233
- // alert("persistent expand done");
234
- // if(instData.storeExpanded){
235
- // cookie = $.cookie(instData.cookiePrefix + EXPANDED);
236
- // if(cookie){
237
- // keyList = cookie.split(instOpts.cookieDelimiter);
238
- // for(i=0; i<keyList.length; i++){
239
- // node = tree.getNodeByKey(keyList[i]);
240
- // if(node){
241
- // if(node.expanded === undefined || instOpts.overrideSource && (node.expanded === false)){
242
- // // node.setExpanded();
243
- // node.expanded = true;
244
- // node.render();
245
- // }
246
- // }else{
247
- // // node is no longer member of the tree: remove from cookie
248
- // instData._setKey(EXPANDED, keyList[i], false);
249
- // }
250
- // }
251
- // }
252
- // }
253
- if(instData.storeSelected){
254
- cookie = $.cookie(instData.cookiePrefix + SELECTED);
249
+ if(local.storeSelected){
250
+ cookie = local._data(local.cookiePrefix + SELECTED);
255
251
  if(cookie){
256
252
  keyList = cookie.split(instOpts.cookieDelimiter);
257
253
  for(i=0; i<keyList.length; i++){
@@ -264,13 +260,13 @@ $.ui.fancytree.registerExtension({
264
260
  }
265
261
  }else{
266
262
  // node is no longer member of the tree: remove from cookie also
267
- instData._setKey(SELECTED, keyList[i], false);
263
+ local._appendKey(SELECTED, keyList[i], false);
268
264
  }
269
265
  }
270
266
  }
271
267
  }
272
- if(instData.storeActive){
273
- cookie = $.cookie(instData.cookiePrefix + ACTIVE);
268
+ if(local.storeActive){
269
+ cookie = local._data(local.cookiePrefix + ACTIVE);
274
270
  if(cookie && (opts.persist.overrideSource || !tree.activeNode)){
275
271
  node = tree.getNodeByKey(cookie);
276
272
  if(node){
@@ -278,7 +274,7 @@ $.ui.fancytree.registerExtension({
278
274
  }
279
275
  }
280
276
  }
281
- if(instData.storeFocus && prevFocus){
277
+ if(local.storeFocus && prevFocus){
282
278
  node = tree.getNodeByKey(prevFocus);
283
279
  if(node){
284
280
  node.setFocus();
@@ -287,59 +283,57 @@ $.ui.fancytree.registerExtension({
287
283
  });
288
284
  });
289
285
  // Init the tree
290
- this._super(ctx);
286
+ return this._super(ctx);
291
287
  },
292
- // treeDestroy: function(ctx){
293
- // this._super(ctx);
294
- // },
295
288
  nodeSetActive: function(ctx, flag, opts) {
296
- var instData = this._local,
297
- instOpts = this.options.persist;
289
+ var res,
290
+ local = this._local;
298
291
 
299
- flag = flag !== false;
300
- this._super(ctx, flag, opts);
292
+ flag = (flag !== false);
293
+ res = this._super(ctx, flag, opts);
301
294
 
302
- if(instData.storeActive){
303
- $.cookie(instData.cookiePrefix + ACTIVE,
304
- this.activeNode ? this.activeNode.key : null,
305
- instOpts.cookie);
295
+ if(local.storeActive){
296
+ local._data(local.cookiePrefix + ACTIVE, this.activeNode ? this.activeNode.key : null);
306
297
  }
298
+ return res;
307
299
  },
308
300
  nodeSetExpanded: function(ctx, flag, opts) {
309
301
  var res,
310
302
  node = ctx.node,
311
- instData = this._local;
303
+ local = this._local;
312
304
 
313
- flag = flag !== false;
305
+ flag = (flag !== false);
314
306
  res = this._super(ctx, flag, opts);
315
307
 
316
- if(instData.storeExpanded){
317
- instData._setKey(EXPANDED, node.key, flag);
308
+ if(local.storeExpanded){
309
+ local._appendKey(EXPANDED, node.key, flag);
318
310
  }
319
311
  return res;
320
312
  },
321
- nodeSetFocus: function(ctx) {
322
- var instData = this._local,
323
- instOpts = this.options.persist;
313
+ nodeSetFocus: function(ctx, flag) {
314
+ var res,
315
+ local = this._local;
324
316
 
325
- this._super(ctx);
317
+ flag = (flag !== false);
318
+ res = this._super(ctx, flag);
326
319
 
327
- if(instData.storeFocus){
328
- $.cookie(instData.cookiePrefix + FOCUS,
329
- this.focusNode ? this.focusNode.key : null,
330
- instOpts.cookie);
320
+ if(flag && local.storeFocus){
321
+ local._data(local.cookiePrefix + FOCUS, this.focusNode ? this.focusNode.key : null);
331
322
  }
323
+ return res;
332
324
  },
333
325
  nodeSetSelected: function(ctx, flag) {
334
- var node = ctx.node,
335
- instData = this._local;
326
+ var res,
327
+ node = ctx.node,
328
+ local = this._local;
336
329
 
337
- flag = flag !== false;
338
- this._super(ctx, flag);
330
+ flag = (flag !== false);
331
+ res = this._super(ctx, flag);
339
332
 
340
- if(instData.storeSelected){
341
- instData._setKey(SELECTED, node.key, flag);
333
+ if(local.storeSelected){
334
+ local._appendKey(SELECTED, node.key, flag);
342
335
  }
336
+ return res;
343
337
  }
344
338
  });
345
339
  }(jQuery, window, document));