/*! jQuery Fancytree Plugin - 2.30.2 - 2019-01-13T08:17:01Z * https://github.com/mar10/fancytree * Copyright (c) 2019 Martin Wendt; Licensed MIT */ /*! jQuery UI - v1.12.1 - 2018-05-20 * http://jqueryui.com * Includes: widget.js, position.js, keycode.js, scroll-parent.js, unique-id.js * Copyright jQuery Foundation and other contributors; Licensed MIT */ /* NOTE: Original jQuery UI wrapper was replaced with a simple IIFE. See README-Fancytree.md */ (function( $ ) { $.ui = $.ui || {}; var version = $.ui.version = "1.12.1"; /*! * jQuery UI Widget 1.12.1 * http://jqueryui.com * * Copyright jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license */ //>>label: Widget //>>group: Core //>>description: Provides a factory for creating stateful widgets with a common API. //>>docs: http://api.jqueryui.com/jQuery.widget/ //>>demos: http://jqueryui.com/widget/ var widgetUuid = 0; var widgetSlice = Array.prototype.slice; $.cleanData = ( function( orig ) { return function( elems ) { var events, elem, i; for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) { try { // Only trigger remove when necessary to save time events = $._data( elem, "events" ); if ( events && events.remove ) { $( elem ).triggerHandler( "remove" ); } // Http://bugs.jquery.com/ticket/8235 } catch ( e ) {} } orig( elems ); }; } )( $.cleanData ); $.widget = function( name, base, prototype ) { var existingConstructor, constructor, basePrototype; // ProxiedPrototype allows the provided prototype to remain unmodified // so that it can be used as a mixin for multiple widgets (#8876) var proxiedPrototype = {}; var namespace = name.split( "." )[ 0 ]; name = name.split( "." )[ 1 ]; var fullName = namespace + "-" + name; if ( !prototype ) { prototype = base; base = $.Widget; } if ( $.isArray( prototype ) ) { prototype = $.extend.apply( null, [ {} ].concat( prototype ) ); } // Create selector for plugin $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { return !!$.data( elem, fullName ); }; $[ namespace ] = $[ namespace ] || {}; existingConstructor = $[ namespace ][ name ]; constructor = $[ namespace ][ name ] = function( options, element ) { // Allow instantiation without "new" keyword if ( !this._createWidget ) { return new constructor( options, element ); } // Allow instantiation without initializing for simple inheritance // must use "new" keyword (the code above always passes args) if ( arguments.length ) { this._createWidget( options, element ); } }; // Extend with the existing constructor to carry over any static properties $.extend( constructor, existingConstructor, { version: prototype.version, // Copy the object used to create the prototype in case we need to // redefine the widget later _proto: $.extend( {}, prototype ), // Track widgets that inherit from this widget in case this widget is // redefined after a widget inherits from it _childConstructors: [] } ); basePrototype = new base(); // We need to make the options hash a property directly on the new instance // otherwise we'll modify the options hash on the prototype that we're // inheriting from basePrototype.options = $.widget.extend( {}, basePrototype.options ); $.each( prototype, function( prop, value ) { if ( !$.isFunction( value ) ) { proxiedPrototype[ prop ] = value; return; } proxiedPrototype[ prop ] = ( function() { function _super() { return base.prototype[ prop ].apply( this, arguments ); } function _superApply( args ) { return base.prototype[ prop ].apply( this, args ); } return function() { var __super = this._super; var __superApply = this._superApply; var returnValue; this._super = _super; this._superApply = _superApply; returnValue = value.apply( this, arguments ); this._super = __super; this._superApply = __superApply; return returnValue; }; } )(); } ); constructor.prototype = $.widget.extend( basePrototype, { // TODO: remove support for widgetEventPrefix // always use the name + a colon as the prefix, e.g., draggable:start // don't prefix for widgets that aren't DOM-based widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name }, proxiedPrototype, { constructor: constructor, namespace: namespace, widgetName: name, widgetFullName: fullName } ); // If this widget is being redefined then we need to find all widgets that // are inheriting from it and redefine all of them so that they inherit from // the new version of this widget. We're essentially trying to replace one // level in the prototype chain. if ( existingConstructor ) { $.each( existingConstructor._childConstructors, function( i, child ) { var childPrototype = child.prototype; // Redefine the child widget using the same prototype that was // originally used, but inherit from the new version of the base $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); } ); // Remove the list of existing child constructors from the old constructor // so the old child constructors can be garbage collected delete existingConstructor._childConstructors; } else { base._childConstructors.push( constructor ); } $.widget.bridge( name, constructor ); return constructor; }; $.widget.extend = function( target ) { var input = widgetSlice.call( arguments, 1 ); var inputIndex = 0; var inputLength = input.length; var key; var value; for ( ; inputIndex < inputLength; inputIndex++ ) { for ( key in input[ inputIndex ] ) { value = input[ inputIndex ][ key ]; if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { // Clone objects if ( $.isPlainObject( value ) ) { target[ key ] = $.isPlainObject( target[ key ] ) ? $.widget.extend( {}, target[ key ], value ) : // Don't extend strings, arrays, etc. with objects $.widget.extend( {}, value ); // Copy everything else by reference } else { target[ key ] = value; } } } } return target; }; $.widget.bridge = function( name, object ) { var fullName = object.prototype.widgetFullName || name; $.fn[ name ] = function( options ) { var isMethodCall = typeof options === "string"; var args = widgetSlice.call( arguments, 1 ); var returnValue = this; if ( isMethodCall ) { // If this is an empty collection, we need to have the instance method // return undefined instead of the jQuery instance if ( !this.length && options === "instance" ) { returnValue = undefined; } else { this.each( function() { var methodValue; var instance = $.data( this, fullName ); if ( options === "instance" ) { returnValue = instance; return false; } if ( !instance ) { return $.error( "cannot call methods on " + name + " prior to initialization; " + "attempted to call method '" + options + "'" ); } if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) { return $.error( "no such method '" + options + "' for " + name + " widget instance" ); } methodValue = instance[ options ].apply( instance, args ); if ( methodValue !== instance && methodValue !== undefined ) { returnValue = methodValue && methodValue.jquery ? returnValue.pushStack( methodValue.get() ) : methodValue; return false; } } ); } } else { // Allow multiple hashes to be passed on init if ( args.length ) { options = $.widget.extend.apply( null, [ options ].concat( args ) ); } this.each( function() { var instance = $.data( this, fullName ); if ( instance ) { instance.option( options || {} ); if ( instance._init ) { instance._init(); } } else { $.data( this, fullName, new object( options, this ) ); } } ); } return returnValue; }; }; $.Widget = function( /* options, element */ ) {}; $.Widget._childConstructors = []; $.Widget.prototype = { widgetName: "widget", widgetEventPrefix: "", defaultElement: "
* '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'. * 'firstChild': add this node as first child of targetNode. * '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"; } else if (mode === "firstChild") { if (targetNode.children && targetNode.children.length) { mode = "before"; targetNode = targetNode.children[0]; } else { mode = "child"; } } var pos, prevParent = this.parent, targetParent = mode === "child" ? targetNode : targetNode.parent; if (this === targetNode) { return; } else if (!this.parent) { $.error("Cannot move system root"); } else if (targetParent.isDescendantOf(this)) { $.error("Cannot move a node to its own descendant"); } if (targetParent !== prevParent) { prevParent.triggerModifyChild("remove", this); } // 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, "invalid source parent"); 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, "invalid target parent"); targetParent.children.splice(pos, 0, this); break; case "after": // Insert this node after target node pos = $.inArray(targetNode, targetParent.children); _assert(pos >= 0, "invalid target parent"); targetParent.children.splice(pos + 1, 0, this); break; default: $.error("Invalid mode " + mode); } } else { targetParent.children = [this]; } // Parent has no
// Access widget methods and members: * var tree = $("#tree").fancytree("getTree"); * var node = $("#tree").fancytree("getActiveNode", "1234"); ** * @mixin Fancytree_Widget */ $.widget( "ui.fancytree", /** @lends Fancytree_Widget# */ { /**These options will be used as defaults * @type {FancytreeOptions} */ options: { activeVisible: true, ajax: { type: "GET", cache: false, // false: Append random '_' argument to the request url to prevent caching. // timeout: 0, // >0: Make sure we get an ajax error if server is unreachable dataType: "json", // Expect json format and pass json object to callbacks. }, aria: true, autoActivate: true, autoCollapse: false, autoScroll: false, checkbox: false, clickFolderMode: 4, debugLevel: null, // 0..4 (null: use global setting $.ui.fancytree.debugInfo) disabled: false, // TODO: required anymore? enableAspx: true, escapeTitles: false, extensions: [], // fx: { height: "toggle", duration: 200 }, // toggleEffect: { effect: "drop", options: {direction: "left"}, duration: 200 }, // toggleEffect: { effect: "slide", options: {direction: "up"}, duration: 200 }, //toggleEffect: { effect: "blind", options: {direction: "vertical", scale: "box"}, duration: 200 }, toggleEffect: { effect: "slideToggle", duration: 200 }, //< "toggle" or "slideToggle" to use jQuery instead of jQueryUI for toggleEffect animation generateIds: false, icon: true, idPrefix: "ft_", focusOnSelect: false, keyboard: true, keyPathSeparator: "/", minExpandLevel: 1, nodata: true, // (bool, string, or callback) display message, when no data available quicksearch: false, rtl: false, scrollOfs: { top: 0, bottom: 0 }, scrollParent: null, selectMode: 2, strings: { loading: "Loading...", // … would be escaped when escapeTitles is true loadError: "Load error!", moreData: "More...", noData: "No data.", }, tabindex: "0", titlesTabbable: false, tooltip: false, _classNames: { node: "fancytree-node", folder: "fancytree-folder", animating: "fancytree-animating", combinedExpanderPrefix: "fancytree-exp-", combinedIconPrefix: "fancytree-ico-", hasChildren: "fancytree-has-children", active: "fancytree-active", selected: "fancytree-selected", expanded: "fancytree-expanded", lazy: "fancytree-lazy", focused: "fancytree-focused", partload: "fancytree-partload", partsel: "fancytree-partsel", radio: "fancytree-radio", // radiogroup: "fancytree-radiogroup", unselectable: "fancytree-unselectable", lastsib: "fancytree-lastsib", loading: "fancytree-loading", error: "fancytree-error", statusNodePrefix: "fancytree-statusnode-", }, // events lazyLoad: null, postProcess: null, }, /* Set up the widget, Called on first $().fancytree() */ _create: function() { this.tree = new Fancytree(this); this.$source = this.source || this.element.data("type") === "json" ? this.element : this.element.find(">ul:first"); // Subclass Fancytree instance with all enabled extensions var extension, extName, i, opts = this.options, extensions = opts.extensions, base = this.tree; for (i = 0; i < extensions.length; i++) { extName = extensions[i]; extension = $.ui.fancytree._extensions[extName]; if (!extension) { $.error( "Could not apply extension '" + extName + "' (it is not registered, did you forget to include it?)" ); } // Add extension options as tree.options.EXTENSION // _assert(!this.tree.options[extName], "Extension name must not exist as option name: " + extName); // console.info("extend " + extName, extension.options, this.tree.options[extName]) // issue #876: we want to replace custom array-options, not merge them this.tree.options[extName] = _simpleDeepMerge( {}, extension.options, this.tree.options[extName] ); // this.tree.options[extName] = $.extend(true, {}, extension.options, this.tree.options[extName]); // console.info("extend " + extName + " =>", this.tree.options[extName]) // console.info("extend " + extName + " org default =>", extension.options) // Add a namespace tree.ext.EXTENSION, to hold instance data _assert( this.tree.ext[extName] === undefined, "Extension name must not exist as Fancytree.ext attribute: '" + extName + "'" ); // this.tree[extName] = extension; this.tree.ext[extName] = {}; // Subclass Fancytree methods using proxies. _subclassObject(this.tree, base, extension, extName); // current extension becomes base for the next extension base = extension; } // if (opts.icons !== undefined) { // 2015-11-16 if (opts.icon !== true) { $.error( "'icons' tree option is deprecated since v2.14.0: use 'icon' only instead" ); } else { this.tree.warn( "'icons' tree option is deprecated since v2.14.0: use 'icon' instead" ); opts.icon = opts.icons; } } if (opts.iconClass !== undefined) { // 2015-11-16 if (opts.icon) { $.error( "'iconClass' tree option is deprecated since v2.14.0: use 'icon' only instead" ); } else { this.tree.warn( "'iconClass' tree option is deprecated since v2.14.0: use 'icon' instead" ); opts.icon = opts.iconClass; } } if (opts.tabbable !== undefined) { // 2016-04-04 opts.tabindex = opts.tabbable ? "0" : "-1"; this.tree.warn( "'tabbable' tree option is deprecated since v2.17.0: use 'tabindex='" + opts.tabindex + "' instead" ); } // this.tree._callHook("treeCreate", this.tree); // Note: 'fancytreecreate' event is fired by widget base class // this.tree._triggerTreeEvent("create"); }, /* Called on every $().fancytree() */ _init: function() { this.tree._callHook("treeInit", this.tree); // TODO: currently we call bind after treeInit, because treeInit // might change tree.$container. // It would be better, to move event binding into hooks altogether this._bind(); }, /* Use the _setOption method to respond to changes to options. */ _setOption: function(key, value) { return this.tree._callHook( "treeSetOption", this.tree, key, value ); }, /** Use the destroy method to clean up any modifications your widget has made to the DOM */ destroy: function() { this._unbind(); this.tree._callHook("treeDestroy", this.tree); // In jQuery UI 1.8, you must invoke the destroy method from the base widget $.Widget.prototype.destroy.call(this); // TODO: delete tree and nodes to make garbage collect easier? // TODO: In jQuery UI 1.9 and above, you would define _destroy instead of destroy and not call the base method }, // ------------------------------------------------------------------------- /* Remove all event handlers for our namespace */ _unbind: function() { var ns = this.tree._ns; this.element.off(ns); this.tree.$container.off(ns); $(document).off(ns); }, /* Add mouse and kyboard handlers to the container */ _bind: function() { var that = this, opts = this.options, tree = this.tree, ns = tree._ns; // selstartEvent = ( $.support.selectstart ? "selectstart" : "mousedown" ) // Remove all previuous handlers for this tree this._unbind(); //alert("keydown" + ns + "foc=" + tree.hasFocus() + tree.$container); // tree.debug("bind events; container: ", tree.$container); tree.$container .on("focusin" + ns + " focusout" + ns, function(event) { var node = FT.getNode(event), flag = event.type === "focusin"; if (!flag && node && $(event.target).is("a")) { // #764 node.debug( "Ignored focusout on embedded element." ); return; } // tree.treeOnFocusInOut.call(tree, event); // tree.debug("Tree container got event " + event.type, node, event, FT.getEventTarget(event)); if (flag) { if (tree._getExpiringValue("focusin")) { // #789: IE 11 may send duplicate focusin events tree.debug("Ignored double focusin."); return; } tree._setExpiringValue("focusin", true, 50); if (!node) { // #789: IE 11 may send focusin before mousdown(?) node = tree._getExpiringValue("mouseDownNode"); if (node) { tree.debug( "Reconstruct mouse target for focusin from recent event." ); } } } if (node) { // For example clicking into an that is part of a node tree._callHook( "nodeSetFocus", tree._makeHookContext(node, event), flag ); } else { if ( tree.tbody && $(event.target).parents( "table.fancytree-container > thead" ).length ) { // #767: ignore events in the table's header tree.debug( "Ignore focus event outside table body.", event ); } else { tree._callHook("treeSetFocus", tree, flag); } } }) .on("selectstart" + ns, "span.fancytree-title", function( event ) { // prevent mouse-drags to select text ranges // tree.debug(" got event " + event.type); event.preventDefault(); }) .on("keydown" + ns, function(event) { // TODO: also bind keyup and keypress // tree.debug("got event " + event.type + ", hasFocus:" + tree.hasFocus()); // if(opts.disabled || opts.keyboard === false || !tree.hasFocus() ){ if (opts.disabled || opts.keyboard === false) { return true; } var res, node = tree.focusNode, // node may be null ctx = tree._makeHookContext(node || tree, event), prevPhase = tree.phase; try { tree.phase = "userEvent"; // If a 'fancytreekeydown' handler returns false, skip the default // handling (implemented by tree.nodeKeydown()). if (node) { res = tree._triggerNodeEvent( "keydown", node, event ); } else { res = tree._triggerTreeEvent("keydown", event); } if (res === "preventNav") { res = true; // prevent keyboard navigation, but don't prevent default handling of embedded input controls } else if (res !== false) { res = tree._callHook("nodeKeydown", ctx); } return res; } finally { tree.phase = prevPhase; } }) .on("mousedown" + ns, function(event) { var et = FT.getEventTarget(event); // that.tree.debug("event(" + event.type + "): node: ", et.node); // #712: Store the clicked node, so we can use it when we get a focusin event // ('click' event fires after focusin) // tree.debug("event(" + event.type + "): node: ", et.node); tree._lastMousedownNode = et ? et.node : null; // #789: Store the node also for a short period, so we can use it // in a *resulting* focusin event tree._setExpiringValue( "mouseDownNode", tree._lastMousedownNode ); }) .on("click" + ns + " dblclick" + ns, function(event) { if (opts.disabled) { return true; } var ctx, et = FT.getEventTarget(event), node = et.node, tree = that.tree, prevPhase = tree.phase; // that.tree.debug("event(" + event.type + "): node: ", node); if (!node) { return true; // Allow bubbling of other events } ctx = tree._makeHookContext(node, event); // that.tree.debug("event(" + event.type + "): node: ", node); try { tree.phase = "userEvent"; switch (event.type) { case "click": ctx.targetType = et.type; if (node.isPagingNode()) { return ( tree._triggerNodeEvent( "clickPaging", ctx, event ) === true ); } return tree._triggerNodeEvent( "click", ctx, event ) === false ? false : tree._callHook("nodeClick", ctx); case "dblclick": ctx.targetType = et.type; return tree._triggerNodeEvent( "dblclick", ctx, event ) === false ? false : tree._callHook("nodeDblclick", ctx); } } finally { tree.phase = prevPhase; } }); }, /** Return the active node or null. * @returns {FancytreeNode} */ getActiveNode: function() { return this.tree.activeNode; }, /** Return the matching node or null. * @param {string} key * @returns {FancytreeNode} */ getNodeByKey: function(key) { return this.tree.getNodeByKey(key); }, /** Return the invisible system root node. * @returns {FancytreeNode} */ getRootNode: function() { return this.tree.rootNode; }, /** Return the current tree instance. * @returns {Fancytree} */ getTree: function() { return this.tree; }, } ); // $.ui.fancytree was created by the widget factory. Create a local shortcut: FT = $.ui.fancytree; /** * Static members in the `$.ui.fancytree` namespace.
// Access static members: * var node = $.ui.fancytree.getNode(element); * alert($.ui.fancytree.version); ** * @mixin Fancytree_Static */ $.extend( $.ui.fancytree, /** @lends Fancytree_Static# */ { /** @type {string} */ version: "2.30.2", // Set to semver by 'grunt release' /** @type {string} */ buildType: "production", // Set to 'production' by 'grunt build' /** @type {int} */ debugLevel: 3, // Set to 3 by 'grunt build' // Used by $.ui.fancytree.debug() and as default for tree.options.debugLevel _nextId: 1, _nextNodeKey: 1, _extensions: {}, // focusTree: null, /** Expose class object as $.ui.fancytree._FancytreeClass */ _FancytreeClass: Fancytree, /** Expose class object as $.ui.fancytree._FancytreeNodeClass */ _FancytreeNodeClass: FancytreeNode, /* Feature checks to provide backwards compatibility */ jquerySupports: { // http://jqueryui.com/upgrade-guide/1.9/#deprecated-offset-option-merged-into-my-and-at positionMyOfs: isVersionAtLeast($.ui.version, 1, 9), }, /** Throw an error if condition fails (debug method). * @param {boolean} cond * @param {string} msg */ assert: function(cond, msg) { return _assert(cond, msg); }, /** Create a new Fancytree instance on a target element. * * @param {Element | jQueryObject | string} el Target DOM element or selector * @param {FancytreeOptions} [opts] Fancytree options * @returns {Fancytree} new tree instance * @example * var tree = $.ui.fancytree.createTree("#tree", { * source: {url: "my/webservice"} * }); // Create tree for this matching element * * @since 2.25 */ createTree: function(el, opts) { var tree = $(el) .fancytree(opts) .fancytree("getTree"); return tree; }, /** Return a function that executes *fn* at most every *timeout* ms. * @param {integer} timeout * @param {function} fn * @param {boolean} [invokeAsap=false] * @param {any} [ctx] */ debounce: function(timeout, fn, invokeAsap, ctx) { var timer; if (arguments.length === 3 && typeof invokeAsap !== "boolean") { ctx = invokeAsap; invokeAsap = false; } return function() { var args = arguments; ctx = ctx || this; invokeAsap && !timer && fn.apply(ctx, args); clearTimeout(timer); timer = setTimeout(function() { invokeAsap || fn.apply(ctx, args); timer = null; }, timeout); }; }, /** Write message to console if debugLevel >= 4 * @param {string} msg */ debug: function(msg) { /*jshint expr:true */ $.ui.fancytree.debugLevel >= 4 && consoleApply("log", arguments); }, /** Write error message to console if debugLevel >= 1. * @param {string} msg */ error: function(msg) { $.ui.fancytree.debugLevel >= 1 && consoleApply("error", arguments); }, /** Convert <, >, &, ", ', / to the equivalent entities. * * @param {string} s * @returns {string} */ escapeHtml: function(s) { return ("" + s).replace(REX_HTML, function(s) { return ENTITY_MAP[s]; }); }, /** Make jQuery.position() arguments backwards compatible, i.e. if * jQuery UI version <= 1.8, convert * { my: "left+3 center", at: "left bottom", of: $target } * to * { my: "left center", at: "left bottom", of: $target, offset: "3 0" } * * See http://jqueryui.com/upgrade-guide/1.9/#deprecated-offset-option-merged-into-my-and-at * and http://jsfiddle.net/mar10/6xtu9a4e/ * * @param {object} opts * @returns {object} the (potentially modified) original opts hash object */ fixPositionOptions: function(opts) { if (opts.offset || ("" + opts.my + opts.at).indexOf("%") >= 0) { $.error( "expected new position syntax (but '%' is not supported)" ); } if (!$.ui.fancytree.jquerySupports.positionMyOfs) { var // parse 'left+3 center' into ['left+3 center', 'left', '+3', 'center', undefined] myParts = /(\w+)([+-]?\d+)?\s+(\w+)([+-]?\d+)?/.exec( opts.my ), atParts = /(\w+)([+-]?\d+)?\s+(\w+)([+-]?\d+)?/.exec( opts.at ), // convert to numbers dx = (myParts[2] ? +myParts[2] : 0) + (atParts[2] ? +atParts[2] : 0), dy = (myParts[4] ? +myParts[4] : 0) + (atParts[4] ? +atParts[4] : 0); opts = $.extend({}, opts, { // make a copy and overwrite my: myParts[1] + " " + myParts[3], at: atParts[1] + " " + atParts[3], }); if (dx || dy) { opts.offset = "" + dx + " " + dy; } } return opts; }, /** Return a {node: FancytreeNode, type: TYPE} object for a mouse event. * * @param {Event} event Mouse event, e.g. click, ... * @returns {object} Return a {node: FancytreeNode, type: TYPE} object * TYPE: 'title' | 'prefix' | 'expander' | 'checkbox' | 'icon' | undefined */ getEventTarget: function(event) { var $target, tree, tcn = event && event.target ? event.target.className : "", res = { node: this.getNode(event.target), type: undefined }; // We use a fast version of $(res.node).hasClass() // See http://jsperf.com/test-for-classname/2 if (/\bfancytree-title\b/.test(tcn)) { res.type = "title"; } else if (/\bfancytree-expander\b/.test(tcn)) { res.type = res.node.hasChildren() === false ? "prefix" : "expander"; // }else if( /\bfancytree-checkbox\b/.test(tcn) || /\bfancytree-radio\b/.test(tcn) ){ } else if (/\bfancytree-checkbox\b/.test(tcn)) { res.type = "checkbox"; } else if (/\bfancytree(-custom)?-icon\b/.test(tcn)) { res.type = "icon"; } else if (/\bfancytree-node\b/.test(tcn)) { // Somewhere near the title res.type = "title"; } else if (event && event.target) { $target = $(event.target); if ($target.is("ul[role=group]")) { // #nnn: Clicking right to a node may hit the surrounding UL tree = res.node && res.node.tree; (tree || FT).debug("Ignoring click on outer UL."); res.node = null; } else if ($target.closest(".fancytree-title").length) { // #228: clicking an embedded element inside a title res.type = "title"; } else if ($target.closest(".fancytree-checkbox").length) { // E.g.