From ef68436ac04da078ffdcacd7e1f785473a303d45 Mon Sep 17 00:00:00 2001 From: Giulio Cesare Solaroli Date: Sun, 02 Oct 2011 23:56:18 +0000 Subject: First version of the newly restructured repository --- (limited to 'frontend/beta/js/YUI-extensions/tree') diff --git a/frontend/beta/js/YUI-extensions/tree/AsyncTreeNode.js b/frontend/beta/js/YUI-extensions/tree/AsyncTreeNode.js new file mode 100644 index 0000000..5d48b00 --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/tree/AsyncTreeNode.js @@ -0,0 +1,58 @@ +YAHOO.ext.tree.AsyncTreeNode = function(config){ + this.loaded = false; + this.loading = false; + YAHOO.ext.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments); + this.events['beforeload'] = true; + this.events['load'] = true; +}; +YAHOO.extendX(YAHOO.ext.tree.AsyncTreeNode, YAHOO.ext.tree.TreeNode, { + expand : function(deep, anim, callback){ + if(this.loading){ // if an async load is already running, waiting til it's done + var timer; + var f = function(){ + if(!this.loading){ // done loading + clearInterval(timer); + this.expand(deep, anim, callback); + } + }.createDelegate(this); + timer = setInterval(f, 200); + } + if(!this.loaded){ + if(this.fireEvent('beforeload', this) === false){ + return; + } + this.loading = true; + this.ui.beforeLoad(this); + var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader(); + if(loader){ + loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback])); + return; + } + } + YAHOO.ext.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback); + }, + + isLoading : function(){ + return this.loading; + }, + + loadComplete : function(deep, anim, callback){ + this.loading = false; + this.loaded = true; + this.ui.afterLoad(this); + this.fireEvent('load', this); + this.expand(deep, anim, callback); + }, + + isLoaded : function(){ + return this.loaded; + }, + + hasChildNodes : function(){ + if(!this.isLeaf() && !this.loaded){ + return true; + }else{ + return YAHOO.ext.tree.AsyncTreeNode.superclass.hasChildNodes.call(this); + } + } +}); diff --git a/frontend/beta/js/YUI-extensions/tree/TreeDragZone.js b/frontend/beta/js/YUI-extensions/tree/TreeDragZone.js new file mode 100644 index 0000000..9b77b3c --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/tree/TreeDragZone.js @@ -0,0 +1,43 @@ +YAHOO.ext.tree.TreeDragZone = function(tree, config){ + YAHOO.ext.tree.TreeDragZone.superclass.constructor.call(this, tree.getEl(), config); + this.tree = tree; +}; + +YAHOO.extendX(YAHOO.ext.tree.TreeDragZone, YAHOO.ext.dd.DragZone, { + ddGroup : 'TreeDD', + + onBeforeDrag : function(data, e){ + var n = data.node; + return n && n.draggable && !n.disabled; + }, + + onInitDrag : function(e){ + var data = this.dragData; + this.tree.getSelectionModel().select(data.node); + this.proxy.update(''); + data.node.ui.appendDDGhost(this.proxy.ghost.dom); + this.tree.fireEvent('startdrag', this.tree, data.node, e); + }, + + getRepairXY : function(e, data){ + return data.node.ui.getDDRepairXY(); + }, + + onEndDrag : function(data, e){ + this.tree.fireEvent('enddrag', this.tree, data.node, e); + }, + + onValidDrop : function(dd, e, id){ + this.tree.fireEvent('dragdrop', this.tree, this.dragData.node, dd, e); + this.hideProxy(); + }, + + beforeInvalidDrop : function(e, id){ + if(YAHOO.util.Anim){ + // this scrolls the original position back into view + var sm = this.tree.getSelectionModel(); + sm.clearSelections(); + sm.select(this.dragData.node); + } + } +}); diff --git a/frontend/beta/js/YUI-extensions/tree/TreeDropZone.js b/frontend/beta/js/YUI-extensions/tree/TreeDropZone.js new file mode 100644 index 0000000..91c24e1 --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/tree/TreeDropZone.js @@ -0,0 +1,228 @@ +YAHOO.ext.tree.TreeDropZone = function(tree, config){ + this.allowParentInsert = false; + this.allowContainerDrop = false; + this.appendOnly = false; + YAHOO.ext.tree.TreeDropZone.superclass.constructor.call(this, tree.container, config); + this.tree = tree; + this.lastInsertClass = 'ytree-no-status'; + this.dragOverData = {}; +}; + +YAHOO.extendX(YAHOO.ext.tree.TreeDropZone, YAHOO.ext.dd.DropZone, { + ddGroup : 'TreeDD', + + expandDelay : 1000, + + expandNode : function(node){ + if(node.hasChildNodes() && !node.isExpanded()){ + node.expand(false, null, this.triggerCacheRefresh.createDelegate(this)); + } + }, + + queueExpand : function(node){ + this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]); + }, + + cancelExpand : function(){ + if(this.expandProcId){ + clearTimeout(this.expandProcId); + this.expandProcId = false; + } + }, + + isValidDropPoint : function(n, pt, dd, e, data){ + if(!n || !data){ return false; } + var targetNode = n.node; + var dropNode = data.node; + // default drop rules + if(!(targetNode && targetNode.isTarget && pt)){ + return false; + } + if(pt == 'append' && targetNode.allowChildren === false){ + return false; + } + if((pt == 'above' || pt == 'below') && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){ + return false; + } + if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){ + return false; + } + // reuse the object + var overEvent = this.dragOverData; + overEvent.tree = this.tree; + overEvent.target = targetNode; + overEvent.data = data; + overEvent.point = pt; + overEvent.source = dd; + overEvent.rawEvent = e; + overEvent.dropNode = dropNode; + overEvent.cancel = false; + var result = this.tree.fireEvent('nodedragover', overEvent); + return overEvent.cancel === false && result !== false; + }, + + getDropPoint : function(e, n, dd){ + var tn = n.node; + if(tn.isRoot){ + return tn.allowChildren !== false ? 'ap-pend' : false; // always append for root + } + var dragEl = n.ddel; + var t = YAHOO.util.Dom.getY(dragEl), b = t + dragEl.offsetHeight; + var y = YAHOO.util.Event.getPageY(e); + var noAppend = tn.allowChildren === false || tn.isLeaf(); + if(this.appendOnly || tn.parentNode.allowChildren === false){ + return noAppend ? false : 'append'; + } + var noBelow = false; + if(!this.allowParentInsert){ + noBelow = tn.hasChildNodes() && tn.isExpanded(); + } + var q = (b - t) / (noAppend ? 2 : 3); + if(y >= t && y < t + q){ + return 'above'; + }else if(!noBelow && (noAppend || y >= b-q && y <= b)){ + return 'below'; + }else{ + return 'append'; + } + return false; + }, + + onNodeEnter : function(n, dd, e, data){ + this.cancelExpand(); + }, + + onNodeOver : function(n, dd, e, data){ + var pt = this.getDropPoint(e, n, dd); + var node = n.node; + + // auto node expand check + if(!this.expandProcId && pt == 'append' && node.hasChildNodes() && !n.node.isExpanded()){ + this.queueExpand(node); + }else if(pt != 'append'){ + this.cancelExpand(); + } + + // set the insert point style on the target node + var returnCls = this.dropNotAllowed; + if(this.isValidDropPoint(n, pt, dd, e, data)){ + if(pt){ + var el = n.ddel; + var cls, returnCls; + if(pt == 'above'){ + returnCls = n.node.isFirst() ? 'ytree-drop-ok-above' : 'ytree-drop-ok-between'; + cls = 'ytree-drag-insert-above'; + }else if(pt == 'below'){ + returnCls = n.node.isLast() ? 'ytree-drop-ok-below' : 'ytree-drop-ok-between'; + cls = 'ytree-drag-insert-below'; + }else{ + returnCls = 'ytree-drop-ok-append'; + cls = 'ytree-drag-append'; + } + if(this.lastInsertClass != cls){ + YAHOO.util.Dom.replaceClass(el, this.lastInsertClass, cls); + this.lastInsertClass = cls; + } + } + } + return returnCls; + }, + + onNodeOut : function(n, dd, e, data){ + this.cancelExpand(); + this.removeDropIndicators(n); + }, + + onNodeDrop : function(n, dd, e, data){ + var point = this.getDropPoint(e, n, dd); + var targetNode = n.node; + targetNode.ui.startDrop(); + if(!this.isValidDropPoint(n, point, dd, e, data)){ + targetNode.ui.endDrop(); + return false; + } + // first try to find the drop node + var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null); + var dropEvent = { + tree : this.tree, + target: targetNode, + data: data, + point: point, + source: dd, + rawEvent: e, + dropNode: dropNode, + cancel: dropNode ? false : true + }; + var retval = this.tree.fireEvent('beforenodedrop', dropEvent); + if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){ + targetNode.ui.endDrop(); + return false; + } + if(point == 'append' && !targetNode.isExpanded()){ + targetNode.expand(false, null, function(){ + this.completeDrop(dropEvent); + }.createDelegate(this)); + }else{ + this.completeDrop(dropEvent); + } + return true; + }, + + completeDrop : function(de){ + var ns = de.dropNode, p = de.point, t = de.target; + if(!(ns instanceof Array)){ + ns = [ns]; + } + var n; + for(var i = 0, len = ns.length; i < len; i++){ + n = ns[i]; + if(p == 'above'){ + t.parentNode.insertBefore(n, t); + }else if(p == 'below'){ + t.parentNode.insertBefore(n, t.nextSibling); + }else{ + t.appendChild(n); + } + } + n.select(); // select and highlight the last insert + if(this.tree.hlDrop){ + n.ui.highlight(); + } + t.ui.endDrop(); + this.tree.fireEvent('nodedrop', de); + }, + + afterNodeMoved : function(dd, data, e, targetNode, dropNode){ + if(this.tree.hlDrop){ + dropNode.select(); + dropNode.ui.highlight(); + } + this.tree.fireEvent('nodedrop', this.tree, targetNode, data, dd, e); + }, + + getTree : function(){ + return this.tree; + }, + + removeDropIndicators : function(n){ + if(n && n.ddel){ + var el = n.ddel; + YAHOO.util.Dom.removeClass(el, 'ytree-drag-insert-above'); + YAHOO.util.Dom.removeClass(el, 'ytree-drag-insert-below'); + YAHOO.util.Dom.removeClass(el, 'ytree-drag-append'); + this.lastInsertClass = '_noclass'; + } + }, + + beforeDragDrop : function(target, e, id){ + this.cancelExpand(); + return true; + }, + + afterRepair : function(data){ + if(data){ + data.node.ui.highlight(); + } + this.hideProxy(); + } +}); diff --git a/frontend/beta/js/YUI-extensions/tree/TreeFilter.js b/frontend/beta/js/YUI-extensions/tree/TreeFilter.js new file mode 100644 index 0000000..9eeb274 --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/tree/TreeFilter.js @@ -0,0 +1,105 @@ +/** + * This doesn't update the indent (lines) or expand collapse icons of the nodes + */ +YAHOO.ext.tree.TreeFilter = function(tree, config){ + this.tree = tree; + this.filtered = {}; + YAHOO.ext.util.Config.apply(this, config, { + clearBlank:false, + reverse:false, + autoClear:false, + remove:false + }); +}; + +YAHOO.ext.tree.TreeFilter.prototype = { + /** + * Filter the data by a specific attribute. + * @param {String/RegExp} value Either string that the attribute value + * should start with or a RegExp to test against the attribute + * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text". + * @param {TreeNode} startNode (optional) The node to start the filter at. + */ + filter : function(value, attr, startNode){ + attr = attr || 'text'; + var f; + if(typeof value == 'string'){ + var vlen = value.length; + // auto clear empty filter + if(vlen == 0 && this.clearBlank){ + this.clearFilter(); + return; + } + value = value.toLowerCase(); + f = function(n){ + return n.attributes[attr].substr(0, vlen).toLowerCase() == value; + } + }else if(value.exec){ // regex? + f = function(n){ + return value.test(n.attributes[attr]); + } + }else{ + throw 'Illegal filter type, must be string or regex'; + } + this.filterBy(f, null, startNode); + }, + + /** + * Filter by a function. The passed function will be called with each + * node in the tree (or from the startNode). If the function returns true, the node is kept + * otherwise it is filtered. If a node is filtered, it's children are also filtered. + * @param {Function} fn The filter function + * @param {Object} scope (optional) The scope of the function (defaults to the current node) + */ + filterBy : function(fn, scope, startNode){ + startNode = startNode || this.tree.root; + if(this.autoClear){ + this.clearFilter(); + } + var af = this.filtered, rv = this.reverse; + var f = function(n){ + if(n == startNode){ + return true; + } + if(af[n.id]){ + return false; + } + var m = fn.call(scope || n, n); + if(!m || rv){ + af[n.id] = n; + n.ui.hide(); + return false; + } + return true; + } + startNode.cascade(f); + if(this.remove){ + for(var id in af){ + if(typeof id != 'function'){ + var n = af[id]; + if(n && n.parentNode){ + n.parentNode.removeChild(n); + } + } + } + } + }, + + /** + * Clears the current filter. Note: with the "remove" option + * set a filter cannot be cleared. + */ + clear : function(){ + var t = this.tree; + var af = this.filtered; + for(var id in af){ + if(typeof id != 'function'){ + var n = af[id]; + if(n){ + n.ui.show(); + } + } + } + this.filtered = {}; + } +}; diff --git a/frontend/beta/js/YUI-extensions/tree/TreeLoader.js b/frontend/beta/js/YUI-extensions/tree/TreeLoader.js new file mode 100644 index 0000000..34989bd --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/tree/TreeLoader.js @@ -0,0 +1,107 @@ +YAHOO.ext.tree.TreeLoader = function(config){ + this.baseParams = {}; + this.requestMethod = 'POST'; + YAHOO.ext.util.Config.apply(this, config); + + this.events = { + 'beforeload' : true, + 'load' : true, + 'loadexception' : true + }; +}; + +YAHOO.extendX(YAHOO.ext.tree.TreeLoader, YAHOO.ext.util.Observable, { + load : function(node, callback){ + if(node.attributes.children){ // preloaded json children + var cs = node.attributes.children; + for(var i = 0, len = cs.length; i < len; i++){ + node.appendChild(this.createNode(cs[i])); + } + if(typeof callback == 'function'){ + callback(); + } + }else if(this.dataUrl){ + this.requestData(node, callback); + } + }, + + getParams: function(node){ + var buf = [], bp = this.baseParams; + for(var key in bp){ + if(typeof bp[key] != 'function'){ + buf.push(encodeURIComponent(key), '=', encodeURIComponent(bp[key]), '&'); + } + } + buf.push('node=', encodeURIComponent(node.id)); + return buf.join(''); + }, + + requestData : function(node, callback){ + if(this.fireEvent('beforeload', this, node, callback) !== false){ + var params = this.getParams(node); + var cb = { + success: this.handleResponse, + failure: this.handleFailure, + scope: this, + argument: {callback: callback, node: node} + }; + this.transId = YAHOO.util.Connect.asyncRequest(this.requestMethod, this.dataUrl, cb, params); + }else{ + // if the load is cancelled, make sure we notify + // the node that we are done + if(typeof callback == 'function'){ + callback(); + } + } + }, + + isLoading : function(){ + return this.transId ? true : false; + }, + + abort : function(){ + if(this.isLoading()){ + YAHOO.util.Connect.abort(this.transId); + } + }, + + createNode : function(attr){ + if(this.applyLoader !== false){ + attr.loader = this; + } + return(attr.leaf ? + new YAHOO.ext.tree.TreeNode(attr) : + new YAHOO.ext.tree.AsyncTreeNode(attr)); + }, + + processResponse : function(response, node, callback){ + var json = response.responseText; + try { + var o = eval('('+json+')'); + for(var i = 0, len = o.length; i < len; i++){ + node.appendChild(this.createNode(o[i])); + } + if(typeof callback == 'function'){ + callback(); + } + }catch(e){ + this.handleFailure(response); + } + }, + + handleResponse : function(response){ + this.transId = false; + var a = response.argument; + this.processResponse(response, a.node, a.callback); + this.fireEvent('load', this, a.node, response); + }, + + handleFailure : function(response){ + this.transId = false; + var a = response.argument; + this.fireEvent('loadexception', this, a.node, response); + if(typeof a.callback == 'function'){ + a.callback(); + } + } +}); diff --git a/frontend/beta/js/YUI-extensions/tree/TreeNode.js b/frontend/beta/js/YUI-extensions/tree/TreeNode.js new file mode 100644 index 0000000..c676481 --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/tree/TreeNode.js @@ -0,0 +1,300 @@ +/** + * @class YAHOO.ext.tree.TreeNode + * @extends YAHOO.ext.data.Node + * @cfg {Boolean} leaf true if this node is a leaf and does not have or cannot have children + * @cfg {Boolean} expanded true to start the node expanded + * @cfg {Boolean} draggable false to make this node undraggable if DD is on (default to true) + * @cfg {Boolean} isTarget false if this node cannot be drop on + * @cfg {Boolean} disabled true to start the node disabled + * @constructor + * @param {Object} attributes The attributes/config for the node + */ +YAHOO.ext.tree.TreeNode = function(attributes){ + attributes = attributes || {}; + if(typeof attributes == 'string'){ + attributes = {text: attributes}; + } + this.el = null; + this.childrenRendered = false; + this.rendered = false; + YAHOO.ext.tree.TreeNode.superclass.constructor.call(this, attributes); + this.expanded = attributes.expanded === true; + this.isTarget = attributes.isTarget !== false; + this.draggable = attributes.draggable !== false && attributes.allowDrag !== false; + this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false; + this.text = attributes.text; + this.disabled = attributes.disabled === true; + + YAHOO.ext.util.Config.apply(this.events, { + 'textchange' : true, + 'beforeexpand' : true, + 'beforecollapse' : true, + 'expand' : true, + 'disabledchange' : true, + 'collapse' : true, + 'beforeclick':true, + 'click':true, + 'dblclick':true, + 'contentmenu':true, + 'beforechildrenrendered':true + }); + + var uiClass = this.attributes.uiProvider || YAHOO.ext.tree.TreeNodeUI; + this.ui = new uiClass(this); +}; +YAHOO.extendX(YAHOO.ext.tree.TreeNode, YAHOO.ext.data.Node, { + isExpanded : function(){ + return this.expanded; + }, + + getUI : function(){ + return this.ui; + }, + + setFirstChild : function(node){ + var of = this.firstChild; + YAHOO.ext.tree.TreeNode.superclass.setFirstChild.call(this, node); + if(this.childrenRendered && of && node != of){ + of.renderIndent(true, true); + } + if(this.rendered){ + this.renderIndent(true, true); + } + }, + + setLastChild : function(node){ + var ol = this.lastChild; + YAHOO.ext.tree.TreeNode.superclass.setLastChild.call(this, node); + if(this.childrenRendered && ol && node != ol){ + ol.renderIndent(true, true); + } + if(this.rendered){ + this.renderIndent(true, true); + } + }, + + // these methods are overridden to provide lazy rendering support + appendChild : function(){ + var node = YAHOO.ext.tree.TreeNode.superclass.appendChild.apply(this, arguments); + if(node && this.childrenRendered){ + node.render(); + } + this.ui.updateExpandIcon(); + return node; + }, + + removeChild : function(node){ + this.ownerTree.getSelectionModel().unselect(node); + YAHOO.ext.tree.TreeNode.superclass.removeChild.apply(this, arguments); + // if it's been rendered remove dom node + if(this.childrenRendered){ + node.ui.remove(); + } + if(this.childNodes.length < 1){ + this.collapse(false, false); + }else{ + this.ui.updateExpandIcon(); + } + return node; + }, + + insertBefore : function(node, refNode){ + var newNode = YAHOO.ext.tree.TreeNode.superclass.insertBefore.apply(this, arguments); + if(newNode && refNode && this.childrenRendered){ + node.render(); + } + this.ui.updateExpandIcon(); + return newNode; + }, + + setText : function(text){ + var oldText = this.text; + this.text = text; + this.attributes.text = text; + if(this.rendered){ // event without subscribing + this.ui.onTextChange(this, text, oldText); + } + this.fireEvent('textchange', this, text, oldText); + }, + + select : function(){ + this.getOwnerTree().getSelectionModel().select(this); + }, + + unselect : function(){ + this.getOwnerTree().getSelectionModel().unselect(this); + }, + + isSelected : function(){ + return this.getOwnerTree().getSelectionModel().isSelected(node); + }, + + expand : function(deep, anim, callback){ + if(!this.expanded){ + if(this.fireEvent('beforeexpand', this, deep, anim) === false){ + return; + } + if(!this.childrenRendered){ + this.renderChildren(); + } + this.expanded = true; + if((this.getOwnerTree().animate && anim !== false) || anim){ + this.ui.animExpand(function(){ + this.fireEvent('expand', this); + if(typeof callback == 'function'){ + callback(this); + } + if(deep === true){ + this.expandChildNodes(true); + } + }.createDelegate(this)); + return; + }else{ + this.ui.expand(); + this.fireEvent('expand', this); + if(typeof callback == 'function'){ + callback(this); + } + } + }else{ + if(typeof callback == 'function'){ + callback(this); + } + } + if(deep === true){ + this.expandChildNodes(true); + } + }, + + collapse : function(deep, anim){ + if(this.expanded && (!this.isRoot || (this.isRoot && this.getOwnerTree().rootVisible))){ + if(this.fireEvent('beforecollapse', this, deep, anim) === false){ + return; + } + this.expanded = false; + if((this.getOwnerTree().animate && anim !== false) || anim){ + this.ui.animCollapse(function(){ + this.fireEvent('collapse', this); + if(deep === true){ + this.collapseChildNodes(true); + } + }.createDelegate(this)); + return; + }else{ + this.ui.collapse(); + this.fireEvent('collapse', this); + } + } + if(deep === true){ + var cs = this.childNodes; + for(var i = 0, len = cs.length; i < len; i++) { + cs[i].collapse(true) + } + } + }, + + delayedExpand : function(delay){ + if(!this.expandProcId){ + this.expandProcId = this.expand.defer(delay, this); + } + }, + + cancelExpand : function(){ + if(this.expandProcId){ + clearTimeout(this.expandProcId); + } + this.expandProcId = false; + }, + + toggle : function(){ + if(this.expanded){ + this.collapse(); + }else{ + this.expand(); + } + }, + + ensureVisible : function(){ + if(this.parentNode){ + this.parentNode.bubble(function(){ + this.expand(false, false); + }); + } + }, + + expandChildNodes : function(deep){ + var cs = this.childNodes; + for(var i = 0, len = cs.length; i < len; i++) { + cs[i].expand(deep); + } + }, + + collapseChildNodes : function(deep){ + var cs = this.childNodes; + for(var i = 0, len = cs.length; i < len; i++) { + cs[i].expand(deep); + } + }, + + disable : function(){ + this.disabled = true; + this.unselect(); + if(this.rendered && this.ui.onDisableChange){ // event without subscribing + this.ui.onDisableChange(this, true); + } + this.fireEvent('disabledchange', this, true); + }, + + enable : function(){ + this.disabled = false; + if(this.rendered && this.ui.onDisableChange){ // event without subscribing + this.ui.onDisableChange(this, false); + } + this.fireEvent('disabledchange', this, false); + }, + + renderChildren : function(suppressEvent){ + if(suppressEvent !== false){ + this.fireEvent('beforechildrenrendered', this); + } + var cs = this.childNodes; + for(var i = 0, len = cs.length; i < len; i++){ + cs[i].render(true); + } + this.childrenRendered = true; + }, + + sort : function(fn, scope){ + YAHOO.ext.tree.TreeNode.superclass.sort.apply(this, arguments); + if(this.childrenRendered){ + var cs = this.childNodes; + for(var i = 0, len = cs.length; i < len; i++){ + cs[i].render(true); + } + } + }, + + render : function(bulkRender){ + this.ui.render(bulkRender); + if(!this.rendered){ + this.rendered = true; + if(this.expanded){ + this.expanded = false; + this.expand(false, false); + } + } + }, + + renderIndent : function(deep, refresh){ + if(refresh){ + this.ui.childIndent = null; + } + this.ui.renderIndent(); + if(deep === true && this.childrenRendered){ + var cs = this.childNodes; + for(var i = 0, len = cs.length; i < len; i++){ + cs[i].renderIndent(true, refresh); + } + } + } +}); diff --git a/frontend/beta/js/YUI-extensions/tree/TreeNodeUI.js b/frontend/beta/js/YUI-extensions/tree/TreeNodeUI.js new file mode 100644 index 0000000..80927f4 --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/tree/TreeNodeUI.js @@ -0,0 +1,452 @@ +/** + * The TreeNode UI implementation is separate from the + * tree implementation. Unless you are customizing the tree UI, + * you should never have to use this directly. + */ +YAHOO.ext.tree.TreeNodeUI = function(node){ + this.node = node; + this.rendered = false; + this.animating = false; +}; + +YAHOO.ext.tree.TreeNodeUI.prototype = { + emptyIcon : Ext.BLANK_IMAGE_URL, + + removeChild : function(node){ + if(this.rendered){ + this.ctNode.removeChild(node.ui.getEl()); + } + }, + + beforeLoad : function(){ + YAHOO.util.Dom.addClass(this.elNode, 'ytree-node-loading'); + }, + + afterLoad : function(){ + YAHOO.util.Dom.removeClass(this.elNode, 'ytree-node-loading'); + }, + + onTextChange : function(node, text, oldText){ + if(this.rendered){ + this.textNode.innerHTML = text; + } + }, + + onDisableChange : function(node, state){ + this.disabled = state; + if(state){ + YAHOO.util.Dom.addClass(this.elNode, 'ytree-node-disabled'); + }else{ + YAHOO.util.Dom.removeClass(this.elNode, 'ytree-node-disabled'); + } + }, + + onSelectedChange : function(state){ + if(state){ + this.focus(); + YAHOO.util.Dom.addClass(this.elNode, 'ytree-selected'); + }else{ + this.blur(); + YAHOO.util.Dom.removeClass(this.elNode, 'ytree-selected'); + } + }, + + onMove : function(tree, node, oldParent, newParent, index, refNode){ + this.childIndent = null; + if(this.rendered){ + var targetNode = newParent.ui.getContainer(); + if(!targetNode){//target not rendered + this.holder = document.createElement('div'); + this.holder.appendChild(this.wrap); + return; + } + var insertBefore = refNode ? refNode.ui.getEl() : null; + if(insertBefore){ + targetNode.insertBefore(this.wrap, insertBefore); + }else{ + targetNode.appendChild(this.wrap); + } + this.node.renderIndent(true); + } + }, + + remove : function(){ + if(this.rendered){ + this.holder = document.createElement('div'); + this.holder.appendChild(this.wrap); + } + }, + + fireEvent : function(){ + this.node.fireEvent.apply(this.node, arguments); + }, + + initEvents : function(){ + this.node.on('move', this.onMove, this, true); + //this.node.on('hiddenchange', this.onHiddenChange, this, true); + + // these were optimized out but a custom UI could use them + //this.node.on('remove', this.onChildRemoved, this, true); + //this.node.on('selectedstatechange', this.onSelectedChange, this, true); + //this.node.on('disabledchange', this.onDisableChange, this, true); + //this.node.on('textchange', this.onTextChange, this, true); + + var E = YAHOO.util.Event; + var a = this.anchor; + + var el = YAHOO.ext.Element.fly(a); + + if(YAHOO.ext.util.Browser.isOpera){ // opera render bug ignores the CSS + el.setStyle('text-decoration', 'none'); + } + + el.mon('click', this.onClick, this, true); + el.mon('dblclick', this.onDblClick, this, true); + el.mon('contextmenu', this.onContextMenu, this, true); + + //el.on('focus', function(){ + // this.node.getOwnerTree().getSelectionModel().select(this.node); + //}, this, true); + + var icon = YAHOO.ext.Element.fly(this.iconNode); + icon.mon('click', this.onClick, this, true); + icon.mon('dblclick', this.onDblClick, this, true); + icon.mon('contextmenu', this.onContextMenu, this, true); + E.on(this.ecNode, 'click', this.ecClick, this, true); + + if(this.node.disabled){ + YAHOO.util.Dom.addClass(this.elNode, 'ytree-node-disabled'); + } + if(this.node.hidden){ + YAHOO.util.Dom.addClass(this.elNode, 'ytree-node-disabled'); + } + var dd = this.node.ownerTree.enableDD || this.node.ownerTree.enableDrag || this.node.ownerTree.enableDrop; + if(dd && (!this.node.isRoot || this.node.ownerTree.rootVisible)){ + YAHOO.ext.dd.Registry.register(this.elNode, { + node: this.node, + handles: [this.iconNode, this.textNode], + isHandle: false + }); + } + }, + + hide : function(){ + if(this.rendered){ + this.wrap.style.display = 'none'; + } + }, + + show : function(){ + if(this.rendered){ + this.wrap.style.display = ''; + } + }, + + onContextMenu : function(e){ + e.preventDefault(); + this.focus(); + this.fireEvent('contextmenu', this.node, e); + }, + + onClick : function(e){ + if(this.dropping){ + return; + } + if(this.fireEvent('beforeclick', this.node, e) !== false){ + if(!this.disabled && this.node.attributes.href){ + this.focus(); + this.fireEvent('click', this.node, e); + return; + } + e.preventDefault(); + if(this.disabled){ + return; + } + this.focus(); + this.fireEvent('click', this.node, e); + }else{ + e.stopEvent(); + } + }, + + onDblClick : function(e){ + e.preventDefault(); + if(this.disabled){ + return; + } + if(!this.animating && this.node.hasChildNodes()){ + this.node.toggle(); + } + this.fireEvent('dblclick', this.node, e); + }, + + ecClick : function(e){ + if(!this.animating && this.node.hasChildNodes()){ + this.node.toggle(); + } + }, + + startDrop : function(){ + this.dropping = true; + }, + + // delayed drop so the click event doesn't get fired on a drop + endDrop : function(){ + setTimeout(function(){ + this.dropping = false; + }.createDelegate(this), 50); + }, + + expand : function(){ + this.updateExpandIcon(); + this.ctNode.style.display = ''; + }, + + focus : function(){ + try{ + this.anchor.focus(); + }catch(e){} + }, + + blur : function(){ + try{ + this.anchor.blur(); + }catch(e){} + }, + + animExpand : function(callback){ + if(this.animating && this.anim){ + this.anim.stop(); + } + this.animating = true; + this.updateExpandIcon(); + var ct = this.ctNode; + var cs = ct.style; + cs.position = 'absolute'; + cs.visibility = 'hidden'; + cs.display = ''; + var h = ct.clientHeight; + cs.overflow = 'hidden'; + cs.height = '1px'; + cs.position = ''; + cs.visibility = ''; + var anim = new YAHOO.util.Anim(ct, { + height: {to: h} + }, this.node.ownerTree.duration || .25, YAHOO.util.Easing.easeOut); + anim.onComplete.subscribe(function(){ + cs.overflow = ''; + cs.height = ''; + this.animating = false; + this.anim = null; + if(typeof callback == 'function'){ + callback(); + } + }, this, true); + this.anim = anim; + anim.animate(); + }, + + highlight : function(){ + var tree = this.node.getOwnerTree(); + var hlColor = tree.hlColor || 'C3DAF9'; + var hlBaseColor = tree.hlBaseColor || 'FFFFFF'; + var anim = new YAHOO.util.ColorAnim(this.wrap, { + backgroundColor: {from: hlColor, to: hlBaseColor} + }, .75, YAHOO.util.Easing.easeNone); + anim.onComplete.subscribe(function(){ + YAHOO.util.Dom.setStyle(this.wrap, 'background-color', ''); + }, this, true); + anim.animate(); + }, + + collapse : function(){ + this.updateExpandIcon(); + this.ctNode.style.display = 'none'; + }, + + animCollapse : function(callback){ + if(this.animating && this.anim){ + this.anim.stop(); + } + this.animating = true; + this.updateExpandIcon(); + var ct = this.ctNode; + var cs = ct.style; + cs.height = ct.offsetHeight +'px'; + cs.overflow = 'hidden'; + var anim = new YAHOO.util.Anim(ct, { + height: {to: 1} + }, this.node.ownerTree.duration || .25, YAHOO.util.Easing.easeOut); + anim.onComplete.subscribe(function(){ + cs.display = 'none'; + cs.overflow = ''; + cs.height = ''; + this.animating = false; + this.anim = null; + if(typeof callback == 'function'){ + callback(); + } + }, this, true); + this.anim = anim; + anim.animate(); + }, + + getContainer : function(){ + return this.ctNode; + }, + + getEl : function(){ + return this.wrap; + }, + + appendDDGhost : function(ghostNode){ + ghostNode.appendChild(this.elNode.cloneNode(true)); + }, + + getDDRepairXY : function(){ + return YAHOO.util.Dom.getXY(this.iconNode); + }, + + onRender : function(){ + this.render(); + }, + + render : function(bulkRender){ + var n = this.node; + var targetNode = n.parentNode ? + n.parentNode.ui.getContainer() : n.ownerTree.container.dom; + if(!this.rendered){ + this.rendered = true; + var a = n.attributes; + + // add some indent caching, this helps performance when rendering a large tree + this.indentMarkup = ''; + if(n.parentNode){ + this.indentMarkup = n.parentNode.ui.getChildIndent(); + } + + var buf = ['
  • ', + '',this.indentMarkup,'', + '', + '', + '',n.text,'
    ', + '', + '
  • ']; + + if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){ + this.wrap = YAHOO.ext.DomHelper.insertHtml('beforeBegin', + n.nextSibling.ui.getEl(), buf.join('')); + }else{ + this.wrap = YAHOO.ext.DomHelper.insertHtml('beforeEnd', targetNode, buf.join('')); + } + this.elNode = this.wrap.childNodes[0]; + this.ctNode = this.wrap.childNodes[1]; + var cs = this.elNode.childNodes; + this.indentNode = cs[0]; + this.ecNode = cs[1]; + this.iconNode = cs[2]; + this.anchor = cs[3]; + this.textNode = cs[3].firstChild; + if(a.qtip){ + if(this.textNode.setAttributeNS){ + this.textNode.setAttributeNS('y', 'qtip', a.qtip); + if(a.qtipTitle){ + this.textNode.setAttributeNS('y', 'qtitle', a.qtipTitle); + } + }else{ + this.textNode.setAttribute('y:qtip', a.qtip); + if(a.qtipTitle){ + this.textNode.setAttribute('y:qtitle', a.qtipTitle); + } + } + } + this.initEvents(); + //this.renderIndent(); cached above now instead call updateExpandIcon + this.updateExpandIcon(); + }else{ + if(bulkRender === true) { + targetNode.appendChild(this.wrap); + } + } + }, + + getAnchor : function(){ + return this.anchor; + }, + + getTextEl : function(){ + return this.textNode; + }, + + getIconEl : function(){ + return this.iconNode; + }, + + updateExpandIcon : function(){ + if(this.rendered){ + var n = this.node; + var cls = n.isLast() ? "ytree-elbow-end" : "ytree-elbow"; + var hasChild = n.hasChildNodes(); + if(hasChild){ + cls += n.expanded ? '-minus' : '-plus'; + var c1 = n.expanded ? 'ytree-node-collapsed' : 'ytree-node-expanded'; + var c2 = n.expanded ? 'ytree-node-expanded' : 'ytree-node-collapsed'; + YAHOO.util.Dom.removeClass(this.elNode, 'ytree-node-leaf'); + YAHOO.util.Dom.replaceClass(this.elNode, c1, c2); + }else{ + YAHOO.util.Dom.replaceClass(this.elNode, 'ytree-node-expanded', 'ytree-node-leaf'); + } + this.ecNode.className = 'ytree-ec-icon '+cls; + } + }, + + getChildIndent : function(){ + if(!this.childIndent){ + var buf = []; + var p = this.node; + while(p){ + if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){ + if(!p.isLast()) { + buf.unshift(''); + } else { + buf.unshift(''); + } + } + p = p.parentNode; + } + this.childIndent = buf.join(''); + } + return this.childIndent; + }, + + renderIndent : function(){ + if(this.rendered){ + var indent = ''; + var p = this.node.parentNode; + if(p){ + indent = p.ui.getChildIndent(); + } + if(this.indentMarkup != indent){ // don't rerender if not required + this.indentNode.innerHTML = indent; + this.indentMarkup = indent; + } + this.updateExpandIcon(); + } + } +}; + +YAHOO.ext.tree.RootTreeNodeUI = function(){ + YAHOO.ext.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments); +}; +YAHOO.extendX(YAHOO.ext.tree.RootTreeNodeUI, YAHOO.ext.tree.TreeNodeUI); +YAHOO.ext.tree.RootTreeNodeUI.prototype.render = function(){ + if(!this.rendered){ + var targetNode = this.node.ownerTree.container.dom; + this.node.expanded = true; + targetNode.innerHTML = '
    '; + this.wrap = this.ctNode = targetNode.firstChild; + } +}; +YAHOO.ext.tree.RootTreeNodeUI.prototype.collapse = function(){ +}; diff --git a/frontend/beta/js/YUI-extensions/tree/TreePanel.js b/frontend/beta/js/YUI-extensions/tree/TreePanel.js new file mode 100644 index 0000000..202c0d0 --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/tree/TreePanel.js @@ -0,0 +1,213 @@ +YAHOO.namespace('ext.tree'); + +YAHOO.ext.tree.TreePanel = function(el, config){ + YAHOO.ext.tree.TreePanel.superclass.constructor.call(this); + this.el = getEl(el); + this.id = this.el.id; + YAHOO.ext.util.Config.apply(this, config || {}, { + rootVisible : true, + lines : true, + enableDD : false, + hlDrop : true/*, + hlColor: null, + ddGroup : 'TreeDD' + hlBaseColor : 'FFFFFF'*/ + + }); + YAHOO.ext.util.Config.apply(this.events, { + 'beforeload' : true, + 'load' : true, + 'textchange' : true, + 'beforeexpand' : true, + 'beforecollapse' : true, + 'expand' : true, + 'collapse' : true, + 'disabledchange' : true, + 'beforeclick':true, + 'click':true, + 'dblclick':true, + 'contentmenu':true, + 'beforechildrenrendered':true, + /** + * @event startdrag + * Fires when a node starts being dragged + * @param {YAHOO.ext.tree.TreePanel} this + * @param {YAHOO.ext.tree.TreeNode} node + * @param {event} e The raw browser event + */ + 'startdrag' : true, + /** + * @event enddrag + * Fires when a drag operation is complete + * @param {YAHOO.ext.tree.TreePanel} this + * @param {YAHOO.ext.tree.TreeNode} node + * @param {event} e The raw browser event + */ + 'enddrag' : true, + /** + * @event dragdrop + * Fires when a dragged node is dropped on a valid DD target + * @param {YAHOO.ext.tree.TreePanel} this + * @param {YAHOO.ext.tree.TreeNode} node + * @param {DD} dd The dd it was dropped on + * @param {event} e The raw browser event + */ + 'dragdrop' : true, + /** + * @event beforenodedrop + * Fires when a DD object is dropped on a node in this tree for preprocessing. This event can cancel. + * @param {Object} dropEvent + */ + 'beforenodedrop' : true, + /** + * @event nodedrop + * Fires after a DD object is dropped on a node in this tree + * @param {Object} dropEvent + */ + 'nodedrop' : true, + /** + * @event nodedragover + * Fires when a tree node is being target + * @param {Object} dragOverEvent + */ + 'nodedragover' : true + }); + if(this.singleExpand){ + this.on('beforeexpand', this.restrictExpand, this, true); + } + // problem with safari and animation + // I am investigating + if(YAHOO.ext.util.Browser.isSafari){ + this.animate = false; + } +}; +YAHOO.extendX(YAHOO.ext.tree.TreePanel, YAHOO.ext.data.Tree, { + restrictExpand : function(node){ + var p = node.parentNode; + if(p){ + if(p.expandedChild && p.expandedChild.parentNode == p){ + p.expandedChild.collapse(); + } + p.expandedChild = node; + } + }, + + setRootNode : function(node){ + YAHOO.ext.tree.TreePanel.superclass.setRootNode.call(this, node); + if(!this.rootVisible){ + node.ui = new YAHOO.ext.tree.RootTreeNodeUI(node); + } + return node; + }, + + getEl : function(){ + return this.el; + }, + + getLoader : function(){ + return this.loader; + }, + + expandAll : function(){ + this.root.expand(true); + }, + + collapseAll : function(){ + this.root.collapse(true); + }, + + getSelectionModel : function(){ + if(!this.selModel){ + this.selModel = new YAHOO.ext.tree.DefaultSelectionModel(); + } + return this.selModel; + }, + + expandPath : function(path, attr, callback){ + attr = attr || 'id'; + var keys = path.split(this.pathSeparator); + var curNode = this.root; + if(curNode.attributes[attr] != keys[1]){ // invalid root + if(callback){ + callback(false, null); + } + return; + } + var index = 1; + var f = function(){ + if(++index == keys.length){ + if(callback){ + callback(true, curNode); + } + return; + } + var c = curNode.findChild(attr, keys[index]); + if(!c){ + if(callback){ + callback(false, curNode); + } + return; + } + curNode = c; + c.expand(false, false, f); + } + curNode.expand(false, false, f); + }, + + selectPath : function(path, attr, callback){ + attr = attr || 'id'; + var keys = path.split(this.pathSeparator); + var v = keys.pop(); + if(keys.length > 0){ + var f = function(success, node){ + if(success && node){ + var n = node.findChild(attr, v); + if(n){ + n.select(); + if(callback){ + callback(true, n); + } + } + }else{ + if(callback){ + callback(false, n); + } + } + }; + this.expandPath(keys.join(this.pathSeparator), attr, f); + }else{ + this.root.select(); + if(callback){ + callback(true, this.root); + } + } + }, + + render : function(){ + this.container = this.el.createChild({tag:'ul', + cls:'ytree-root-ct ' + + (this.lines ? 'ytree-lines' : 'ytree-no-lines')}); + + if(this.containerScroll){ + YAHOO.ext.dd.ScrollManager.register(this.el); + } + + if((this.enableDD || this.enableDrop) && !this.dropZone){ + this.dropZone = new YAHOO.ext.tree.TreeDropZone(this, this.dropConfig || { + ddGroup: this.ddGroup || 'TreeDD' + }); + } + if((this.enableDD || this.enableDrag) && !this.dragZone){ + this.dragZone = new YAHOO.ext.tree.TreeDragZone(this, this.dragConfig || { + ddGroup: this.ddGroup || 'TreeDD', + scroll: this.ddScroll + }); + } + this.getSelectionModel().init(this); + this.root.render(); + if(!this.rootVisible){ + this.root.renderChildren(); + } + return this; + } +}); diff --git a/frontend/beta/js/YUI-extensions/tree/TreeSelectionModel.js b/frontend/beta/js/YUI-extensions/tree/TreeSelectionModel.js new file mode 100644 index 0000000..4fed88e --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/tree/TreeSelectionModel.js @@ -0,0 +1,195 @@ +YAHOO.ext.tree.DefaultSelectionModel = function(){ + this.selNode = null; + + this.events = { + 'selectionchange' : true + }; +}; + +YAHOO.extendX(YAHOO.ext.tree.DefaultSelectionModel, YAHOO.ext.util.Observable, { + init : function(tree){ + this.tree = tree; + tree.el.mon('keydown', this.onKeyDown, this, true); + tree.on('click', this.onNodeClick, this, true); + }, + + onNodeClick : function(node, e){ + this.select(node); + }, + + select : function(node){ + if(this.selNode && this.selNode != node){ + this.selNode.ui.onSelectedChange(false); + } + this.selNode = node; + node.ui.onSelectedChange(true); + this.fireEvent('selectionchange', this, node); + return node; + }, + + unselect : function(node){ + if(this.selNode == node){ + this.clearSelections(); + } + }, + + clearSelections : function(){ + var n = this.selNode; + if(n){ + n.ui.onSelectedChange(false); + this.selNode = null; + this.fireEvent('selectionchange', this, null); + } + return n; + }, + + getSelectedNode : function(){ + return this.selNode; + }, + + isSelected : function(node){ + return this.selNode == node; + }, + + onKeyDown : function(e){ + var s = this.selNode || this.lastSelNode; + // undesirable, but required + var sm = this; + if(!s){ + return; + } + var k = e.getKey(); + //alert(k) + switch(k){ + case e.DOWN: + e.preventDefault(); + if(s.firstChild && s.isExpanded()){ + this.select(s.firstChild, e); + }else if(s.nextSibling){ + this.select(s.nextSibling, e); + }else if(s.parentNode){ + s.parentNode.bubble(function(){ + if(this.nextSibling){ + sm.select(this.nextSibling, e); + return false; + } + }); + } + break; + case e.UP: + e.preventDefault(); + var ps = s.previousSibling; + if(ps){ + if(!ps.isExpanded()){ + this.select(ps, e); + }else{ + var lc = ps.lastChild; + while(lc && lc.isExpanded()){ + lc = lc.lastChild; + } + this.select(lc, e); + } + }else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){ + this.select(s.parentNode, e); + } + break; + case e.RIGHT: + e.preventDefault(); + if(s.hasChildNodes()){ + if(!s.isExpanded()){ + s.expand(); + }else if(s.firstChild){ + this.select(s.firstChild, e); + } + } + break; + case e.LEFT: + e.preventDefault(); + if(s.hasChildNodes() && s.isExpanded()){ + s.collapse(); + }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){ + this.select(s.parentNode, e); + } + break; + }; + } +}); + +YAHOO.ext.tree.MultiSelectionModel = function(){ + this.selNodes = []; + this.selMap = {}; + this.events = { + 'selectionchange' : true + }; +}; + +YAHOO.extendX(YAHOO.ext.tree.MultiSelectionModel, YAHOO.ext.util.Observable, { + init : function(tree){ + this.tree = tree; + tree.el.mon('keydown', this.onKeyDown, this, true); + tree.on('click', this.onNodeClick, this, true); + }, + + onNodeClick : function(node, e){ + this.select(node, e, e.ctrlKey); + }, + + select : function(node, e, keepExisting){ + if(keepExisting !== true){ + this.clearSelections(true); + } + this.selNodes.push(node); + this.selMap[node.id] = node; + this.lastSelNode = node; + node.ui.onSelectedChange(true); + this.fireEvent('selectionchange', this, this.selNodes); + return node; + }, + + unselect : function(node){ + if(this.selMap[node.id]){ + node.ui.onSelectedChange(false); + var sn = this.selNodes; + var index = -1; + if(sn.indexOf){ + index = sn.indexOf(node); + }else{ + for(var i = 0, len = sn.length; i < len; i++){ + if(sn[i] == node){ + index = i; + break; + } + } + } + if(index != -1){ + this.selNodes.splice(index, 1); + } + delete this.selMap[node.id]; + this.fireEvent('selectionchange', this, this.selNodes); + } + }, + + clearSelections : function(suppressEvent){ + var sn = this.selNodes; + if(sn.length > 0){ + for(var i = 0, len = sn.length; i < len; i++){ + sn[i].ui.onSelectedChange(false); + } + this.selNodes = []; + this.selMap = {}; + if(suppressEvent !== true){ + this.fireEvent('selectionchange', this, this.selNodes); + } + } + }, + + isSelected : function(node){ + return this.selMap[node.id] ? true : false; + }, + + getSelectedNodes : function(){ + return this.selNodes; + }, + + onKeyDown : YAHOO.ext.tree.DefaultSelectionModel.prototype.onKeyDown +}); diff --git a/frontend/beta/js/YUI-extensions/tree/TreeSorter.js b/frontend/beta/js/YUI-extensions/tree/TreeSorter.js new file mode 100644 index 0000000..9960703 --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/tree/TreeSorter.js @@ -0,0 +1,49 @@ +YAHOO.ext.tree.TreeSorter = function(tree, config){ + YAHOO.ext.util.Config.apply(this, config); + tree.on('beforechildrenrendered', this.doSort, this, true); + tree.on('append', this.updateSort, this, true); + tree.on('insert', this.updateSort, this, true); + + var dsc = this.dir && this.dir.toLowerCase() == 'desc'; + var p = this.property || 'text'; + var sortType = this.sortType; + var fs = this.folderSort; + var cs = this.caseSensitive === true; + + this.sortFn = function(n1, n2){ + if(fs){ + if(n1.leaf && !n2.leaf){ + return 1; + } + if(!n1.leaf && n2.leaf){ + return -1; + } + } + var v1 = sortType ? sortType(n1) : (cs ? n1[p] : n1[p].toUpperCase()); + var v2 = sortType ? sortType(n2) : (cs ? n2[p] : n2[p].toUpperCase()); + if(v1 < v2){ + return dsc ? +1 : -1; + }else if(v1 > v2){ + return dsc ? -1 : +1; + }else{ + return 0; + } + }; +}; + +YAHOO.ext.tree.TreeSorter.prototype = { + doSort : function(node){ + node.sort(this.sortFn); + }, + + compareNodes : function(n1, n2){ + + return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1); + }, + + updateSort : function(tree, node){ + if(node.childrenRendered){ + this.doSort.defer(1, this, [node]); + } + } +}; -- cgit v0.9.0.2