diff --git a/web/app/views/main.scala.html b/web/app/views/main.scala.html index 35a1728814..1b263e9ce1 100644 --- a/web/app/views/main.scala.html +++ b/web/app/views/main.scala.html @@ -11,6 +11,7 @@ + @@ -18,6 +19,7 @@ + @@ -76,7 +78,7 @@ aria-expand="false" dropdown-toggle> Tools - +
- * '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; + // Expand bottom-up, so only the top node is animated + for(i = len - 1; 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 === 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
- * -
- *
- *
- * // only present in checkbox mode
- *
- * Node 1
- *
- *
// only present if node has children
- * - child1 ...
- * - child2 ...
- *
- *
- *
- *
- * @param {EventData} ctx
- * @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
- * @param {boolean} [collapsed=false] force root node to be collapsed, so we can apply animated expand later
- */
- nodeRender: function(ctx, force, deep, collapsed, _recursive) {
- /* This method must take care of all cases where the current data mode
- * (i.e. node hierarchy) does not match the current markup.
- *
- * - node was not yet rendered:
- * create markup
- * - node was rendered: exit fast
- * - children have been added
- * - childern have been removed
- */
- var childLI, childNode1, childNode2, i, l, next, subCtx,
- node = ctx.node,
- tree = ctx.tree,
- opts = ctx.options,
- aria = opts.aria,
- firstTime = false,
- parent = node.parent,
- isRootNode = !parent,
- children = node.children;
- // FT.debug("nodeRender(" + !!force + ", " + !!deep + ")", node.toString());
+ // FT.debug("nodeRemoveChildren()", node.toString());
+ if(!children){
+ return;
+ }
+ if( this.activeNode && this.activeNode.isDescendantOf(node)){
+ this.activeNode.setActive(false); // TODO: don't fire events
+ }
+ if( this.focusNode && this.focusNode.isDescendantOf(node)){
+ this.focusNode = null;
+ }
+ // TODO: persist must take care to clear select and expand cookies
+ this.nodeRemoveChildMarkup(ctx);
+ // Unlink children to support GC
+ // TODO: also delete this.children (not possible using visit())
+ subCtx = $.extend({}, ctx);
+ node.visit(function(n){
+ n.parent = null;
+ tree._callHook("treeRegisterNode", tree, false, n);
+ if ( opts.removeNode ){
+ subCtx.node = n;
+ opts.removeNode.call(ctx.tree, {type: "removeNode"}, subCtx);
+ }
+ });
+ if( node.lazy ){
+ // 'undefined' would be interpreted as 'not yet loaded' for lazy nodes
+ node.children = [];
+ } else{
+ node.children = null;
+ }
+ this.nodeRenderStatus(ctx);
+ },
+ /**Remove HTML markup for ctx.node and all its descendents.
+ * @param {EventData} ctx
+ */
+ nodeRemoveMarkup: function(ctx) {
+ var node = ctx.node;
+ // FT.debug("nodeRemoveMarkup()", node.toString());
+ // TODO: Unlink attr.ftnode to support GC
+ if(node.li){
+ $(node.li).remove();
+ node.li = null;
+ }
+ this.nodeRemoveChildMarkup(ctx);
+ },
+ /**
+ * Create `<li><span>..</span> .. </li>` tags for this node.
+ *
+ * This method takes care that all HTML markup is created that is required
+ * to display this node in it's current state.
+ *
+ * Call this method to create new nodes, or after the strucuture
+ * was changed (e.g. after moving this node or adding/removing children)
+ * nodeRenderTitle() and nodeRenderStatus() are implied.
+ *
+ * Note: if a node was created/removed, nodeRender() must be called for the
+ * parent.
+ *
+ * -
+ *
+ *
+ * // only present in checkbox mode
+ *
+ * Node 1
+ *
+ *
// only present if node has children
+ * - child1 ...
+ * - child2 ...
+ *
+ *
+ *
+ *
+ * @param {EventData} ctx
+ * @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
+ * @param {boolean} [collapsed=false] force root node to be collapsed, so we can apply animated expand later
+ */
+ nodeRender: function(ctx, force, deep, collapsed, _recursive) {
+ /* This method must take care of all cases where the current data mode
+ * (i.e. node hierarchy) does not match the current markup.
+ *
+ * - node was not yet rendered:
+ * create markup
+ * - node was rendered: exit fast
+ * - children have been added
+ * - childern have been removed
+ */
+ var childLI, childNode1, childNode2, i, l, next, subCtx,
+ node = ctx.node,
+ tree = ctx.tree,
+ opts = ctx.options,
+ aria = opts.aria,
+ firstTime = false,
+ parent = node.parent,
+ isRootNode = !parent,
+ children = node.children;
+ // FT.debug("nodeRender(" + !!force + ", " + !!deep + ")", node.toString());
- if( ! isRootNode && ! parent.ul ) {
- // Calling node.collapse on a deep, unrendered node
- return;
- }
- _assert(isRootNode || parent.ul, "parent UL must exist");
+ if( ! isRootNode && ! parent.ul ) {
+ // Calling node.collapse on a deep, unrendered node
+ return;
+ }
+ _assert(isRootNode || parent.ul, "parent UL must exist");
// if(node.li && (force || (node.li.parentNode !== node.parent.ul) ) ){
// if(node.li.parentNode !== node.parent.ul){
@@ -2703,1409 +2703,1412 @@ $.extend(Fancytree.prototype,
// // this.debug("nodeRemoveMarkup...");
// this.nodeRemoveMarkup(ctx);
// }
- // Render the node
- if( !isRootNode ){
- // Discard markup on force-mode, or if it is not linked to parent // Access widget methods and members: - * var tree = $("#tree").fancytree("getTree"); - * var node = $("#tree").fancytree("getActiveNode", "1234"); - *- * - * @mixin Fancytree_Widget - */ + /** + * The plugin (derrived from jQuery.Widget).
// 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. + $.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: false, // TODO: default to true - autoActivate: true, - autoCollapse: false, + dataType: "json" // Expect json format and pass json object to callbacks. + }, // + aria: false, // TODO: default to true + autoActivate: true, + autoCollapse: false, // autoFocus: false, - autoScroll: false, - checkbox: false, - /**defines click behavior*/ - clickFolderMode: 4, - debugLevel: null, // 0..2 (null: use global setting $.ui.fancytree.debugInfo) - disabled: false, // TODO: required anymore? - enableAspx: true, // TODO: document - extensions: [], - fx: { height: "toggle", duration: 200 }, - generateIds: false, - icons: true, - idPrefix: "ft_", - keyboard: true, - keyPathSeparator: "/", - minExpandLevel: 1, - scrollOfs: {top: 0, bottom: 0}, - scrollParent: null, - selectMode: 2, - strings: { - loading: "Loading…", - loadError: "Load error!" - }, - tabbable: true, - titlesTabbable: false, - _classNames: { - node: "fancytree-node", - folder: "fancytree-folder", - 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", - partsel: "fancytree-partsel", - lastsib: "fancytree-lastsib", - loading: "fancytree-loading", - error: "fancytree-error" - }, - // events - lazyLoad: null, - postProcess: null - }, - /* Set up the widget, Called on first $().fancytree() */ - _create: function() { - this.tree = new Fancytree(this); + autoScroll: false, + checkbox: false, + /**defines click behavior*/ + clickFolderMode: 4, + debugLevel: null, // 0..2 (null: use global setting $.ui.fancytree.debugInfo) + disabled: false, // TODO: required anymore? + enableAspx: true, // TODO: document + extensions: [], + fx: { height: "toggle", duration: 200 }, + generateIds: false, + icons: false, + idPrefix: "ft_", + keyboard: true, + keyPathSeparator: "/", + minExpandLevel: 1, + scrollOfs: {top: 0, bottom: 0}, + scrollParent: null, + selectMode: 2, + strings: { + loading: "Loading…", + loadError: "Load error!" + }, + tabbable: true, + titlesTabbable: false, + _classNames: { + node: "fancytree-node", + folder: "fancytree-folder", + 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", + partsel: "fancytree-partsel", + lastsib: "fancytree-lastsib", + loading: "fancytree-loading", + error: "fancytree-error", + glyph: "glyphicon glyphicon-plus-sign" + }, + // 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, - extensions = this.options.extensions, - base = this.tree; + 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, + extensions = this.options.extensions, + base = this.tree; - for(i=0; i
// 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: "@VERSION", // Set to semver by 'grunt release' - /** @type {string} */ - buildType: "development", // Set to 'production' by 'grunt build' - /** @type {int} */ - debugLevel: 2, // Set to 1 by 'grunt build' - // Used by $.ui.fancytree.debug() and as default for tree.options.debugLevel + /** + * 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: "@VERSION", // Set to semver by 'grunt release' + /** @type {string} */ + buildType: "development", // Set to 'production' by 'grunt build' + /** @type {int} */ + debugLevel: 2, // Set to 1 by 'grunt build' + // Used by $.ui.fancytree.debug() and as default for tree.options.debugLevel - _nextId: 1, - _nextNodeKey: 1, - _extensions: {}, - // focusTree: null, + _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); - }, - /** 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 >= 2 - * @param {string} msg - */ - debug: function(msg){ - /*jshint expr:true */ - ($.ui.fancytree.debugLevel >= 2) && consoleApply("log", arguments); - }, - /** Write error message to console. - * @param {string} msg - */ - error: function(msg){ - consoleApply("error", arguments); - }, - /** Convert <, >, &, ", ', / to the equivalent entitites. - * - * @param {string} s - * @returns {string} - */ - escapeHtml: function(s){ - return ("" + s).replace(/[&<>"'\/]/g, function (s) { - return ENTITY_MAP[s]; - }); - }, - /** Inverse of escapeHtml(). - * - * @param {string} s - * @returns {string} - */ - unescapeHtml: function(s){ - var e = document.createElement("div"); - e.innerHTML = s; - return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue; - }, - /** Return a {node: FancytreeNode, type: TYPE} object for a mouse event. - * - * @param {Event} event Mouse event, e.g. click, ... - * @returns {string} 'title' | 'prefix' | 'expander' | 'checkbox' | 'icon' | undefined - */ - getEventTargetType: function(event){ - return this.getEventTarget(event).type; - }, - /** 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 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) ){ - res.type = "checkbox"; - }else if( /\bfancytree-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 && $(event.target).closest(".fancytree-title").length ) { - // #228: clicking an embedded element inside a title - res.type = "title"; - } - return res; - }, - /** Return a FancytreeNode instance from element. - * - * @param {Element | jQueryObject | Event} el - * @returns {FancytreeNode} matching node or null - */ - getNode: function(el){ - if(el instanceof FancytreeNode){ - return el; // el already was a FancytreeNode - }else if(el.selector !== undefined){ - el = el[0]; // el was a jQuery object: use the DOM element - }else if(el.originalEvent !== undefined){ - el = el.target; // el was an Event - } - while( el ) { - if(el.ftnode) { - return el.ftnode; - } - el = el.parentNode; - } - return null; - }, - /* Return a Fancytree instance from element. - * TODO: this function could help to get around the data('fancytree') / data('ui-fancytree') problem - * @param {Element | jQueryObject | Event} el - * @returns {Fancytree} matching tree or null - * / - getTree: function(el){ - if(el instanceof Fancytree){ - return el; // el already was a Fancytree - }else if(el.selector !== undefined){ - el = el[0]; // el was a jQuery object: use the DOM element - }else if(el.originalEvent !== undefined){ - el = el.target; // el was an Event - } - ... - return null; - }, - */ - /** Write message to console if debugLevel >= 1 - * @param {string} msg - */ - info: function(msg){ - /*jshint expr:true */ - ($.ui.fancytree.debugLevel >= 1) && consoleApply("info", arguments); - }, - /** - * Parse tree data from HTML