From e1c8ac6560a67319bc7143f79504498819c44507 Mon Sep 17 00:00:00 2001 From: Oleg Vivtash Date: Sat, 25 Oct 2014 00:13:39 +0300 Subject: [PATCH 1/6] FancyTree v2.4.1, skinning works --- .gitignore | 1 + .../jquery-ui.License.txt | 0 CHANGELOG.md | 7 + app/assets/images/fancytree/icons.gif | Bin 5512 -> 0 bytes app/assets/images/fancytree/loading.gif | Bin 3234 -> 0 bytes app/assets/javascripts/fancytree.js | 2 +- .../fancytree/jquery.fancytree-all.js | 6896 +---------------- .../fancytree/jquery.fancytree-core.js | 1 + .../fancytree/jquery.fancytree-custom.min.js | 44 - .../javascripts/fancytree/jquery.fancytree.js | 4106 ---------- .../fancytree/jquery.fancytree.min.js | 14 - .../src/jquery.fancytree.childcounter.js | 4 +- .../fancytree/src/jquery.fancytree.clones.js | 16 +- .../src/jquery.fancytree.columnview.js | 23 +- .../fancytree/src/jquery.fancytree.debug.js | 4 +- .../fancytree/src/jquery.fancytree.dnd.js | 4 +- .../fancytree/src/jquery.fancytree.edit.js | 180 +- .../fancytree/src/jquery.fancytree.filter.js | 4 +- .../fancytree/src/jquery.fancytree.glyph.js | 33 +- .../fancytree/src/jquery.fancytree.gridnav.js | 4 +- .../fancytree/src/jquery.fancytree.js | 170 +- .../fancytree/src/jquery.fancytree.menu.js | 4 +- .../fancytree/src/jquery.fancytree.persist.js | 14 +- .../fancytree/src/jquery.fancytree.table.js | 11 +- .../src/jquery.fancytree.themeroller.js | 4 +- .../fancytree/src/jquery.fancytree.wide.js | 12 +- .../fancytree/src/jsdoc-globals.js | 195 + .../skin-awesome/ui.fancytree.css.erb | 311 - .../skin-awesome/ui.fancytree.min.css.erb | 6 - .../fancytree/skin-awesome/ui.fancytree.scss | 53 + .../skin-bootstrap/ui.fancytree-w.scss | 180 + .../skin-bootstrap/ui.fancytree.css.erb | 391 - .../skin-bootstrap/ui.fancytree.min.css.erb | 6 - .../skin-bootstrap/ui.fancytree.scss | 166 + .../stylesheets/fancytree/skin-common.scss | 618 ++ .../fancytree/skin-lion/ui.fancytree.css.erb | 506 -- .../skin-lion/ui.fancytree.min.css.erb | 6 - .../fancytree/skin-lion/ui.fancytree.scss | 87 + .../skin-themeroller/ui.fancytree.css.erb | 505 -- .../skin-themeroller/ui.fancytree.min.css.erb | 1 - .../fancytree/skin-vista/ui.fancytree.css.erb | 512 -- .../skin-vista/ui.fancytree.min.css.erb | 6 - .../fancytree/skin-vista/ui.fancytree.scss | 105 + .../fancytree/skin-win7/ui.fancytree.css.erb | 576 -- .../skin-win7/ui.fancytree.min.css.erb | 6 - .../fancytree/skin-win7/ui.fancytree.scss | 159 + .../skin-win8-xxl/ui.fancytree.css.erb | 533 -- .../skin-win8-xxl/ui.fancytree.min.css.erb | 11 - .../fancytree/skin-win8-xxl/ui.fancytree.scss | 30 + .../fancytree/skin-win8/ui.fancytree-w.scss | 136 + .../fancytree/skin-win8/ui.fancytree.css.erb | 523 -- .../skin-win8/ui.fancytree.min.css.erb | 6 - .../fancytree/skin-win8/ui.fancytree.scss | 136 + .../fancytree/skin-xp/ui.fancytree.css.erb | 503 -- .../skin-xp/ui.fancytree.min.css.erb | 6 - .../fancytree/skin-xp/ui.fancytree.scss | 89 + app/assets/stylesheets/ui.fancytree.css | 4 +- lib/fancytree/rails/version.rb | 4 +- 58 files changed, 2257 insertions(+), 15677 deletions(-) rename {3th-party => 3rd-party}/jquery-ui.License.txt (100%) delete mode 100644 app/assets/images/fancytree/icons.gif delete mode 100644 app/assets/images/fancytree/loading.gif create mode 100644 app/assets/javascripts/fancytree/jquery.fancytree-core.js delete mode 100644 app/assets/javascripts/fancytree/jquery.fancytree-custom.min.js delete mode 100644 app/assets/javascripts/fancytree/jquery.fancytree.js delete mode 100644 app/assets/javascripts/fancytree/jquery.fancytree.min.js create mode 100644 app/assets/javascripts/fancytree/src/jsdoc-globals.js delete mode 100644 app/assets/stylesheets/fancytree/skin-awesome/ui.fancytree.css.erb delete mode 100644 app/assets/stylesheets/fancytree/skin-awesome/ui.fancytree.min.css.erb create mode 100644 app/assets/stylesheets/fancytree/skin-awesome/ui.fancytree.scss create mode 100644 app/assets/stylesheets/fancytree/skin-bootstrap/ui.fancytree-w.scss delete mode 100644 app/assets/stylesheets/fancytree/skin-bootstrap/ui.fancytree.css.erb delete mode 100644 app/assets/stylesheets/fancytree/skin-bootstrap/ui.fancytree.min.css.erb create mode 100644 app/assets/stylesheets/fancytree/skin-bootstrap/ui.fancytree.scss create mode 100644 app/assets/stylesheets/fancytree/skin-common.scss delete mode 100644 app/assets/stylesheets/fancytree/skin-lion/ui.fancytree.css.erb delete mode 100644 app/assets/stylesheets/fancytree/skin-lion/ui.fancytree.min.css.erb create mode 100644 app/assets/stylesheets/fancytree/skin-lion/ui.fancytree.scss delete mode 100644 app/assets/stylesheets/fancytree/skin-themeroller/ui.fancytree.css.erb delete mode 100644 app/assets/stylesheets/fancytree/skin-themeroller/ui.fancytree.min.css.erb delete mode 100644 app/assets/stylesheets/fancytree/skin-vista/ui.fancytree.css.erb delete mode 100644 app/assets/stylesheets/fancytree/skin-vista/ui.fancytree.min.css.erb create mode 100644 app/assets/stylesheets/fancytree/skin-vista/ui.fancytree.scss delete mode 100644 app/assets/stylesheets/fancytree/skin-win7/ui.fancytree.css.erb delete mode 100644 app/assets/stylesheets/fancytree/skin-win7/ui.fancytree.min.css.erb create mode 100644 app/assets/stylesheets/fancytree/skin-win7/ui.fancytree.scss delete mode 100644 app/assets/stylesheets/fancytree/skin-win8-xxl/ui.fancytree.css.erb delete mode 100644 app/assets/stylesheets/fancytree/skin-win8-xxl/ui.fancytree.min.css.erb create mode 100644 app/assets/stylesheets/fancytree/skin-win8-xxl/ui.fancytree.scss create mode 100644 app/assets/stylesheets/fancytree/skin-win8/ui.fancytree-w.scss delete mode 100644 app/assets/stylesheets/fancytree/skin-win8/ui.fancytree.css.erb delete mode 100644 app/assets/stylesheets/fancytree/skin-win8/ui.fancytree.min.css.erb create mode 100644 app/assets/stylesheets/fancytree/skin-win8/ui.fancytree.scss delete mode 100644 app/assets/stylesheets/fancytree/skin-xp/ui.fancytree.css.erb delete mode 100644 app/assets/stylesheets/fancytree/skin-xp/ui.fancytree.min.css.erb create mode 100644 app/assets/stylesheets/fancytree/skin-xp/ui.fancytree.scss diff --git a/.gitignore b/.gitignore index 1063fcb..fb455ee 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ test/tmp test/version_tmp tmp /tags +.idea diff --git a/3th-party/jquery-ui.License.txt b/3rd-party/jquery-ui.License.txt similarity index 100% rename from 3th-party/jquery-ui.License.txt rename to 3rd-party/jquery-ui.License.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 68caed6..b571a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v2.4.1 + + * use fancytree v. 2.4.1 + * stylesheets converted to SCSS and does not use ERB anymore + * minimized and concatenated file versions removed in favor of asset pipeline + * skins are working + ## v2.3.0 * use fancytree v. 2.3.0 diff --git a/app/assets/images/fancytree/icons.gif b/app/assets/images/fancytree/icons.gif deleted file mode 100644 index 6e237a0f44fbaae7fe406ddc9597918ecf5a997a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5512 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 K9^nZQ5CA(qX4@SA diff --git a/app/assets/images/fancytree/loading.gif b/app/assets/images/fancytree/loading.gif deleted file mode 100644 index ef88497ccc2f6c2a3a0c5e41f3cbfc7a18ff5329..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3234 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 RGR}AoOL=)|H48+m{|ju`J$C>A diff --git a/app/assets/javascripts/fancytree.js b/app/assets/javascripts/fancytree.js index 423a52b..953a021 100644 --- a/app/assets/javascripts/fancytree.js +++ b/app/assets/javascripts/fancytree.js @@ -1,2 +1,2 @@ //= require jquery -//= require fancytree/jquery.fancytree-all.js +//= require fancytree/jquery.fancytree-all diff --git a/app/assets/javascripts/fancytree/jquery.fancytree-all.js b/app/assets/javascripts/fancytree/jquery.fancytree-all.js index 85a1ac4..48b9d68 100644 --- a/app/assets/javascripts/fancytree/jquery.fancytree-all.js +++ b/app/assets/javascripts/fancytree/jquery.fancytree-all.js @@ -1,6882 +1,14 @@ -/*! - * jquery.fancytree.js - * Dynamic tree view control, with support for lazy loading of branches. - * https://github.com/mar10/fancytree/ - * - * Copyright (c) 2006-2014, Martin Wendt (http://wwWendt.de) - * Released under the MIT license - * https://github.com/mar10/fancytree/wiki/LicenseInfo - * - * @version 2.3.0 - * @date 2014-08-17T10:39 - */ - -/** Core Fancytree module. - */ - - -// Start of local namespace -;(function($, window, document, undefined) { -"use strict"; - -// prevent duplicate loading -if ( $.ui.fancytree && $.ui.fancytree.version ) { - $.ui.fancytree.warn("Fancytree: ignored duplicate include"); - return; -} - - -/* ***************************************************************************** - * Private functions and variables - */ - -function _raiseNotImplemented(msg){ - msg = msg || ""; - $.error("Not implemented: " + msg); -} - -function _assert(cond, msg){ - // TODO: see qunit.js extractStacktrace() - if(!cond){ - msg = msg ? ": " + msg : ""; - $.error("Assertion failed" + msg); - } -} - -function consoleApply(method, args){ - var i, s, - fn = window.console ? window.console[method] : null; - - if(fn){ - if(fn.apply){ - fn.apply(window.console, args); - }else{ - // IE? - s = ""; - for( i=0; i t ); - } - } - return true; -} - -/** Return a wrapper that calls sub.methodName() and exposes - * this : tree - * this._local : tree.ext.EXTNAME - * this._super : base.methodName() - */ -function _makeVirtualFunction(methodName, tree, base, extension, extName){ - // $.ui.fancytree.debug("_makeVirtualFunction", methodName, tree, base, extension, extName); - // if(rexTestSuper && !rexTestSuper.test(func)){ - // // extension.methodName() doesn't call _super(), so no wrapper required - // return func; - // } - // Use an immediate function as closure - var proxy = (function(){ - var prevFunc = tree[methodName], // org. tree method or prev. proxy - baseFunc = extension[methodName], // - _local = tree.ext[extName], - _super = function(){ - return prevFunc.apply(tree, arguments); - }; - - // Return the wrapper function - return function(){ - var prevLocal = tree._local, - prevSuper = tree._super; - try{ - tree._local = _local; - tree._super = _super; - return baseFunc.apply(tree, arguments); - }finally{ - tree._local = prevLocal; - tree._super = prevSuper; - } - }; - })(); // end of Immediate Function - return proxy; -} - -/** - * Subclass `base` by creating proxy functions - */ -function _subclassObject(tree, base, extension, extName){ - // $.ui.fancytree.debug("_subclassObject", tree, base, extension, extName); - for(var attrName in extension){ - if(typeof extension[attrName] === "function"){ - if(typeof tree[attrName] === "function"){ - // override existing method - tree[attrName] = _makeVirtualFunction(attrName, tree, base, extension, extName); - }else if(attrName.charAt(0) === "_"){ - // Create private methods in tree.ext.EXTENSION namespace - tree.ext[extName][attrName] = _makeVirtualFunction(attrName, tree, base, extension, extName); - }else{ - $.error("Could not override tree." + attrName + ". Use prefix '_' to create tree." + extName + "._" + attrName); - } - }else{ - // Create member variables in tree.ext.EXTENSION namespace - if(attrName !== "options"){ - tree.ext[extName][attrName] = extension[attrName]; - } - } - } -} - - -function _getResolvedPromise(context, argArray){ - if(context === undefined){ - return $.Deferred(function(){this.resolve();}).promise(); - }else{ - return $.Deferred(function(){this.resolveWith(context, argArray);}).promise(); - } -} - - -function _getRejectedPromise(context, argArray){ - if(context === undefined){ - return $.Deferred(function(){this.reject();}).promise(); - }else{ - return $.Deferred(function(){this.rejectWith(context, argArray);}).promise(); - } -} - - -function _makeResolveFunc(deferred, context){ - return function(){ - deferred.resolveWith(context); - }; -} - - -function _getElementDataAsDict($el){ - // Evaluate 'data-NAME' attributes with special treatment for 'data-json'. - var d = $.extend({}, $el.data()), - json = d.json; - delete d.fancytree; // added to container by widget factory - if( json ) { - delete d.json; - //
  • is already returned as object (http://api.jquery.com/data/#data-html5) - d = $.extend(d, json); - } - return d; -} - - -// TODO: use currying -function _makeNodeTitleMatcher(s){ - s = s.toLowerCase(); - return function(node){ - return node.title.toLowerCase().indexOf(s) >= 0; - }; -} - -var i, - FT = null, // initialized below - ENTITY_MAP = {"&": "&", "<": "<", ">": ">", "\"": """, "'": "'", "/": "/"}, - //boolean attributes that can be set with equivalent class names in the LI tags - CLASS_ATTRS = "active expanded focus folder hideCheckbox lazy selected unselectable".split(" "), - CLASS_ATTR_MAP = {}, - // Top-level Fancytree node attributes, that can be set by dict - NODE_ATTRS = "expanded extraClasses folder hideCheckbox key lazy refKey selected title tooltip unselectable".split(" "), - NODE_ATTR_MAP = {}, - // Attribute names that should NOT be added to node.data - NONE_NODE_DATA_MAP = {"active": true, "children": true, "data": true, "focus": true}; - -for(i=0; i - * For lazy nodes, null or undefined means 'not yet loaded'. Use an empty array - * to define a node that has no children. - * @property {boolean} expanded Use isExpanded(), setExpanded() to access this property. - * @property {string} extraClasses Addtional CSS classes, added to the node's `<span>` - * @property {boolean} folder Folder nodes have different default icons and click behavior.
    - * Note: Also non-folders may have children. - * @property {string} statusNodeType null or type of temporarily generated system node like 'loading', or 'error'. - * @property {boolean} lazy True if this node is loaded on demand, i.e. on first expansion. - * @property {boolean} selected Use isSelected(), setSelected() to access this property. - * @property {string} tooltip Alternative description used as hover banner - */ -function FancytreeNode(parent, obj){ - var i, l, name, cl; - - this.parent = parent; - this.tree = parent.tree; - this.ul = null; - this.li = null; //
  • tag - this.statusNodeType = null; // if this is a temp. node to display the status of its parent - this._isLoading = false; // if this node itself is loading - this._error = null; // {message: '...'} if a load error occured - this.data = {}; - - // TODO: merge this code with node.toDict() - // copy attributes from obj object - for(i=0, l=NODE_ATTRS.length; i= 0, "insertBefore must be an existing child"); - // insert nodeList after children[pos] - this.children.splice.apply(this.children, [pos, 0].concat(nodeList)); - } - if( !this.parent || this.parent.ul || this.tr ){ - // render if the parent was rendered (or this is a root node) - this.render(); - } - if( this.tree.options.selectMode === 3 ){ - this.fixSelection3FromEndNodes(); - } - return firstNode; - }, - /** - * Append or prepend a node, or append a child node. - * - * This a convenience function that calls addChildren() - * - * @param {NodeData} node node definition - * @param {string} [mode=child] 'before', 'after', or 'child' ('over' is a synonym for 'child') - * @returns {FancytreeNode} new node - */ - addNode: function(node, mode){ - if(mode === undefined || mode === "over"){ - mode = "child"; - } - switch(mode){ - case "after": - return this.getParent().addChildren(node, this.getNextSibling()); - case "before": - return this.getParent().addChildren(node, this); - case "child": - case "over": - return this.addChildren(node); - } - _assert(false, "Invalid mode: " + mode); - }, - /** - * Append new node after this. - * - * This a convenience function that calls addNode(node, 'after') - * - * @param {NodeData} node node definition - * @returns {FancytreeNode} new node - */ - appendSibling: function(node){ - return this.addNode(node, "after"); - }, - /** - * Modify existing child nodes. - * - * @param {NodePatch} patch - * @returns {$.Promise} - * @see FancytreeNode#addChildren - */ - applyPatch: function(patch) { - // patch [key, null] means 'remove' - if(patch === null){ - this.remove(); - return _getResolvedPromise(this); - } - // TODO: make sure that root node is not collapsed or modified - // copy (most) attributes to node.ATTR or node.data.ATTR - var name, promise, v, - IGNORE_MAP = { children: true, expanded: true, parent: true }; // TODO: should be global - - for(name in patch){ - v = patch[name]; - if( !IGNORE_MAP[name] && !$.isFunction(v)){ - if(NODE_ATTR_MAP[name]){ - this[name] = v; - }else{ - this.data[name] = v; - } - } - } - // Remove and/or create children - if(patch.hasOwnProperty("children")){ - this.removeChildren(); - if(patch.children){ // only if not null and not empty list - // TODO: addChildren instead? - this._setChildren(patch.children); - } - // TODO: how can we APPEND or INSERT child nodes? - } - if(this.isVisible()){ - this.renderTitle(); - this.renderStatus(); - } - // Expand collapse (final step, since this may be async) - if(patch.hasOwnProperty("expanded")){ - promise = this.setExpanded(patch.expanded); - }else{ - promise = _getResolvedPromise(this); - } - return promise; - }, - /** Collapse all sibling nodes. - * @returns {$.Promise} - */ - collapseSiblings: function() { - return this.tree._callHook("nodeCollapseSiblings", this); - }, - /** Copy this node as sibling or child of `node`. - * - * @param {FancytreeNode} node source node - * @param {string} mode 'before' | 'after' | 'child' - * @param {Function} [map] callback function(NodeData) that could modify the new node - * @returns {FancytreeNode} new - */ - copyTo: function(node, mode, map) { - return node.addNode(this.toDict(true, map), mode); - }, - /** Count direct and indirect children. - * - * @param {boolean} [deep=true] pass 'false' to only count direct children - * @returns {int} number of child nodes - */ - countChildren: function(deep) { - var cl = this.children, i, l, n; - if( !cl ){ - return 0; - } - n = cl.length; - if(deep !== false){ - for(i=0, l=n; i= 2 (prepending node info) - * - * @param {*} msg string or object or array of such - */ - debug: function(msg){ - if( this.tree.options.debugLevel >= 2 ) { - Array.prototype.unshift.call(arguments, this.toString()); - consoleApply("debug", arguments); - } - }, - /** Deprecated. - * @deprecated since 2014-02-16. Use resetLazy() instead. - */ - discard: function(){ - this.warn("FancytreeNode.discard() is deprecated since 2014-02-16. Use .resetLazy() instead."); - return this.resetLazy(); - }, - // TODO: expand(flag) - /**Find all nodes that contain `match` in the title. - * - * @param {string | function(node)} match string to search for, of a function that - * returns `true` if a node is matched. - * @returns {FancytreeNode[]} array of nodes (may be empty) - * @see FancytreeNode#findAll - */ - findAll: function(match) { - match = $.isFunction(match) ? match : _makeNodeTitleMatcher(match); - var res = []; - this.visit(function(n){ - if(match(n)){ - res.push(n); - } - }); - return res; - }, - /**Find first node that contains `match` in the title (not including self). - * - * @param {string | function(node)} match string to search for, of a function that - * returns `true` if a node is matched. - * @returns {FancytreeNode} matching node or null - * @example - * fat text - */ - findFirst: function(match) { - match = $.isFunction(match) ? match : _makeNodeTitleMatcher(match); - var res = null; - this.visit(function(n){ - if(match(n)){ - res = n; - return false; - } - }); - return res; - }, - /* Apply selection state (internal use only) */ - _changeSelectStatusAttrs: function (state) { - var changed = false; - - switch(state){ - case false: - changed = ( this.selected || this.partsel ); - this.selected = false; - this.partsel = false; - break; - case true: - changed = ( !this.selected || !this.partsel ); - this.selected = true; - this.partsel = true; - break; - case undefined: - changed = ( this.selected || !this.partsel ); - this.selected = false; - this.partsel = true; - break; - default: - _assert(false, "invalid state: " + state); - } - // this.debug("fixSelection3AfterLoad() _changeSelectStatusAttrs()", state, changed); - if( changed ){ - this.renderStatus(); - } - return changed; - }, - /** - * Fix selection status, after this node was (de)selected in multi-hier mode. - * This includes (de)selecting all children. - */ - fixSelection3AfterClick: function() { - var flag = this.isSelected(); - -// this.debug("fixSelection3AfterClick()"); - - this.visit(function(node){ - node._changeSelectStatusAttrs(flag); - }); - this.fixSelection3FromEndNodes(); - }, - /** - * Fix selection status for multi-hier mode. - * Only end-nodes are considered to update the descendants branch and parents. - * Should be called after this node has loaded new children or after - * children have been modified using the API. - */ - fixSelection3FromEndNodes: function() { -// this.debug("fixSelection3FromEndNodes()"); - _assert(this.tree.options.selectMode === 3, "expected selectMode 3"); - - // Visit all end nodes and adjust their parent's `selected` and `partsel` - // attributes. Return selection state true, false, or undefined. - function _walk(node){ - var i, l, child, s, state, allSelected,someSelected, - children = node.children; - - if( children && children.length ){ - // check all children recursively - allSelected = true; - someSelected = false; - - for( i=0, l=children.length; i= 1 (prepending node info) - * - * @param {*} msg string or object or array of such - */ - info: function(msg){ - if( this.tree.options.debugLevel >= 1 ) { - Array.prototype.unshift.call(arguments, this.toString()); - consoleApply("info", arguments); - } - }, - /** Return true if node is active (see also FancytreeNode#isSelected). - * @returns {boolean} - */ - isActive: function() { - return (this.tree.activeNode === this); - }, - /** Return true if node is a direct child of otherNode. - * @param {FancytreeNode} otherNode - * @returns {boolean} - */ - isChildOf: function(otherNode) { - return (this.parent && this.parent === otherNode); - }, - /** Return true, if node is a direct or indirect sub node of otherNode. - * @param {FancytreeNode} otherNode - * @returns {boolean} - */ - isDescendantOf: function(otherNode) { - if(!otherNode || otherNode.tree !== this.tree){ - return false; - } - var p = this.parent; - while( p ) { - if( p === otherNode ){ - return true; - } - p = p.parent; - } - return false; - }, - /** Return true if node is expanded. - * @returns {boolean} - */ - isExpanded: function() { - return !!this.expanded; - }, - /** Return true if node is the first node of its parent's children. - * @returns {boolean} - */ - isFirstSibling: function() { - var p = this.parent; - return !p || p.children[0] === this; - }, - /** Return true if node is a folder, i.e. has the node.folder attribute set. - * @returns {boolean} - */ - isFolder: function() { - return !!this.folder; - }, - /** Return true if node is the last node of its parent's children. - * @returns {boolean} - */ - isLastSibling: function() { - var p = this.parent; - return !p || p.children[p.children.length-1] === this; - }, - /** Return true if node is lazy (even if data was already loaded) - * @returns {boolean} - */ - isLazy: function() { - return !!this.lazy; - }, - /** Return true if node is lazy and loaded. For non-lazy nodes always return true. - * @returns {boolean} - */ - isLoaded: function() { - return !this.lazy || this.hasChildren() !== undefined; // Also checks if the only child is a status node - }, - /** Return true if children are currently beeing loaded, i.e. a Ajax request is pending. - * @returns {boolean} - */ - isLoading: function() { - return !!this._isLoading; - }, - /** Return true if this is the (invisible) system root node. - * @returns {boolean} - */ - isRoot: function() { - return (this.tree.rootNode === this); - }, - /** Return true if node is selected, i.e. has a checkmark set (see also FancytreeNode#isActive). - * @returns {boolean} - */ - isSelected: function() { - return !!this.selected; - }, - /** Return true if this node is a temporarily generated system node like - * 'loading', or 'error' (node.statusNodeType contains the type). - * @returns {boolean} - */ - isStatusNode: function() { - return !!this.statusNodeType; - }, - /** Return true if node is lazy and not yet loaded. For non-lazy nodes always return false. - * @returns {boolean} - */ - isUndefined: function() { - return this.hasChildren() === undefined; // also checks if the only child is a status node - }, - /** Return true if all parent nodes are expanded. Note: this does not check - * whether the node is scrolled into the visible part of the screen. - * @returns {boolean} - */ - isVisible: function() { - var i, l, - parents = this.getParentList(false, false); - - for(i=0, l=parents.length; i= 0; i--){ - // that.debug("pushexpand" + parents[i]); - deferreds.push(parents[i].setExpanded(true, opts)); - } - $.when.apply($, deferreds).done(function(){ - // All expands have finished - // that.debug("expand DONE", scroll); - if( scroll ){ - that.scrollIntoView(effects).done(function(){ - // that.debug("scroll DONE"); - dfd.resolve(); - }); - } else { - dfd.resolve(); - } - }); - return dfd.promise(); - }, - /** Move this node to targetNode. - * @param {FancytreeNode} targetNode - * @param {string} mode
    -	 *      'child': append this node as last child of targetNode.
    -	 *               This is the default. To be compatble with the D'n'd
    -	 *               hitMode, we also accept 'over'.
    -	 *      'before': add this node as sibling before targetNode.
    -	 *      'after': add this node as sibling after targetNode.
    - * @param {function} [map] optional callback(FancytreeNode) to allow modifcations - */ - moveTo: function(targetNode, mode, map) { - if(mode === undefined || mode === "over"){ - mode = "child"; - } - var pos, - prevParent = this.parent, - targetParent = (mode === "child") ? targetNode : targetNode.parent; - - if(this === targetNode){ - return; - }else if( !this.parent ){ - throw "Cannot move system root"; - }else if( targetParent.isDescendantOf(this) ){ - throw "Cannot move a node to its own descendant"; - } - // Unlink this node from current parent - if( this.parent.children.length === 1 ) { - if( this.parent === targetParent ){ - return; // #258 - } - this.parent.children = this.parent.lazy ? [] : null; - this.parent.expanded = false; - } else { - pos = $.inArray(this, this.parent.children); - _assert(pos >= 0); - this.parent.children.splice(pos, 1); - } - // Remove from source DOM parent -// if(this.parent.ul){ -// this.parent.ul.removeChild(this.li); -// } - - // Insert this node to target parent's child list - this.parent = targetParent; - if( targetParent.hasChildren() ) { - switch(mode) { - case "child": - // Append to existing target children - targetParent.children.push(this); - break; - case "before": - // Insert this node before target node - pos = $.inArray(targetNode, targetParent.children); - _assert(pos >= 0); - targetParent.children.splice(pos, 0, this); - break; - case "after": - // Insert this node after target node - pos = $.inArray(targetNode, targetParent.children); - _assert(pos >= 0); - targetParent.children.splice(pos+1, 0, this); - break; - default: - throw "Invalid mode " + mode; - } - } else { - targetParent.children = [ this ]; - } - // Parent has no
      tag yet: -// if( !targetParent.ul ) { -// // This is the parent's first child: create UL tag -// // (Hidden, because it will be -// targetParent.ul = document.createElement("ul"); -// targetParent.ul.style.display = "none"; -// targetParent.li.appendChild(targetParent.ul); -// } -// // Issue 319: Add to target DOM parent (only if node was already rendered(expanded)) -// if(this.li){ -// targetParent.ul.appendChild(this.li); -// }^ - - // Let caller modify the nodes - if( map ){ - targetNode.visit(map, true); - } - // Handle cross-tree moves - if( this.tree !== targetNode.tree ) { - // Fix node.tree for all source nodes -// _assert(false, "Cross-tree move is not yet implemented."); - this.warn("Cross-tree moveTo is experimantal!"); - this.visit(function(n){ - // TODO: fix selection state and activation, ... - n.tree = targetNode.tree; - }, true); - } - - // A collaposed node won't re-render children, so we have to remove it manually - // if( !targetParent.expanded ){ - // prevParent.ul.removeChild(this.li); - // } - - // Update HTML markup - if( !prevParent.isDescendantOf(targetParent)) { - prevParent.render(); - } - if( !targetParent.isDescendantOf(prevParent) && targetParent !== prevParent) { - targetParent.render(); - } - // TODO: fix selection state - // TODO: fix active state - -/* - var tree = this.tree; - var opts = tree.options; - var pers = tree.persistence; - - - // Always expand, if it's below minExpandLevel -// tree.logDebug ("%s._addChildNode(%o), l=%o", this, ftnode, ftnode.getLevel()); - if ( opts.minExpandLevel >= ftnode.getLevel() ) { -// tree.logDebug ("Force expand for %o", ftnode); - this.bExpanded = true; - } - - // In multi-hier mode, update the parents selection state - // DT issue #82: only if not initializing, because the children may not exist yet -// if( !ftnode.data.isStatusNode() && opts.selectMode==3 && !isInitializing ) -// ftnode._fixSelectionState(); - - // In multi-hier mode, update the parents selection state - if( ftnode.bSelected && opts.selectMode==3 ) { - var p = this; - while( p ) { - if( !p.hasSubSel ) - p._setSubSel(true); - p = p.parent; - } - } - // render this node and the new child - if ( tree.bEnableUpdate ) - this.render(); - - return ftnode; - -*/ - }, - /** Set focus relative to this node and optionally activate. - * - * @param {number} where The keyCode that would normally trigger this move, - * e.g. `$.ui.keyCode.LEFT` would collapse the node if it - * is expanded or move to the parent oterwise. - * @param {boolean} [activate=true] - * @returns {$.Promise} - */ - navigate: function(where, activate) { - var i, parents, - handled = true, - KC = $.ui.keyCode, - sib = null; - - // Navigate to node - function _goto(n){ - if( n ){ - try { n.makeVisible(); } catch(e) {} // #272 - // Node may still be hidden by a filter - if( ! $(n.span).is(":visible") ) { - n.debug("Navigate: skipping hidden node"); - n.navigate(where, activate); - return; - } - return activate === false ? n.setFocus() : n.setActive(); - } - } - - switch( where ) { - case KC.BACKSPACE: - if( this.parent && this.parent.parent ) { - _goto(this.parent); - } - break; - case KC.LEFT: - if( this.expanded ) { - this.setExpanded(false); - _goto(this); - } else if( this.parent && this.parent.parent ) { - _goto(this.parent); - } - break; - case KC.RIGHT: - if( !this.expanded && (this.children || this.lazy) ) { - this.setExpanded(); - _goto(this); - } else if( this.children && this.children.length ) { - _goto(this.children[0]); - } - break; - case KC.UP: - sib = this.getPrevSibling(); - while( sib && sib.expanded && sib.children && sib.children.length ){ - sib = sib.children[sib.children.length - 1]; - } - if( !sib && this.parent && this.parent.parent ){ - sib = this.parent; - } - _goto(sib); - break; - case KC.DOWN: - if( this.expanded && this.children && this.children.length ) { - sib = this.children[0]; - } else { - parents = this.getParentList(false, true); - for(i=parents.length-1; i>=0; i--) { - sib = parents[i].getNextSibling(); - if( sib ){ break; } - } - } - _goto(sib); - break; - default: - handled = false; - } - }, - /** - * Remove this node (not allowed for system root). - */ - remove: function() { - return this.parent.removeChild(this); - }, - /** - * Remove childNode from list of direct children. - * @param {FancytreeNode} childNode - */ - removeChild: function(childNode) { - return this.tree._callHook("nodeRemoveChild", this, childNode); - }, - /** - * Remove all child nodes and descendents. This converts the node into a leaf.
      - * If this was a lazy node, it is still considered 'loaded'; call node.resetLazy() - * in order to trigger lazyLoad on next expand. - */ - removeChildren: function() { - return this.tree._callHook("nodeRemoveChildren", this); - }, - /** - * This method renders and updates all HTML markup that is required - * to display this node in its current state.
      - * Note: - *
        - *
      • It should only be neccessary to call this method after the node object - * was modified by direct access to its properties, because the common - * API methods (node.setTitle(), moveTo(), addChildren(), remove(), ...) - * already handle this. - *
      • {@link FancytreeNode#renderTitle} and {@link FancytreeNode#renderStatus} - * are implied. If changes are more local, calling only renderTitle() or - * renderStatus() may be sufficient and faster. - *
      • If a node was created/removed, node.render() must be called on the parent. - *
      - * - * @param {boolean} [force=false] re-render, even if html markup was already created - * @param {boolean} [deep=false] also render all descendants, even if parent is collapsed - */ - render: function(force, deep) { - return this.tree._callHook("nodeRender", this, force, deep); - }, - /** Create HTML markup for the node's outer (expander, checkbox, icon, and title). - * @see Fancytree_Hooks#nodeRenderTitle - */ - renderTitle: function() { - return this.tree._callHook("nodeRenderTitle", this); - }, - /** Update element's CSS classes according to node state. - * @see Fancytree_Hooks#nodeRenderStatus - */ - renderStatus: function() { - return this.tree._callHook("nodeRenderStatus", this); - }, - /** - * Remove all children, collapse, and set the lazy-flag, so that the lazyLoad - * event is triggered on next expand. - */ - resetLazy: function() { - this.removeChildren(); - this.expanded = false; - this.lazy = true; - this.children = undefined; - this.renderStatus(); - }, - /** Schedule activity for delayed execution (cancel any pending request). - * scheduleAction('cancel') will only cancel a pending request (if any). - * @param {string} mode - * @param {number} ms - */ - scheduleAction: function(mode, ms) { - if( this.tree.timer ) { - clearTimeout(this.tree.timer); -// this.tree.debug("clearTimeout(%o)", this.tree.timer); - } - this.tree.timer = null; - var self = this; // required for closures - switch (mode) { - case "cancel": - // Simply made sure that timer was cleared - break; - case "expand": - this.tree.timer = setTimeout(function(){ - self.tree.debug("setTimeout: trigger expand"); - self.setExpanded(true); - }, ms); - break; - case "activate": - this.tree.timer = setTimeout(function(){ - self.tree.debug("setTimeout: trigger activate"); - self.setActive(true); - }, ms); - break; - default: - throw "Invalid mode " + mode; - } -// this.tree.debug("setTimeout(%s, %s): %s", mode, ms, this.tree.timer); - }, - /** - * - * @param {boolean | PlainObject} [effects=false] animation options. - * @param {object} [options=null] {topNode: null, effects: ..., parent: ...} this node will remain visible in - * any case, even if `this` is outside the scroll pane. - * @returns {$.Promise} - */ - scrollIntoView: function(effects, options) { - if( options !== undefined && _isNode(options) ) { - this.warn("scrollIntoView() with 'topNode' option is deprecated since 2014-05-08. Use 'options.topNode' instead."); - options = {topNode: options}; - } - // this.$scrollParent = (this.options.scrollParent === "auto") ? $ul.scrollParent() : $(this.options.scrollParent); - // this.$scrollParent = this.$scrollParent.length ? this.$scrollParent || this.$container; - - var topNodeY, nodeY, horzScrollbarHeight, containerOffsetTop, - opts = $.extend({ - effects: (effects === true) ? {duration: 200, queue: false} : effects, - scrollOfs: this.tree.options.scrollOfs, - scrollParent: this.tree.options.scrollParent || this.tree.$container, - topNode: null - }, options), - dfd = new $.Deferred(), - that = this, - nodeHeight = $(this.span).height(), - $container = $(opts.scrollParent), - topOfs = opts.scrollOfs.top || 0, - bottomOfs = opts.scrollOfs.bottom || 0, - containerHeight = $container.height(),// - topOfs - bottomOfs, - scrollTop = $container.scrollTop(), - $animateTarget = $container, - isParentWindow = $container[0] === window, - topNode = opts.topNode || null, - newScrollTop = null; - - // this.debug("scrollIntoView(), scrollTop=", scrollTop, opts.scrollOfs); - _assert($(this.span).is(":visible"), "scrollIntoView node is invisible"); // otherwise we cannot calc offsets - - if( isParentWindow ) { - nodeY = $(this.span).offset().top; - topNodeY = topNode ? $(topNode.span).offset().top : 0; - $animateTarget = $("html,body"); - - } else { - _assert($container[0] !== document && $container[0] !== document.body, "scrollParent should be an simple element or `window`, not document or body."); - - containerOffsetTop = $container.offset().top, - nodeY = $(this.span).offset().top - containerOffsetTop + scrollTop; // relative to scroll parent - topNodeY = topNode ? $(topNode.span).offset().top - containerOffsetTop + scrollTop : 0; - horzScrollbarHeight = Math.max(0, ($container.innerHeight() - $container[0].clientHeight)); - containerHeight -= horzScrollbarHeight; - } - - // this.debug(" scrollIntoView(), nodeY=", nodeY, "containerHeight=", containerHeight); - if( nodeY < (scrollTop + topOfs) ){ - // Node is above visible container area - newScrollTop = nodeY - topOfs; - // this.debug(" scrollIntoView(), UPPER newScrollTop=", newScrollTop); - - }else if((nodeY + nodeHeight) > (scrollTop + containerHeight - bottomOfs)){ - newScrollTop = nodeY + nodeHeight - containerHeight + bottomOfs; - // this.debug(" scrollIntoView(), LOWER newScrollTop=", newScrollTop); - // If a topNode was passed, make sure that it is never scrolled - // outside the upper border - if(topNode){ - _assert($(topNode.span).is(":visible")); - if( topNodeY < newScrollTop ){ - newScrollTop = topNodeY - topOfs; - // this.debug(" scrollIntoView(), TOP newScrollTop=", newScrollTop); - } - } - } - - if(newScrollTop !== null){ - // this.debug(" scrollIntoView(), SET newScrollTop=", newScrollTop); - if(opts.effects){ - opts.effects.complete = function(){ - dfd.resolveWith(that); - }; - $animateTarget.stop(true).animate({ - scrollTop: newScrollTop - }, opts.effects); - }else{ - $animateTarget[0].scrollTop = newScrollTop; - dfd.resolveWith(this); - } - }else{ - dfd.resolveWith(this); - } - return dfd.promise(); - }, - - /**Activate this node. - * @param {boolean} [flag=true] pass false to deactivate - * @param {object} [opts] additional options. Defaults to {noEvents: false} - */ - setActive: function(flag, opts){ - return this.tree._callHook("nodeSetActive", this, flag, opts); - }, - /**Expand or collapse this node. Promise is resolved, when lazy loading and animations are done. - * @param {boolean} [flag=true] pass false to collapse - * @param {object} [opts] additional options. Defaults to {noAnimation: false, noEvents: false} - * @returns {$.Promise} - */ - setExpanded: function(flag, opts){ - return this.tree._callHook("nodeSetExpanded", this, flag, opts); - }, - /**Set keyboard focus to this node. - * @param {boolean} [flag=true] pass false to blur - * @see Fancytree#setFocus - */ - setFocus: function(flag){ - return this.tree._callHook("nodeSetFocus", this, flag); - }, - /**Select this node, i.e. check the checkbox. - * @param {boolean} [flag=true] pass false to deselect - */ - setSelected: function(flag){ - return this.tree._callHook("nodeSetSelected", this, flag); - }, - /**Mark a lazy node as 'error', 'loading', or 'ok'. - * @param {string} status 'error', 'ok' - * @param {string} [message] - * @param {string} [details] - */ - setStatus: function(status, message, details){ - return this.tree._callHook("nodeSetStatus", this, status, message, details); - }, - /**Rename this node. - * @param {string} title - */ - setTitle: function(title){ - this.title = title; - this.renderTitle(); - }, - /**Sort child list by title. - * @param {function} [cmp] custom compare function(a, b) that returns -1, 0, or 1 (defaults to sort by title). - * @param {boolean} [deep=false] pass true to sort all descendant nodes - */ - sortChildren: function(cmp, deep) { - var i,l, - cl = this.children; - - if( !cl ){ - return; - } - cmp = cmp || function(a, b) { - var x = a.title.toLowerCase(), - y = b.title.toLowerCase(); - return x === y ? 0 : x > y ? 1 : -1; - }; - cl.sort(cmp); - if( deep ){ - for(i=0, l=cl.length; i"; - }, - /** Call fn(node) for all child nodes.
      - * Stop iteration, if fn() returns false. Skip current branch, if fn() returns "skip".
      - * Return false if iteration was stopped. - * - * @param {function} fn the callback function. - * Return false to stop iteration, return "skip" to skip this node and children only. - * @param {boolean} [includeSelf=false] - * @returns {boolean} - */ - visit: function(fn, includeSelf) { - var i, l, - res = true, - children = this.children; - - if( includeSelf === true ) { - res = fn(this); - if( res === false || res === "skip" ){ - return res; - } - } - if(children){ - for(i=0, l=children.length; i - * Stop iteration, if fn() returns false.
      - * Return false if iteration was stopped. - * - * @param {function} fn the callback function. - * Return false to stop iteration, return "skip" to skip this node and children only. - * @param {boolean} [includeSelf=false] - * @returns {boolean} - */ - visitParents: function(fn, includeSelf) { - // Visit parent nodes (bottom up) - if(includeSelf && fn(this) === false){ - return false; - } - var p = this.parent; - while( p ) { - if(fn(p) === false){ - return false; - } - p = p.parent; - } - return true; - }, - /** Write warning to browser console (prepending node info) - * - * @param {*} msg string or object or array of such - */ - warn: function(msg){ - Array.prototype.unshift.call(arguments, this.toString()); - consoleApply("warn", arguments); - } -}; - - -/* ***************************************************************************** - * Fancytree - */ -/** - * Construct a new tree object. - * - * @class Fancytree - * @classdesc The controller behind a fancytree. - * This class also contains 'hook methods': see {@link Fancytree_Hooks}. - * - * @param {Widget} widget - * - * @property {FancytreeOptions} options - * @property {FancytreeNode} rootNode - * @property {FancytreeNode} activeNode - * @property {FancytreeNode} focusNode - * @property {jQueryObject} $div - * @property {object} widget - * @property {object} ext - * @property {object} data - * @property {object} options - * @property {string} _id - * @property {string} statusClassPropName - * @property {string} ariaPropName - * @property {string} nodeContainerAttrName - * @property {string} $container - * @property {FancytreeNode} lastSelectedNode - */ -function Fancytree(widget) { - this.widget = widget; - this.$div = widget.element; - this.options = widget.options; - if( this.options && $.isFunction(this.options.lazyload) ) { - if( ! $.isFunction(this.options.lazyLoad ) ) { - this.options.lazyLoad = function() { - FT.warn("The 'lazyload' event is deprecated since 2014-02-25. Use 'lazyLoad' (with uppercase L) instead."); - widget.options.lazyload.apply(this, arguments); - }; - } - } - if( this.options && $.isFunction(this.options.loaderror) ) { - $.error("The 'loaderror' event was renamed since 2014-07-03. Use 'loadError' (with uppercase E) instead."); - } - this.ext = {}; // Active extension instances - // allow to init tree.data.foo from
      - this.data = _getElementDataAsDict(this.$div); - this._id = $.ui.fancytree._nextId++; - this._ns = ".fancytree-" + this._id; // append for namespaced events - this.activeNode = null; - this.focusNode = null; - this._hasFocus = null; - this.lastSelectedNode = null; - this.systemFocusElement = null; - - this.statusClassPropName = "span"; - this.ariaPropName = "li"; - this.nodeContainerAttrName = "li"; - - // Remove previous markup if any - this.$div.find(">ul.fancytree-container").remove(); - - // Create a node without parent. - var fakeParent = { tree: this }, - $ul; - this.rootNode = new FancytreeNode(fakeParent, { - title: "root", - key: "root_" + this._id, - children: null, - expanded: true - }); - this.rootNode.parent = null; - - // Create root markup - $ul = $("
        ", { - "class": "ui-fancytree fancytree-container" - }).appendTo(this.$div); - this.$container = $ul; - this.rootNode.ul = $ul[0]; - - if(this.options.debugLevel == null){ - this.options.debugLevel = FT.debugLevel; - } - // Add container to the TAB chain - // See http://www.w3.org/TR/wai-aria-practices/#focus_activedescendant - this.$container.attr("tabindex", this.options.tabbable ? "0" : "-1"); - if(this.options.aria){ - this.$container - .attr("role", "tree") - .attr("aria-multiselectable", true); - } -} - - -Fancytree.prototype = /** @lends Fancytree# */{ - /* Return a context object that can be re-used for _callHook(). - * @param {Fancytree | FancytreeNode | EventData} obj - * @param {Event} originalEvent - * @param {Object} extra - * @returns {EventData} - */ - _makeHookContext: function(obj, originalEvent, extra) { - var ctx, tree; - if(obj.node !== undefined){ - // obj is already a context object - if(originalEvent && obj.originalEvent !== originalEvent){ - $.error("invalid args"); - } - ctx = obj; - }else if(obj.tree){ - // obj is a FancytreeNode - tree = obj.tree; - ctx = { node: obj, tree: tree, widget: tree.widget, options: tree.widget.options, originalEvent: originalEvent }; - }else if(obj.widget){ - // obj is a Fancytree - ctx = { node: null, tree: obj, widget: obj.widget, options: obj.widget.options, originalEvent: originalEvent }; - }else{ - $.error("invalid args"); - } - if(extra){ - $.extend(ctx, extra); - } - return ctx; - }, - /* Trigger a hook function: funcName(ctx, [...]). - * - * @param {string} funcName - * @param {Fancytree|FancytreeNode|EventData} contextObject - * @param {any} [_extraArgs] optional additional arguments - * @returns {any} - */ - _callHook: function(funcName, contextObject, _extraArgs) { - var ctx = this._makeHookContext(contextObject), - fn = this[funcName], - args = Array.prototype.slice.call(arguments, 2); - if(!$.isFunction(fn)){ - $.error("_callHook('" + funcName + "') is not a function"); - } - args.unshift(ctx); -// this.debug("_hook", funcName, ctx.node && ctx.node.toString() || ctx.tree.toString(), args); - return fn.apply(this, args); - }, - /* Check if current extensions dependencies are met and throw an error if not. - * - * This method may be called inside the `treeInit` hook for custom extensions. - * - * @param {string} extension name of the required extension - * @param {boolean} [required=true] pass `false` if the extension is optional, but we want to check for order if it is present - * @param {boolean} [before] `true` if `name` must be included before this, `false` otherwise (use `null` if order doesn't matter) - * @param {string} [message] optional error message (defaults to a descriptve error message) - */ - _requireExtension: function(name, required, before, message) { - before = !!before; - var thisName = this._local.name, - extList = this.options.extensions, - isBefore = $.inArray(name, extList) < $.inArray(thisName, extList), - isMissing = required && this.ext[name] == null, - badOrder = !isMissing && before != null && (before !== isBefore); - - _assert(thisName && thisName !== name); - - if( isMissing || badOrder ){ - if( !message ){ - if( isMissing || required ){ - message = "'" + thisName + "' extension requires '" + name + "'"; - if( badOrder ){ - message += " to be registered " + (before ? "before" : "after") + " itself"; - } - }else{ - message = "If used together, `" + name + "` must be registered " + (before ? "before" : "after") + " `" + thisName + "`"; - } - } - $.error(message); - return false; - } - return true; - }, - /** Activate node with a given key and fire focus and activate events. - * - * A prevously activated node will be deactivated. - * If activeVisible option is set, all parents will be expanded as necessary. - * Pass key = false, to deactivate the current node only. - * @param {string} key - * @returns {FancytreeNode} activated node (null, if not found) - */ - activateKey: function(key) { - var node = this.getNodeByKey(key); - if(node){ - node.setActive(); - }else if(this.activeNode){ - this.activeNode.setActive(false); - } - return node; - }, - /** (experimental) - * - * @param {Array} patchList array of [key, NodePatch] arrays - * @returns {$.Promise} resolved, when all patches have been applied - * @see TreePatch - */ - applyPatch: function(patchList) { - var dfd, i, p2, key, patch, node, - patchCount = patchList.length, - deferredList = []; - - for(i=0; i= 2 (prepending tree name) - * - * @param {*} msg string or object or array of such - */ - debug: function(msg){ - if( this.options.debugLevel >= 2 ) { - Array.prototype.unshift.call(arguments, this.toString()); - consoleApply("debug", arguments); - } - }, - // TODO: disable() - // TODO: enable() - // TODO: enableUpdate() - // TODO: fromDict - /** - * Generate INPUT elements that can be submitted with html forms. - * - * In selectMode 3 only the topmost selected nodes are considered. - * - * @param {boolean | string} [selected=true] - * @param {boolean | string} [active=true] - */ - generateFormElements: function(selected, active) { - // TODO: test case - var nodeList, - selectedName = (selected !== false) ? "ft_" + this._id + "[]" : selected, - activeName = (active !== false) ? "ft_" + this._id + "_active" : active, - id = "fancytree_result_" + this._id, - $result = $("#" + id); - - if($result.length){ - $result.empty(); - }else{ - $result = $("
        ", { - id: id - }).hide().insertAfter(this.$container); - } - if(selectedName){ - nodeList = this.getSelectedNodes( this.options.selectMode === 3 ); - $.each(nodeList, function(idx, node){ - $result.append($("", { - type: "checkbox", - name: selectedName, - value: node.key, - checked: true - })); - }); - } - if(activeName && this.activeNode){ - $result.append($("", { - type: "radio", - name: activeName, - value: this.activeNode.key, - checked: true - })); - } - }, - /** - * Return the currently active node or null. - * @returns {FancytreeNode} - */ - getActiveNode: function() { - return this.activeNode; - }, - /** Return the first top level node if any (not the invisible root node). - * @returns {FancytreeNode | null} - */ - getFirstChild: function() { - return this.rootNode.getFirstChild(); - }, - /** - * Return node that has keyboard focus. - * @param {boolean} [ifTreeHasFocus=false] (not yet implemented) - * @returns {FancytreeNode} - */ - getFocusNode: function(ifTreeHasFocus) { - // TODO: implement ifTreeHasFocus - return this.focusNode; - }, - /** - * Return node with a given key or null if not found. - * @param {string} key - * @param {FancytreeNode} [searchRoot] only search below this node - * @returns {FancytreeNode | null} - */ - getNodeByKey: function(key, searchRoot) { - // Search the DOM by element ID (assuming this is faster than traversing all nodes). - // $("#...") has problems, if the key contains '.', so we use getElementById() - var el, match; - if(!searchRoot){ - el = document.getElementById(this.options.idPrefix + key); - if( el ){ - return el.ftnode ? el.ftnode : null; - } - } - // Not found in the DOM, but still may be in an unrendered part of tree - // TODO: optimize with specialized loop - // TODO: consider keyMap? - searchRoot = searchRoot || this.rootNode; - match = null; - searchRoot.visit(function(node){ -// window.console.log("getNodeByKey(" + key + "): ", node.key); - if(node.key === key) { - match = node; - return false; - } - }, true); - return match; - }, - /** Return the invisible system root node. - * @returns {FancytreeNode} - */ - getRootNode: function() { - return this.rootNode; - }, - /** - * Return an array of selected nodes. - * @param {boolean} [stopOnParents=false] only return the topmost selected - * node (useful with selectMode 3) - * @returns {FancytreeNode[]} - */ - getSelectedNodes: function(stopOnParents) { - var nodeList = []; - this.rootNode.visit(function(node){ - if( node.selected ) { - nodeList.push(node); - if( stopOnParents === true ){ - return "skip"; // stop processing this branch - } - } - }); - return nodeList; - }, - /** Return true if the tree control has keyboard focus - * @returns {boolean} - */ - hasFocus: function(){ - return !!this._hasFocus; - }, - /** Write to browser console if debugLevel >= 1 (prepending tree name) - * @param {*} msg string or object or array of such - */ - info: function(msg){ - if( this.options.debugLevel >= 1 ) { - Array.prototype.unshift.call(arguments, this.toString()); - consoleApply("info", arguments); - } - }, -/* - TODO: isInitializing: function() { - return ( this.phase=="init" || this.phase=="postInit" ); - }, - TODO: isReloading: function() { - return ( this.phase=="init" || this.phase=="postInit" ) && this.options.persist && this.persistence.cookiesFound; - }, - TODO: isUserEvent: function() { - return ( this.phase=="userEvent" ); - }, -*/ - - /** - * Make sure that a node with a given ID is loaded, by traversing - and - * loading - its parents. This method is ment for lazy hierarchies. - * A callback is executed for every node as we go. - * @example - * tree.loadKeyPath("/_3/_23/_26/_27", function(node, status){ - * if(status === "loaded") { - * console.log("loaded intermiediate node " + node); - * }else if(status === "ok") { - * node.activate(); - * } - * }); - * - * @param {string | string[]} keyPathList one or more key paths (e.g. '/3/2_1/7') - * @param {function} callback callback(node, status) is called for every visited node ('loading', 'loaded', 'ok', 'error') - * @returns {$.Promise} - */ - loadKeyPath: function(keyPathList, callback, _rootNode) { - var deferredList, dfd, i, path, key, loadMap, node, segList, - root = _rootNode || this.rootNode, - sep = this.options.keyPathSeparator, - self = this; - - if(!$.isArray(keyPathList)){ - keyPathList = [keyPathList]; - } - // Pass 1: handle all path segments for nodes that are already loaded - // Collect distinct top-most lazy nodes in a map - loadMap = {}; - - for(i=0; i