From a9dcb2c50a38c5a4c07404ed259da966e3ede022 Mon Sep 17 00:00:00 2001 From: Wanderson Braganca Date: Wed, 4 Jun 2014 16:56:14 -0300 Subject: [PATCH] initial commit --- .gitattributes | 26 + LICENSE | 27 + README.md | 64 + assets/MIT-LICENSE.txt | 21 + assets/jquery.fancytree-all.js | 6365 ++++++++++++++++++ assets/jquery.fancytree-custom.min.js | 41 + assets/jquery.fancytree.js | 4070 +++++++++++ assets/jquery.fancytree.min.js | 14 + assets/skin-awesome/ui.fancytree.css | 311 + assets/skin-awesome/ui.fancytree.min.css | 6 + assets/skin-bootstrap/ui.fancytree.css | 391 ++ assets/skin-bootstrap/ui.fancytree.min.css | 6 + assets/skin-lion/icons.gif | Bin 0 -> 5906 bytes assets/skin-lion/loading.gif | Bin 0 -> 1849 bytes assets/skin-lion/ui.fancytree.css | 501 ++ assets/skin-lion/ui.fancytree.min.css | 6 + assets/skin-themeroller/icons.gif | Bin 0 -> 5492 bytes assets/skin-themeroller/loading.gif | Bin 0 -> 3234 bytes assets/skin-themeroller/ui.fancytree.css | 505 ++ assets/skin-themeroller/ui.fancytree.min.css | 1 + assets/skin-vista/icons.gif | Bin 0 -> 5492 bytes assets/skin-vista/loading.gif | Bin 0 -> 3234 bytes assets/skin-vista/ui.fancytree.css | 507 ++ assets/skin-vista/ui.fancytree.min.css | 6 + assets/skin-win7/icons.gif | Bin 0 -> 5492 bytes assets/skin-win7/loading.gif | Bin 0 -> 3234 bytes assets/skin-win7/ui.fancytree.css | 571 ++ assets/skin-win7/ui.fancytree.min.css | 6 + assets/skin-win8-xxl/icons.gif | Bin 0 -> 20860 bytes assets/skin-win8-xxl/loading.gif | Bin 0 -> 6243 bytes assets/skin-win8-xxl/ui.fancytree.css | 528 ++ assets/skin-win8-xxl/ui.fancytree.min.css | 11 + assets/skin-win8/icons.gif | Bin 0 -> 5492 bytes assets/skin-win8/loading.gif | Bin 0 -> 3234 bytes assets/skin-win8/ui.fancytree.css | 518 ++ assets/skin-win8/ui.fancytree.min.css | 6 + assets/skin-xp/icons-rtl.gif | Bin 0 -> 4046 bytes assets/skin-xp/icons.gif | Bin 0 -> 4041 bytes assets/skin-xp/loading.gif | Bin 0 -> 570 bytes assets/skin-xp/ui.fancytree.css | 498 ++ assets/skin-xp/ui.fancytree.min.css | 6 + assets/skin-xp/vline-rtl.gif | Bin 0 -> 842 bytes assets/skin-xp/vline.gif | Bin 0 -> 844 bytes assets/src/jquery.fancytree.childcounter.js | 185 + assets/src/jquery.fancytree.clones.js | 447 ++ assets/src/jquery.fancytree.columnview.js | 149 + assets/src/jquery.fancytree.debug.js | 142 + assets/src/jquery.fancytree.dnd.js | 523 ++ assets/src/jquery.fancytree.edit.js | 314 + assets/src/jquery.fancytree.filter.js | 161 + assets/src/jquery.fancytree.glyph.js | 127 + assets/src/jquery.fancytree.gridnav.js | 199 + assets/src/jquery.fancytree.js | 4070 +++++++++++ assets/src/jquery.fancytree.menu.js | 155 + assets/src/jquery.fancytree.persist.js | 339 + assets/src/jquery.fancytree.table.js | 359 + assets/src/jquery.fancytree.themeroller.js | 79 + assets/src/jquery.fancytree.wide.js | 113 + composer.json | 25 + fancytree/FancytreeAsset.php | 63 + fancytree/FancytreeWidget.php | 79 + 61 files changed, 22541 insertions(+) create mode 100644 .gitattributes create mode 100644 LICENSE create mode 100644 README.md create mode 100644 assets/MIT-LICENSE.txt create mode 100644 assets/jquery.fancytree-all.js create mode 100644 assets/jquery.fancytree-custom.min.js create mode 100644 assets/jquery.fancytree.js create mode 100644 assets/jquery.fancytree.min.js create mode 100644 assets/skin-awesome/ui.fancytree.css create mode 100644 assets/skin-awesome/ui.fancytree.min.css create mode 100644 assets/skin-bootstrap/ui.fancytree.css create mode 100644 assets/skin-bootstrap/ui.fancytree.min.css create mode 100644 assets/skin-lion/icons.gif create mode 100644 assets/skin-lion/loading.gif create mode 100644 assets/skin-lion/ui.fancytree.css create mode 100644 assets/skin-lion/ui.fancytree.min.css create mode 100644 assets/skin-themeroller/icons.gif create mode 100644 assets/skin-themeroller/loading.gif create mode 100644 assets/skin-themeroller/ui.fancytree.css create mode 100644 assets/skin-themeroller/ui.fancytree.min.css create mode 100644 assets/skin-vista/icons.gif create mode 100644 assets/skin-vista/loading.gif create mode 100644 assets/skin-vista/ui.fancytree.css create mode 100644 assets/skin-vista/ui.fancytree.min.css create mode 100644 assets/skin-win7/icons.gif create mode 100644 assets/skin-win7/loading.gif create mode 100644 assets/skin-win7/ui.fancytree.css create mode 100644 assets/skin-win7/ui.fancytree.min.css create mode 100644 assets/skin-win8-xxl/icons.gif create mode 100644 assets/skin-win8-xxl/loading.gif create mode 100644 assets/skin-win8-xxl/ui.fancytree.css create mode 100644 assets/skin-win8-xxl/ui.fancytree.min.css create mode 100644 assets/skin-win8/icons.gif create mode 100644 assets/skin-win8/loading.gif create mode 100644 assets/skin-win8/ui.fancytree.css create mode 100644 assets/skin-win8/ui.fancytree.min.css create mode 100644 assets/skin-xp/icons-rtl.gif create mode 100644 assets/skin-xp/icons.gif create mode 100644 assets/skin-xp/loading.gif create mode 100644 assets/skin-xp/ui.fancytree.css create mode 100644 assets/skin-xp/ui.fancytree.min.css create mode 100644 assets/skin-xp/vline-rtl.gif create mode 100644 assets/skin-xp/vline.gif create mode 100644 assets/src/jquery.fancytree.childcounter.js create mode 100644 assets/src/jquery.fancytree.clones.js create mode 100644 assets/src/jquery.fancytree.columnview.js create mode 100644 assets/src/jquery.fancytree.debug.js create mode 100644 assets/src/jquery.fancytree.dnd.js create mode 100644 assets/src/jquery.fancytree.edit.js create mode 100644 assets/src/jquery.fancytree.filter.js create mode 100644 assets/src/jquery.fancytree.glyph.js create mode 100644 assets/src/jquery.fancytree.gridnav.js create mode 100644 assets/src/jquery.fancytree.js create mode 100644 assets/src/jquery.fancytree.menu.js create mode 100644 assets/src/jquery.fancytree.persist.js create mode 100644 assets/src/jquery.fancytree.table.js create mode 100644 assets/src/jquery.fancytree.themeroller.js create mode 100644 assets/src/jquery.fancytree.wide.js create mode 100644 composer.json create mode 100644 fancytree/FancytreeAsset.php create mode 100644 fancytree/FancytreeWidget.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..39a8c57 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,26 @@ +# Autodetect text files +* text=auto + +# ...Unless the name matches the following overriding patterns + +# Definitively text files +*.php text +*.css text +*.js text +*.txt text +*.md text +*.xml text +*.json text +*.bat text +*.sql text +*.xml text +*.yml text + +# Ensure those won't be messed up with +*.png binary +*.jpg binary +*.gif binary +*.ttf binary +*.svg binary +*.eot binary +*.woff binary diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b5c1623 --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014, Wanderson Bragança +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +* Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2b82e0e --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +yii2-fancytree-widget +===================== +The yii2-fancytree-widget is a Yii 2 wrapper for the [Fancytree](http://wwwendt.de/tech/fancytree/demo/). A JavaScript dynamic tree view plugin for jQuery with support for persistence, keyboard, checkboxes, tables, drag'n'drop, and lazy loading. + +Installation +------------ + +The preferred way to install this extension is through [composer](http://getcomposer.org/download/). + +Either run + +``` +php composer.phar require --prefer-dist wbraganca/yii2-fancytree-widget "*" +``` + +or add + +``` +"wbraganca/yii2-fancytree-widget": "*" +``` + +to the require section of your `composer.json` file. + + +How to use +---------- + +On your view file. + +```php + + 'Node 1', 'key' => 1], + ['title' => 'Folder 2', 'key' => '2', 'folder' => true, 'children' => [ + ['title' => 'Node 2.1', 'key' => '3'], + ['title' => 'Node 2.2', 'key' => '4'] + ]] +]; + +echo \wbraganca\fancytree\FancytreeWidget::widget([ + 'options' =>[ + 'source' => $data, + 'extensions' => ['dnd'], + 'dnd' => [ + 'preventVoidMoves' => true, + 'preventRecursiveMoves' => true, + 'autoExpandMS' => 400, + 'dragStart' => new JsExpression('function(node, data) { + return true; + }'), + 'dragEnter' => new JsExpression('function(node, data) { + return true; + }'), + 'dragDrop' => new JsExpression('function(node, data) { + data.otherNode.moveTo(node, data.hitMode); + }'), + ], + ] +]); +?> + +``` diff --git a/assets/MIT-LICENSE.txt b/assets/MIT-LICENSE.txt new file mode 100644 index 0000000..43d69f0 --- /dev/null +++ b/assets/MIT-LICENSE.txt @@ -0,0 +1,21 @@ +Copyright 2006-2014 Martin Wendt, +http://wwWendt.de/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/assets/jquery.fancytree-all.js b/assets/jquery.fancytree-all.js new file mode 100644 index 0000000..69be56e --- /dev/null +++ b/assets/jquery.fancytree-all.js @@ -0,0 +1,6365 @@ +/*! + * 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.1.0 + * @date 2014-05-29T16:44 + */ + +/** 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 ){ + // check all children recursively + allSelected = true; + someSelected = false; + + for( i=0, l=children.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 ) { + 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