app/assets/javascripts/fancytree/jquery.fancytree-all.js in fancytree-rails-2.1.0.pre.0 vs app/assets/javascripts/fancytree/jquery.fancytree-all.js in fancytree-rails-2.3.0

- old
+ new

@@ -5,12 +5,12 @@ * * 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 + * @version 2.3.0 + * @date 2014-08-17T10:39 */ /** Core Fancytree module. */ @@ -612,11 +612,11 @@ // attributes. Return selection state true, false, or undefined. function _walk(node){ var i, l, child, s, state, allSelected,someSelected, children = node.children; - if( children ){ + if( children && children.length ){ // check all children recursively allSelected = true; someSelected = false; for( i=0, l=children.length; i<l; i++ ){ @@ -850,10 +850,20 @@ * @returns {boolean} */ hasFocus: function() { return (this.tree.hasFocus() && this.tree.focusNode === this); }, + /** Write to browser console if debugLevel >= 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); @@ -1061,10 +1071,13 @@ }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); @@ -1194,11 +1207,11 @@ sib = null; // Navigate to node function _goto(n){ if( n ){ - n.makeVisible(); + 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; @@ -1465,17 +1478,24 @@ * @see Fancytree#setFocus */ setFocus: function(flag){ return this.tree._callHook("nodeSetFocus", this, flag); }, - // TODO: setLazyNodeStatus /**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; @@ -1664,10 +1684,13 @@ 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 <div data-foo=''> this.data = _getElementDataAsDict(this.$div); this._id = $.ui.fancytree._nextId++; this._ns = ".fancytree-" + this._id; // append for namespaced events @@ -1881,21 +1904,21 @@ * @param {boolean | string} [active=true] */ generateFormElements: function(selected, active) { // TODO: test case var nodeList, - selectedName = (selected !== false) ? "ft_" + this._id : selected, + selectedName = (selected !== false) ? "ft_" + this._id + "[]" : selected, activeName = (active !== false) ? "ft_" + this._id + "_active" : active, id = "fancytree_result_" + this._id, - $result = this.$container.find("div#" + id); + $result = $("#" + id); if($result.length){ $result.empty(); }else{ $result = $("<div>", { id: id - }).hide().appendTo(this.$container); + }).hide().insertAfter(this.$container); } if(selectedName){ nodeList = this.getSelectedNodes( this.options.selectMode === 3 ); $.each(nodeList, function(idx, node){ $result.append($("<input>", { @@ -1965,11 +1988,16 @@ return false; } }, true); return match; }, - // TODO: getRoot() + /** 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[]} @@ -2371,11 +2399,11 @@ * @param {object[]|object|string|$.Promise|function} source * @returns {$.Promise} The deferred will be resolved as soon as the (ajax) * data was rendered. */ nodeLoadChildren: function(ctx, source) { - var ajax, delay, + var ajax, delay, dfd, tree = ctx.tree, node = ctx.node; if($.isFunction(source)){ source = source(); @@ -2391,55 +2419,64 @@ delay = delay[0] + Math.random() * (delay[1] - delay[0]); } node.debug("nodeLoadChildren waiting debug delay " + Math.round(delay) + "ms"); ajax.debugDelay = false; - source = $.Deferred(function (dfd) { + dfd = $.Deferred(function (dfd) { setTimeout(function () { $.ajax(ajax) .done(function () { dfd.resolveWith(this, arguments); }) .fail(function () { dfd.rejectWith(this, arguments); }); }, delay); }); }else{ - source = $.ajax(ajax); + dfd = $.ajax(ajax); } - // TODO: change 'pipe' to 'then' for jQuery 1.8 - // $.pipe returns a new Promise with filtered results - source = source.pipe(function (data, textStatus, jqXHR) { - var res; + // Defer the deferred: we want to be able to reject, even if ajax + // resolved ok. + source = new $.Deferred(); + dfd.done(function (data, textStatus, jqXHR) { + var errorObj, res; if(typeof data === "string"){ $.error("Ajax request returned a string (did you get the JSON dataType wrong?)."); } - // postProcess is similar to the standard dataFilter hook, + // postProcess is similar to the standard ajax dataFilter hook, // but it is also called for JSONP if( ctx.options.postProcess ){ - res = tree._triggerNodeEvent("postProcess", ctx, ctx.originalEvent, {response: data, dataType: this.dataType}); + res = tree._triggerNodeEvent("postProcess", ctx, ctx.originalEvent, {response: data, error: null, dataType: this.dataType}); + if( res.error ) { + errorObj = $.isPlainObject(res.error) ? res.error : {message: res.error}; + errorObj = tree._makeHookContext(node, null, errorObj); + source.rejectWith(this, [errorObj]); + return; + } data = $.isArray(res) ? res : data; + } else if (data && data.hasOwnProperty("d") && ctx.options.enableAspx ) { // Process ASPX WebMethod JSON object inside "d" property data = (typeof data.d === "string") ? $.parseJSON(data.d) : data.d; } - return data; - }, function (jqXHR, textStatus, errorThrown) { - return tree._makeHookContext(node, null, { + source.resolveWith(this, [data]); + }).fail(function (jqXHR, textStatus, errorThrown) { + var errorObj = tree._makeHookContext(node, null, { error: jqXHR, args: Array.prototype.slice.call(arguments), message: errorThrown, details: jqXHR.status + ": " + errorThrown }); + source.rejectWith(this, [errorObj]); }); } if($.isFunction(source.promise)){ // `source` is a deferred, i.e. ajax request _assert(!node.isLoading()); // node._isLoading = true; tree.nodeSetStatus(ctx, "loading"); - source.done(function () { + source.done(function (children) { tree.nodeSetStatus(ctx, "ok"); }).fail(function(error){ var ctxErr; if (error.node && error.error && error.message) { // error is already a context object @@ -2449,12 +2486,13 @@ error: error, // it can be jqXHR or any custom error args: Array.prototype.slice.call(arguments), message: error ? (error.message || error.toString()) : "" }); } - tree._triggerNodeEvent("loaderror", ctxErr, null); - tree.nodeSetStatus(ctx, "error", ctxErr.message, ctxErr.details); + if( tree._triggerNodeEvent("loadError", ctxErr, null) !== false ) { + tree.nodeSetStatus(ctx, "error", ctxErr.message, ctxErr.details); + } }); } // $.when(source) resolves also for non-deferreds return $.when(source).done(function(children){ var metaData; @@ -2470,13 +2508,11 @@ $.extend(tree.data, metaData); } _assert($.isArray(children), "expected array of children"); node._setChildren(children); // trigger fancytreeloadchildren - // if( node.parent ) { tree._triggerNodeEvent("loadChildren", node); - // } // }).always(function(){ // node._isLoading = false; }); }, /** [Not Implemented] */ @@ -3789,11 +3825,11 @@ */ $.extend($.ui.fancytree, /** @lends Fancytree_Static# */ { /** @type {string} */ - version: "2.1.0", // Set to semver by 'grunt release' + version: "2.3.0", // Set to semver by 'grunt release' /** @type {string} */ buildType: "production", // Set to 'production' by 'grunt build' /** @type {int} */ debugLevel: 1, // Set to 1 by 'grunt build' // Used by $.ui.fancytree.debug() and as default for tree.options.debugLevel @@ -4088,12 +4124,12 @@ * Copyright (c) 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 + * @version 2.3.0 + * @date 2014-08-17T10:39 */ // To keep the global namespace clean, we wrap everything in a closure ;(function($, undefined) { @@ -4254,22 +4290,474 @@ }); // End of namespace closure }(jQuery)); /*! + * + * jquery.fancytree.clones.js + * Support faster lookup of nodes by key and shared ref-ids. + * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/) + * + * Copyright (c) 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 + */ + +;(function($, window, document, undefined) { + +"use strict"; + +/******************************************************************************* + * Private functions and variables + */ +function _assert(cond, msg){ + // TODO: see qunit.js extractStacktrace() + if(!cond){ + msg = msg ? ": " + msg : ""; + $.error("Assertion failed" + msg); + } +} + + +/* Return first occurrence of member from array. */ +function _removeArrayMember(arr, elem) { + // TODO: use Array.indexOf for IE >= 9 + var i; + for (i = arr.length - 1; i >= 0; i--) { + if (arr[i] === elem) { + arr.splice(i, 1); + return true; + } + } + return false; +} + + +// /** +// * Calculate a 32 bit FNV-1a hash +// * Found here: https://gist.github.com/vaiorabbit/5657561 +// * Ref.: http://isthe.com/chongo/tech/comp/fnv/ +// * +// * @param {string} str the input value +// * @param {boolean} [asString=false] set to true to return the hash value as +// * 8-digit hex string instead of an integer +// * @param {integer} [seed] optionally pass the hash of the previous chunk +// * @returns {integer | string} +// */ +// function hashFnv32a(str, asString, seed) { +// /*jshint bitwise:false */ +// var i, l, +// hval = (seed === undefined) ? 0x811c9dc5 : seed; + +// for (i = 0, l = str.length; i < l; i++) { +// hval ^= str.charCodeAt(i); +// hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); +// } +// if( asString ){ +// // Convert to 8 digit hex string +// return ("0000000" + (hval >>> 0).toString(16)).substr(-8); +// } +// return hval >>> 0; +// } + + +/** + * JS Implementation of MurmurHash3 (r136) (as of May 20, 2011) + * + * @author <a href="mailto:gary.court@gmail.com">Gary Court</a> + * @see http://github.com/garycourt/murmurhash-js + * @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a> + * @see http://sites.google.com/site/murmurhash/ + * + * @param {string} key ASCII only + * @param {boolean} [asString=false] + * @param {number} seed Positive integer only + * @return {number} 32-bit positive integer hash + */ +function hashMurmur3(key, asString, seed) { + /*jshint bitwise:false */ + var h1b, k1, + remainder = key.length & 3, + bytes = key.length - remainder, + h1 = seed, + c1 = 0xcc9e2d51, + c2 = 0x1b873593, + i = 0; + + while (i < bytes) { + k1 = + ((key.charCodeAt(i) & 0xff)) | + ((key.charCodeAt(++i) & 0xff) << 8) | + ((key.charCodeAt(++i) & 0xff) << 16) | + ((key.charCodeAt(++i) & 0xff) << 24); + ++i; + + k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff; + k1 = (k1 << 15) | (k1 >>> 17); + k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff; + + h1 ^= k1; + h1 = (h1 << 13) | (h1 >>> 19); + h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff; + h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16)); + } + + k1 = 0; + + switch (remainder) { + /*jshint -W086:true */ + case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16; + case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8; + case 1: k1 ^= (key.charCodeAt(i) & 0xff); + + k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff; + k1 = (k1 << 15) | (k1 >>> 17); + k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff; + h1 ^= k1; + } + + h1 ^= key.length; + + h1 ^= h1 >>> 16; + h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff; + h1 ^= h1 >>> 13; + h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff; + h1 ^= h1 >>> 16; + + if( asString ){ + // Convert to 8 digit hex string + return ("0000000" + (h1 >>> 0).toString(16)).substr(-8); + } + return h1 >>> 0; +} + +// console.info(hashMurmur3("costarring")); +// console.info(hashMurmur3("costarring", true)); +// console.info(hashMurmur3("liquid")); +// console.info(hashMurmur3("liquid", true)); + + +/* + * Return a unique key for node by calculationg the hash of the parents refKey-list + */ +function calcUniqueKey(node) { + var key, + path = $.map(node.getParentList(false, true), function(e){ return e.refKey || e.key; }); + path = path.join("/"); + key = "id_" + hashMurmur3(path, true); + node.debug(path + " -> " + key); + return key; +} + + +/** + * [ext-clones] Return a list of clone-nodes or null. + * @param {boolean} [includeSelf=false] + * @returns {FancytreeNode[] | null} + * + * @alias FancytreeNode#getCloneList + * @requires jquery.fancytree.clones.js + */ +$.ui.fancytree._FancytreeNodeClass.prototype.getCloneList = function(includeSelf){ + var key, + tree = this.tree, + refList = tree.refMap[this.refKey] || null, + keyMap = tree.keyMap; + + if( refList ) { + key = this.key; + // Convert key list to node list + if( includeSelf ) { + refList = $.map(refList, function(val){ return keyMap[val]; }); + } else { + refList = $.map(refList, function(val){ return val === key ? null : keyMap[val]; }); + if( refList.length < 1 ) { + refList = null; + } + } + } + return refList; +}; + + +/** + * [ext-clones] Return true if this node has at least another clone with same refKey. + * @returns {boolean} + * + * @alias FancytreeNode#isClone + * @requires jquery.fancytree.clones.js + */ +$.ui.fancytree._FancytreeNodeClass.prototype.isClone = function(){ + var refKey = this.refKey || null, + refList = refKey && this.tree.refMap[refKey] || null; + return !!(refList && refList.length > 1); +}; + + +/** + * [ext-clones] Update key and/or refKey for an existing node. + * @param {string} key + * @param {string} refKey + * @returns {boolean} + * + * @alias FancytreeNode#reRegister + * @requires jquery.fancytree.clones.js + */ +$.ui.fancytree._FancytreeNodeClass.prototype.reRegister = function(key, refKey){ + key = (key == null) ? null : "" + key; + refKey = (refKey == null) ? null : "" + refKey; + this.debug("reRegister", key, refKey); + + var tree = this.tree, + prevKey = this.key, + prevRefKey = this.refKey, + keyMap = tree.keyMap, + refMap = tree.refMap, + refList = refMap[prevRefKey] || null, +// curCloneKeys = refList ? node.getCloneList(true), + modified = false; + + // Key has changed: update all references + if( key != null && key !== this.key ) { + if( keyMap[key] ) { + $.error("[ext-clones] reRegister(" + key + "): already exists."); + } + // Update keyMap + delete keyMap[prevKey]; + keyMap[key] = this; + // Update refMap + if( refList ) { + refMap[prevRefKey] = $.map(refList, function(e){ + return e === prevKey ? key : e; + }); + } + this.key = key; + modified = true; + } + + // refKey has changed + if( refKey != null && refKey !== this.refKey ) { + // Remove previous refKeys + if( refList ){ + if( refList.length === 1 ){ + delete refMap[prevRefKey]; + }else{ + refMap[prevRefKey] = $.map(refList, function(e){ + return e === prevKey ? null : e; + }); + } + } + // Add refKey + if( refMap[refKey] ) { + refMap[refKey].append(key); + }else{ + refMap[refKey] = [ this.key ]; + } + this.refKey = refKey; + modified = true; + } + return modified; +}; + + +/** + * [ext-clones] Return all nodes with a given refKey (null if not found). + * @param {string} refKey + * @param {FancytreeNode} [rootNode] optionally restrict results to descendants of this node + * @returns {FancytreeNode[] | null} + * @alias Fancytree#getNodesByRef + * @requires jquery.fancytree.clones.js + */ +$.ui.fancytree._FancytreeClass.prototype.getNodesByRef = function(refKey, rootNode){ + var keyMap = this.keyMap, + refList = this.refMap[refKey] || null; + + if( refList ) { + // Convert key list to node list + if( rootNode ) { + refList = $.map(refList, function(val){ + var node = keyMap[val]; + return node.isDescendantOf(rootNode) ? node : null; + }); + }else{ + refList = $.map(refList, function(val){ return keyMap[val]; }); + } + if( refList.length < 1 ) { + refList = null; + } + } + return refList; +}; + + +/** + * [ext-clones] Replace a refKey with a new one. + * @param {string} oldRefKey + * @param {string} newRefKey + * @alias Fancytree#changeRefKey + * @requires jquery.fancytree.clones.js + */ +$.ui.fancytree._FancytreeClass.prototype.changeRefKey = function(oldRefKey, newRefKey) { + var i, node, + keyMap = this.keyMap, + refList = this.refMap[oldRefKey] || null; + + if (refList) { + for (i = 0; i < refList.length; i++) { + node = keyMap[refList[i]]; + node.refKey = newRefKey; + } + delete this.refMap[oldRefKey]; + this.refMap[newRefKey] = refList; + } +}; + + +/******************************************************************************* + * Extension code + */ +$.ui.fancytree.registerExtension({ + name: "clones", + version: "0.0.3", + // Default options for this extension. + options: { + highlightActiveClones: true, // set 'fancytree-active-clone' on active clones and all peers + highlightClones: false // set 'fancytree-clone' class on any node that has at least one clone + }, + + treeCreate: function(ctx){ + this._super(ctx); + ctx.tree.refMap = {}; + ctx.tree.keyMap = {}; + }, + treeInit: function(ctx){ + this.$container.addClass("fancytree-ext-clones"); + _assert(ctx.options.defaultKey == null); + // Generate unique / reproducible default keys + ctx.options.defaultKey = function(node){ + return calcUniqueKey(node); + }; + // The default implementation loads initial data + this._super(ctx); + }, + treeClear: function(ctx){ + ctx.tree.refMap = {}; + ctx.tree.keyMap = {}; + return this._super(ctx); + }, + treeRegisterNode: function(ctx, add, node) { + var refList, len, + tree = ctx.tree, + keyMap = tree.keyMap, + refMap = tree.refMap, + key = node.key, + refKey = (node && node.refKey != null) ? "" + node.refKey : null; + +// ctx.tree.debug("clones.treeRegisterNode", add, node); + + if( key === "_statusNode" ){ + return this._super(ctx, add, node); + } + + if( add ) { + if( keyMap[node.key] != null ) { + $.error("clones.treeRegisterNode: node.key already exists: " + node.key); + } + keyMap[key] = node; + if( refKey ) { + refList = refMap[refKey]; + if( refList ) { + refList.push(key); + if( refList.length === 2 && ctx.options.clones.highlightClones ) { + // Mark peer node, if it just became a clone (no need to + // mark current node, since it will be rendered later anyway) + keyMap[refList[0]].renderStatus(); + } + } else { + refMap[refKey] = [key]; + } + node.debug("clones.treeRegisterNode: add clone =>", refMap[refKey]); + } + }else { + if( keyMap[key] == null ) { + $.error("clones.treeRegisterNode: node.key not registered: " + node.key); + } + delete keyMap[key]; + if( refKey ) { + refList = refMap[refKey]; + node.debug("clones.treeRegisterNode: remove clone BEFORE =>", refMap[refKey]); + if( refList ) { + len = refList.length; + if( len <= 1 ){ + _assert(len === 1); + _assert(refList[0] === key); + delete refMap[refKey]; + }else{ + _removeArrayMember(refList, key); + // Unmark peer node, if this was the only clone + if( len === 2 && ctx.options.clones.highlightClones ) { +// node.debug("clones.treeRegisterNode: last =>", node.getCloneList()); + keyMap[refList[0]].renderStatus(); + } + } + node.debug("clones.treeRegisterNode: remove clone =>", refMap[refKey]); + } + } + } + return this._super(ctx, add, node); + }, + nodeRenderStatus: function(ctx) { + var $span, res, + node = ctx.node; + + res = this._super(ctx); + + if( ctx.options.clones.highlightClones ) { + $span = $(node[ctx.tree.statusClassPropName]); + // Only if span already exists + if( $span.length && node.isClone() ){ +// node.debug("clones.nodeRenderStatus: ", ctx.options.clones.highlightClones); + $span.addClass("fancytree-clone"); + } + } + return res; + }, + nodeSetActive: function(ctx, flag) { + var res, + scpn = ctx.tree.statusClassPropName, + node = ctx.node; + + res = this._super(ctx, flag); + + if( ctx.options.clones.highlightActiveClones && node.isClone() ) { + $.each(node.getCloneList(true), function(idx, n){ + n.debug("clones.nodeSetActive: ", flag !== false); + $(n[scpn]).toggleClass("fancytree-active-clone", flag !== false); + }); + } + return res; + } +}); +}(jQuery, window, document)); + +/*! * jquery.fancytree.dnd.js * * Drag-and-drop support. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/) * * Copyright (c) 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 + * @version 2.3.0 + * @date 2014-08-17T10:39 */ ;(function($, window, document, undefined) { "use strict"; @@ -4300,13 +4788,12 @@ addClasses: false, appendTo: "body", containment: false, delay: 0, distance: 4, - // TODO: merge Dynatree issue 419 revert: false, - scroll: true, // issue 244: enable scrolling (if ul.fancytree-container) + scroll: true, // to disable, also set css 'position: inherit' on ul.fancytree-container scrollSpeed: 7, scrollSensitivity: 10, // Delegate draggable.start, drag, and stop events to our handler connectToFancytree: true, // Let source tree create the helper element @@ -4459,10 +4946,11 @@ // helper: null, // Make tree nodes accept draggables autoExpandMS: 1000, // Expand nodes after n milliseconds of hovering. preventVoidMoves: true, // Prevent dropping nodes 'before self', etc. preventRecursiveMoves: true, // Prevent dropping nodes on own descendants + focusOnClick: false, // Focus, although draggable cancels mousedown event (#270) dragEnter: null, // Callback(targetNode, data) dragOver: null, // Callback(targetNode, data) dragDrop: null, // Callback(targetNode, data) dragLeave: null, // Callback(targetNode, data) // @@ -4471,20 +4959,40 @@ }, treeInit: function(ctx){ var tree = ctx.tree; this._super(ctx); + // issue #270: draggable eats mousedown events + if( tree.options.dnd.dragStart ){ + tree.$container.on("mousedown", function(event){ + if( !tree.hasFocus() && ctx.options.dnd.focusOnClick ) { + var node = $.ui.fancytree.getNode(event); + node.debug("Re-enable focus that was prevented by jQuery UI draggable."); + // node.setFocus(); + // $(node.span).closest(":tabbable").focus(); + // $(event.target).trigger("focus"); + // $(event.target).closest(":tabbable").trigger("focus"); + $(event.target).closest(":tabbable").focus(); + } + }); + } _initDragAndDrop(tree); }, /* Override key handler in order to cancel dnd on escape.*/ nodeKeydown: function(ctx) { var event = ctx.originalEvent; if( event.which === $.ui.keyCode.ESCAPE) { this._local._cancelDrag(); } return this._super(ctx); }, + nodeClick: function(ctx) { + // if( ctx.options.dnd.dragStart ){ + // ctx.tree.$container.focus(); + // } + return this._super(ctx); + }, /* Display drop marker according to hitMode ('after', 'before', 'over', 'out', 'start', 'stop'). */ _setDndStatus: function(sourceNode, targetNode, helper, hitMode, accept) { var posOpts, markerOffsetX = 0, markerAt = "center", @@ -4788,12 +5296,12 @@ * Copyright (c) 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 + * @version 2.3.0 + * @date 2014-08-17T10:39 */ ;(function($, window, document, undefined) { "use strict"; @@ -4896,10 +5404,11 @@ break; case $.ui.keyCode.ENTER: node.editEnd(true, event); return false; // so we don't start editmode on Mac } + event.stopPropagation(); }).blur(function(event){ return node.editEnd(true, event); }); instOpts.edit.call(node, {type: "edit"}, eventData); @@ -5103,12 +5612,12 @@ * Copyright (c) 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 + * @version 2.3.0 + * @date 2014-08-17T10:39 */ ;(function($, window, document, undefined) { "use strict"; @@ -5265,12 +5774,12 @@ * Copyright (c) 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 + * @version 2.3.0 + * @date 2014-08-17T10:39 */ ;(function($, window, document, undefined) { "use strict"; @@ -5314,10 +5823,11 @@ tree.$container.addClass("fancytree-ext-glyph"); }, nodeRenderStatus: function(ctx) { var icon, span, node = ctx.node, + $span = $(node.span), opts = ctx.options.glyph, // callback = opts.icon, map = opts.map // prefix = opts.prefix // $span = $(node.span) @@ -5326,12 +5836,13 @@ this._super(ctx); if( node.isRoot() ){ return; } - - span = $("span.fancytree-expander", node.span).get(0); + // #257: only search one level deep: + // span = $("span.fancytree-expander", node.span).get(0); + span = $span.children("span.fancytree-expander").get(0); if( span ){ if( node.isLoading() ){ icon = "loading"; }else if( node.expanded ){ icon = "expanderOpen"; @@ -5340,20 +5851,26 @@ }else if( node.hasChildren() ){ icon = "expanderClosed"; }else{ icon = "noExpander"; } + // node.debug(icon, map[icon], span); span.className = "fancytree-expander " + map[icon]; } - span = $("span.fancytree-checkbox", node.tr || node.span).get(0); + if( node.tr ){ + span = $(node.tr).children("span.fancytree-checkbox").get(0); + }else{ + span = $span.children("span.fancytree-checkbox").get(0); + } if( span ){ icon = node.selected ? "checkboxSelected" : (node.partsel ? "checkboxUnknown" : "checkbox"); span.className = "fancytree-checkbox " + map[icon]; } - span = $("span.fancytree-icon", node.span).get(0); + // span = $("span.fancytree-icon", node.span).get(0); + span = $span.children("span.fancytree-icon").get(0); if( span ){ if( node.folder ){ icon = node.expanded ? _getIcon(opts, "folderOpen") : _getIcon(opts, "folder"); }else{ icon = node.expanded ? _getIcon(opts, "docOpen") : _getIcon(opts, "doc"); @@ -5393,12 +5910,12 @@ * Copyright (c) 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 + * @version 2.3.0 + * @date 2014-08-17T10:39 */ ;(function($, window, document, undefined) { "use strict"; @@ -5595,12 +6112,12 @@ * Copyright (c) 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 + * @version 2.3.0 + * @date 2014-08-17T10:39 */ ;(function($, window, document, undefined) { "use strict"; @@ -5933,12 +6450,12 @@ * Copyright (c) 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 + * @version 2.3.0 + * @date 2014-08-17T10:39 */ ;(function($, window, document, undefined) { "use strict"; @@ -6295,11 +6812,11 @@ * Copyright (c) 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 + * @version 2.3.0 + * @date 2014-08-17T10:39 */ ;(function($, window, document, undefined) { "use strict";