/** * LeftAndMain_left.js * Code for supporting the left-hand panel of all the 2-pane admin windows * This includes code for the action panel at the top, and the draggable, ajax-linked tree. */ if(typeof SiteTreeHandlers == 'undefined') SiteTreeHandlers = {}; SiteTreeHandlers.parentChanged_url = 'admin/ajaxupdateparent'; SiteTreeHandlers.orderChanged_url = 'admin/ajaxupdatesort'; SiteTreeHandlers.showRecord_url = 'admin/show/'; SiteTreeHandlers.controller_url = 'admin'; var _HANDLER_FORMS = { addpage : 'Form_AddPageOptionsForm', deletepage : 'deletepage_options', sortitems : 'sortitems_options' }; /** * Overload this with a real context menu if necessary */ var TreeContextMenu = null; /** * Extra methods for the tree when used in the LHS of the CMS */ TreeAPI = Class.create(); TreeAPI.prototype = { /** * Perform the given code on the each tree node with the given index. * There could be more than one :-) * @param idx The index of the tree node * @param code A method to be executed, that will be passed the tree node */ performOnTreeNode: function(idx, code) { var treeNode = this.getTreeNodeByIdx(idx); if(!treeNode) return; if(treeNode.className.indexOf('manyparents') == -1) { code(treeNode); } else { var i,item,allNodes = this.getElementsByTagName('li'); for(i=0;item=allNodes[i];i++) { if(treeNode.id == item.id) code(item); } } }, getTreeNodeByIdx : function(idx) { if(!idx) idx = "0"; var node = document.getElementById('record-' + idx); if(idx == "0" && !node) node = document.getElementById('record-root'); return node; }, getIdxOf : function(treeNode) { if(treeNode && treeNode.id && treeNode.id.match(/record-([0-9a-zA-Z\-]+)$/)) return RegExp.$1; }, createTreeNode : function(idx, title, pageType) { var i; var node = document.createElement('li'); node.id = 'record-' + idx; node.className = pageType; var aTag = document.createElement('a'); aTag.href = SiteTreeHandlers.showRecord_url + idx; aTag.innerHTML = title; node.appendChild(aTag); SiteTreeNode.create(node, this.options); return node; }, setNodeIdx : function(idx, newIdx) { this.performOnTreeNode(idx, function(treeNode) { treeNode.id = 'record-' + newIdx; var aTag = treeNode.getElementsByTagName('a')[0]; aTag.href = aTag.href.replace(idx, newIdx); }); var treeNode = this.getTreeNodeByIdx(idx); }, setNodeTitle : function(idx, title) { this.performOnTreeNode(idx, function(treeNode) { var aTag = treeNode.getElementsByTagName('a')[0]; aTag.innerHTML = title; }); }, setNodeIcon: function(idx, oldClassName, newClassName) { this.performOnTreeNode(idx, function(treeNode) { treeNode.className = treeNode.className.replace(oldClassName,newClassName); treeNode.aSpan.className = 'a ' + treeNode.className.replace('closed','spanClosed'); treeNode.setIconByClass(); }); }, setCurrentByIdx : function(idx) { if(this.selected) { var i,item; for(i=0;item=this.selected[i];i++) { item.removeNodeClass('current'); } } __tree = this; __tree.selected = []; this.performOnTreeNode(idx, function(treeNode) { __tree.selected.push(treeNode); treeNode.expose(); treeNode.addNodeClass('current'); }); }, changeCurrentTo : function(newNode) { if(this.selected) { var i,item; for(i=0;item=this.selected[i];i++) { item.removeNodeClass('current'); } } newNode.addNodeClass('current'); this.selected = [newNode]; newNode.expose(); }, firstSelected : function() { if(this.selected) return this.selected[0]; } }; /** * Extra methods for the tree node when used in the LHS of the CMS */ TreeNodeAPI = Class.create(); TreeNodeAPI.prototype = { selectTreeNode : function() { if(this.getElementsByTagName('a')[0].href) { _AJAX_LOADING = true; if($('sitetree').notify('SelectionChanged', this)) { autoSave(true, this.getPageFromServer.bind(this)); } } }, getPageFromServer : function() { $('Form_EditForm').getPageFromServer(this.id.replace('record-',''), this); _AJAX_LOADING = false; }, ajaxExpansion : function() { this.addNodeClass('loading'); var ul = this.treeNodeHolder(false); ul.innerHTML = 'loading...'; // Any attempts to add children to this page should, in fact, cue them up for insertion later ul.cuedNewNodes = []; ul.appendTreeNode = function(node) { this.cuedNewNodes[this.cuedNewNodes.length] = node; } new Ajax.Request(SiteTreeHandlers.loadTree_url + '&ajax=1&ID=' + this.getIdx(), { onSuccess : this.installSubtree.bind(this), onFailure : this.showSubtreeLoadingError }); }, showSubtreeLoadingError: function(response) { errorMessage('error loading subtree', response); }, /** * Context menu */ oncontextmenu: function(event) { if(TreeContextMenu) { if(!event) event = window.event; createContextMenu(event, this, TreeContextMenu); Event.stop(event); return false; } } } /** * In the case of Tree & DraggableTree, the root tree and the sub-trees all use the same class. * In this case, however, SiteTree has a much bigger API and so SiteSubTree is smaller. */ SiteSubTree = Class.extend('Tree').extend('TreeAPI'); SiteSubTree.prototype = { castAsTreeNode: function(li) { behaveAs(li, SiteTreeNode, this.options); } } /** * Our SiteTree class extends the tree object with a richer manipulation API. * The server will send a piece javascript that uses these functions. In this way, the server * has flexibility over its operation, but the Script->HTML interface is kept client-side. */ SiteTree = Class.extend('SiteSubTree'); SiteTree.prototype = { initialize : function() { this.Tree.initialize(); /* if(!this.tree.selected) this.tree.selected = []; var els = this.getElementsByTagName('li'); for(var i=0;i -1) { this.tree.selected.push(els[i]); break; } */ this.observeMethod('SelectionChanged', this.interruptLoading.bind(this) ); }, destroy: function () { if(this.Tree) this.Tree.destroy(); this.Tree = null; this.SiteSubTree = null; this.TreeAPI = null; this.selected = null; }, /** * Stop the currently loading node from loading. */ interruptLoading: function( newLoadingNode ) { if( this.loadingNode ) this.loadingNode.removeNodeClass('loading'); this.loadingNode = newLoadingNode; } } SiteTreeNode = Class.extend('TreeNode').extend('TreeNodeAPI'); SiteTreeNode.prototype = { initialize: function(options) { this.TreeNode.initialize(options); if(this.className && this.className.match(/ *([^ ]+)( +|$)/)) { if((typeof siteTreeHints != 'undefined') && siteTreeHints[ RegExp.$1 ]) { this.hints = siteTreeHints[ RegExp.$1 ]; this.dropperOptions = { accept : (this.hints.allowedChildren && (this.className.indexOf('nochildren') == -1)) ? this.hints.allowedChildren : 'none' }; } } if(this.className.indexOf('current') > -1) { if(!this.tree.selected) this.tree.selected = []; this.tree.selected.push(this); } if(!this.hints) this.hints = {} }, destroy: function () { if(this.TreeNode) this.TreeNode.destroy(); this.TreeNode = null; this.TreeNodeAPI = null; }, castAsTree: function(childUL) { behaveAs(childUL, SiteSubTree, this.options); if(this.draggableObj) childUL.makeDraggable(); }, onselect: function() { this.selectTreeNode(); return false; }, /** * Drag'n'drop handlers - Ajax saving */ onParentChanged : function(node, oldParent, newParent) { if(newParent.id.match(/^record-new/)) { alert("You must save the page before dragging children into it"); return false; } if( node == newParent || node.getIdx() == newParent.getIdx() ) { alert("You cannot add a page to itself"); return false; } if(node.innerHTML.toLowerCase().indexOf(' -1) { alert("You can't moved deleted pages"); return false; } if( Element.hasClassName( newParent, 'nochildren' ) ) { alert("You can't add children to that node"); return false; } var currentlyOpenPageID = 0; if($('Form_EditForm').elements.ID) currentlyOpenPageID = $('Form_EditForm').elements.ID.value; statusMessage('saving...', '', true); new Ajax.Request(SiteTreeHandlers.parentChanged_url, { method : 'post', postBody : 'ID=' + node.getIdx() + '&ParentID=' + newParent.getIdx() + '&CurrentlyOpenPageID=' + currentlyOpenPageID, onSuccess : Ajax.Evaluator, onFailure : function(response) { errorMessage('error saving parent', response); } }); return true; }, /** * Called when the tree has been resorted * nodeList is a list of all the nodes in the correct rder * movedNode is the node that actually got moved to trigger this resorting */ onOrderChanged : function(nodeList, movedNode) { statusMessage('saving...', '', true); var i, parts = Array(); sort = 0; for(i=0;i' + response.responseText); } }); return false; } }