// ------------------------------------------------------------------------------------ // AJAX TREE PACKAGE // // TODO: Try to use JcmsJsonRequest // ------------------------------------------------------------------------------------ Ajax.Tree = { enableDragNDrop: true, dragdropArray: $A(new Array()), // ------------------------------------------------------------------------------------ // AJAX Function // ------------------------------------------------------------------------------------ _downloadChildrenHooks: $H(), /** * Allow other javascript code to register a custom callback * when a new tree branch is retrieved in ajax. */ registerDownloadChildrenHook: function(ajaxSuffix, customCallback) { var callbacks = Ajax.Tree._downloadChildrenHooks.get(ajaxSuffix); if (!callbacks) { callbacks = $A(); Ajax.Tree._downloadChildrenHooks.set(ajaxSuffix,callbacks); } callbacks.push(customCallback); }, invokeDownloadChildrenHook: function(ajaxSuffix, ul) { var callbacks = Ajax.Tree._downloadChildrenHooks.get(ajaxSuffix); if (!callbacks) { return; } callbacks.each(function(item) { item(ul); }); }, /** * Get HTML of children * - for the ID in the img.classname (ID_j_42) * - for the full tree with given checkedArray * * Update UL innerHTML with this new content. * Remove Image click function to toggleVisibility instead of calling a new Ajax call. * * @param img the clicked image * @param ajaxSuffix String that represents the main UL id * @param ul the root branch to fill * @param checkedArray a Array of checked category ids */ downloadChildren: function(img, ajaxSuffix, ul, checkedArray, openedArray, customCallback) { JcmsLogger.debug('TreeCat','downloadChildren():', ajaxSuffix, img, checkedArray, openedArray); // Init Json Request var jsonRequest = new JcmsJsonRequest(img); // Init RPC with jsonRequest var funcRPC = function(){ if (img){ Ajax.Tree._getRpcTree(ajaxSuffix).getChildren(jsonRequest.asyncJsonCallBack.bind(jsonRequest), $(img).getJcmsId()); } else { Ajax.Tree._getRpcTree(ajaxSuffix).getChildren(jsonRequest.asyncJsonCallBack.bind(jsonRequest),checkedArray,openedArray); } } // Init Callback with jsonRequest var funcCallback = function(returnValue){ if (!returnValue){ ul.parentNode.removeChild(ul); return; } // Clean Ajax.Tree._disposeUL(ul); Util.cleanDOMElements(ul,true); ul.innerHTML = returnValue; // Update DragDrop if($(ajaxSuffix).hasClassName('dragdrop') && Ajax.Tree.enableDragNDrop){ setTimeout(function(){ Ajax.Tree._initDragDrop(ul); },10); } // Call custom callback if (customCallback){ customCallback(); } Ajax.Tree.invokeDownloadChildrenHook(ajaxSuffix, ul); } // Init custom exception handle var funcException = function(){ ul.parentNode.removeChild(ul); Ajax.Tree.toggleOpenClose(img); } // Run JSON Request jsonRequest.rpc = funcRPC; jsonRequest.callback = funcCallback; jsonRequest.exception = funcException; jsonRequest.asyncJsonCall(); }, /** * Rename the given category using ajax call * Refresh the full category tree in ajax * * @param ajaxSuffix String that represents the main UL id * @param catId The JCMS Category id * @param value The new category name in current language */ rename: function(ajaxSuffix, catId, value){ JcmsLogger.debug('TreeCat','rename():', ajaxSuffix, catId, value); // Init Json Request var jsonRequest = new JcmsJsonRequest(); // Init RPC with jsonRequest callback var funcRPC = function(){ Ajax.Tree._getRpcTree(ajaxSuffix).rename(function(msg){ Ajax.Tree._handleRPCResponse(jsonRequest, msg, ajaxSuffix); }, catId, value); }; // Run JSON Request jsonRequest.rpc = funcRPC; jsonRequest.callback = Ajax.Tree._callbackRefresh; jsonRequest.asyncJsonCall(); }, /** * Add a new category using ajax call * Refresh the full category tree in ajax * * @param ajaxSuffix String that represents the main UL id * @param catId The JCMS Category id * @param value The category name to add in current language */ addSubCat: function(ajaxSuffix, catId, value){ JcmsLogger.debug('TreeCat','addSubCat():', ajaxSuffix, catId, value); // Init Json Request var jsonRequest = new JcmsJsonRequest(); // Init RPC with jsonRequest callback var funcRPC = function(){ Ajax.Tree._getRpcTree(ajaxSuffix).addSubCat(function(msg){ Ajax.Tree._handleRPCResponse(jsonRequest, msg, ajaxSuffix); },catId, value); }; // Run JSON Request jsonRequest.rpc = funcRPC; jsonRequest.callback = function(returnValue, returnEffect){ Ajax.Tree._callbackRefresh(returnValue, returnEffect, catId); }; jsonRequest.asyncJsonCall(); }, /** * Add a sibling category using ajax call * Refresh the full category tree in ajax * * @param ajaxSuffix String that represents the main UL id * @param catId The JCMS Category id * @param value The category name to add in current language */ addSiblingCat: function(ajaxSuffix, catId, value){ JcmsLogger.debug('TreeCat','addSiblingCat():', ajaxSuffix, catId, value); // Init Json Request var jsonRequest = new JcmsJsonRequest(); // Init RPC with jsonRequest callback var funcRPC = function(){ Ajax.Tree._getRpcTree(ajaxSuffix).addSiblingCat(function(msg){ Ajax.Tree._handleRPCResponse(jsonRequest, msg, ajaxSuffix); },catId, value); }; // Run JSON Request jsonRequest.rpc = funcRPC; jsonRequest.callback = function(returnValue, returnEffect){ Ajax.Tree._callbackRefresh(returnValue, returnEffect); }; jsonRequest.asyncJsonCall(); }, /** * Remove a category using ajax call * Refresh the full category tree in ajax * * @param ajaxSuffix String that represents the main UL id * @param catId The JCMS Category id */ remove: function(ajaxSuffix, catId){ JcmsLogger.debug('TreeCat','remove():', ajaxSuffix, catId); // Init Json Request var jsonRequest = new JcmsJsonRequest(); // Init RPC with jsonRequest callback var funcRPC = function(){ Ajax.Tree._getRpcTree(ajaxSuffix).remove(function(msg){ Ajax.Tree._handleRPCResponse(jsonRequest, msg, ajaxSuffix); },catId); }; // Run JSON Request jsonRequest.rpc = funcRPC; jsonRequest.callback = Ajax.Tree._callbackRefresh; jsonRequest.asyncJsonCall(); }, /** * Update the parent of a givent category * Refresh the full category tree in ajax * * @param ajaxSuffix String that represents the main UL id * @param catId The JCMS Category id */ setParent: function(ajaxSuffix, catId, parentId){ JcmsLogger.debug('TreeCat','setParent():', ajaxSuffix, catId, parentId); // Init Json Request var jsonRequest = new JcmsJsonRequest(); // Init RPC with jsonRequest callback var funcRPC = function(){ Ajax.Tree._getRpcTree(ajaxSuffix).setParent(function(msg){ Ajax.Tree._handleRPCResponse(jsonRequest, msg, ajaxSuffix); },catId, parentId); }; // Run JSON Request jsonRequest.rpc = funcRPC; jsonRequest.callback = function(returnValue, returnEffect){ Ajax.Tree._callbackRefresh(returnValue, returnEffect, parentId); }; jsonRequest.asyncJsonCall(); }, /** * Refresh an Ajax TreeCat * 1. Parse tree to record checked nodes. * 2. Remove all children of main branch. * 3. Retrieve a new Tree using Ajax.Tree.downloadChildren * 4. Update main branch * * If UL.TreeCat have className 'follow' then the tree will open (not select) * the given branch ids then follow the Ahref link. * * @param ajaxSuffix String that represents the main UL id * @param ids an array of ids to add to checked/open elements * or a single id (that will follow url after refresh) */ refresh: function(ajaxSuffix, ids, checked){ JcmsLogger.debug('TreeCat','refresh():',ajaxSuffix,ids); Ajax.setWaitState(true); var ul = $(ajaxSuffix); // Search checked categories var openedArray = new Array(); var checkedArray = new Array(); $A(ul.getElementsByTagName('INPUT')).each(function(elm, idx){ if (elm.checked){ checkedArray.push(elm.value); } }); // Search opened nodes $A(ul.getElementsBySelector('LI.open')).each(function(elm, idx){ var node = elm.down(); if (node){ var nodeCatId = $(node).getJcmsId(); openedArray.push(nodeCatId); JcmsLogger.debug('TreeCat', 'Opened:', nodeCatId); } }); // Add given additional checked ids if (ids){ openedArray = openedArray.concat(ids); if (checked && !ul.hasClassName('follow')){ checkedArray = checkedArray.concat(ids); } } // Removing all children Ajax.Tree._disposeUL(ul); Util.cleanDOMElements(ul,true); // Append message ul.innerHTML = "
  • Loading...
  • "; // Prepare custom callback follow var customCallback = null; if (ul.hasClassName('follow') && ids && !(ids instanceof Array)){ customCallback = function(){ Ajax.Tree._followRefreshCallback(ajaxSuffix, ids); } } // Dowload children Ajax.Tree.downloadChildren(null, ajaxSuffix, ul, checkedArray, openedArray, customCallback); }, /** * Called by refresh() for Treecat with class 'follow' * @param ajaxSuffix String that represents the main UL id * @param ids the id to work with to find URL to follow */ _followRefreshCallback: function(ajaxSuffix, ids){ var elms = $(ajaxSuffix).select('IMG.ID_'+ids); if (!elms || !elms[0]){ JcmsLogger.warn('TreeCat','Id not found',ids); return; } var img = elms[0]; var ahref = img.next('A'); if (ahref){ document.location = ahref.href; } }, // ------------------------------------------------------------------------------------ // Utility Function // ------------------------------------------------------------------------------------ /** * Makes AJAX call to import all the children * for the given img LI and given AJAX suffix. * * @param img the clicked image */ _importChildren: function(img) { var li = $(img.parentNode); if (li.hasClassName('imported')) { return; } Ajax.setWaitState(true,img); // Set wait icon var ul = document.createElement('UL'); ul.innerHTML = "
  • Loading...
  • "; li.appendChild(ul); // Mark this branch as imported li.addClassName('imported'); // Asynchronous call var ajaxSuffix = $(img).fastUp('UL', 'TreeCat').id; Ajax.Tree.downloadChildren(img, ajaxSuffix, ul); }, /** * Toggles className 'open' and 'close' on parent LI of image. * * @param img the clicked image */ toggleOpenClose: function(img) { var li = $(img.parentNode); li.toggleClassName('close'); li.toggleClassName('open'); Ajax.Tree._importChildren(img); }, /** * Returns the ajaxSuffix of the parent UL of * the given element. * * @param the children element */ getAjaxSuffix: function(elm){ if (!elm){ return; } var elm = $(elm); // Hook for an element handle click as a proxy if (elm.id && elm.id.indexOf('proxy') >= 0){ return elm.id.substring(6); } var ul = elm.fastUp('UL', 'TreeCat'); if (!ul){ return; } JcmsLogger.debug('TreeCat','getAjaxSuffix():', ul.id); return ul.id; }, /** * Returns the category id for the given element * * @param elm the element to work with */ getCategoryId: function(elm){ if (!elm){ return; } if (elm.tagName == 'LI'){ return Ajax.Tree.getCategoryId(elm.down(0)); } else if (elm.tagName == 'IMG'){ return $(elm).getJcmsId(); } else{ return Ajax.Tree.getCategoryId(elm.up('UL.TreeCat LI')); } }, // ------------------------------------------------------------------------------------ // Internal Function // ------------------------------------------------------------------------------------ /** * Returns the correct JSON-RPC AjaxTree from given ajaxSuffix * * @param ajaxSuffix the ajaxSuffix or null */ _getRpcTree: function(ajaxSuffix){ if (!ajaxSuffix){ return JcmsJsContext.getJsonRPC().AjaxTree; } else { return JcmsJsContext.getJsonRPC()['AjaxTree'+ajaxSuffix]; } }, /** * Convenient callback function called to refresh * treecat after json-rpc call * * @param ajaxSuffix the ajaxsuffix used to refresh tree * @param returnEffect the Effect (not used) */ _callbackRefresh: function(ajaxSuffix, returnEffect, openId){ JcmsLogger.debug('TreeCat','Callback Refresh',ajaxSuffix,returnEffect,openId); if (ajaxSuffix){ if (openId){ var ids = new Array(); ids.push(openId); Ajax.Tree.refresh(ajaxSuffix, ids); } else { Ajax.Tree.refresh(ajaxSuffix); } } }, /** * Convenient function used to handle RPC reponse. * If RPC returns a message then call alert and finish JsonRequest job * else finish JsonRequest Job with the given return value. * * @param msg the error message * @param returnValue the value to return if there is no errors */ _handleRPCResponse: function(jsonRequest, msg, returnValue){ if (msg){ alert(msg); jsonRequest.asyncJsonCallBack(); return; } jsonRequest.asyncJsonCallBack(returnValue); }, /** * Init Sortable on TreeCat */ _initTreeCat: function(){ JcmsLogger.info('TreeCat','Init TreeCat'); if (!Ajax.Tree.enableDragNDrop){ return; } $$('UL.TreeCat').each(function(elm, idx){ if (elm.hasClassName('dragdrop')){ Ajax.Tree._initDragDrop(elm); } }); }, /** * Clean the cached dragdropArray */ dispose: function(){ // Because underlaying function only dispose DrangNDrop if (!Ajax.Tree.enableDragNDrop){ return; } $$('UL.TreeCat').each(function(elm, idx){ Ajax.Tree._disposeUL(elm); }); Ajax.Tree.dragdropArray.clear(); }, /** * Clean LI items * * @param li the li to clean */ _disposeLI: function(li){ // Remove previous draggable if (li.draggable){ li.draggable.destroy(); li.draggable = null; } // Remove previous droppable Droppables.remove(li); }, /** * Remove all drag/drop object bind to LIs under given UL * * @param ul the ul element to work with */ _disposeUL: function(ul){ var ul = $(ul); // Remove LI Drag/Drop $A(ul.getElementsByTagName('LI')).each(function(li,idx){ Ajax.Tree._disposeLI(li); Ajax.Tree.dragdropArray.splice(idx,1); }); // Remove onclick events $A(ul.getElementsByTagName('A')).each(function(ahref,idx){ ahref.onclick = null; }); }, /** * Inits all drag/drop object bind to LIs under given UL * * @param ul the ul element to work with */ _initDragDrop: function(ul){ var ul = $(ul); $A(ul.getElementsByTagName('LI')).each(function(li,idx){ var li = $(li); var anchor = li.down('IMG.visual'); Event.observe(anchor,'mousedown',Ajax.Tree._lazyDrag); // Init Droppable Droppables.remove(li); Droppables.add(li,{greedy:false, onHover:Ajax.Tree._onHover, onDrop:Ajax.Tree._onDrop}); // Cache LI's for clean purpose Ajax.Tree.dragdropArray.push(li); }); }, /** * Used by _initDragDrop() to lazy initialise draggable onmousedown * @param event the mousedown event */ _lazyDrag: function(event){ var anchor = Event.element(event); JcmsLogger.debug('TreeCat','_lazyDrag',anchor); // Destroy previous Draggable var li = anchor.fastUp('LI'); if (li.draggable){ li.draggable.destroy(); } // Init new Draggable li.draggable = new Draggable(li,{revert: true, handle:'visual'}); // Remove observer Event.stopObserving(anchor,'mousedown',Ajax.Tree._lazyDrag); // Kick start dnd li.draggable.initDrag(event); Draggables.updateDrag(event); }, /** * Convenient internal function to stop Drag/Drop events * * @param event the event */ _stopEvent: function(event){ Event.stop(event); }, /** * Internal function called by _initDragDrop() * * @param dragElm the object that encapsulate the real element */ _onChange: function(dragElm){ // Skip quickly if (!dragElm.element.dragObserver){ JcmsLogger.debug('TreeCat','Start dragObserver'); Event.observe(dragElm.element, 'click', Ajax.Tree._stopEvent); dragElm.element.dragObserver = true; } }, /** * Internal function called by _initDragDrop() * * @param dragElm Dragged element * @param dropElm Dropped element * @param overlap Percetage of overlaping */ _onHover: function(dragElm, dropElm, overlap){ // Skip quickly if (dropElm.className.indexOf('droppable') >= 0){ return; } // Remove previous droppable if item changes if (dragElm.oldDropElm && dragElm.oldDropElm != dropElm){ dragElm.oldDropElm.removeClassName('droppable'); } // Do not highlight it's own parent // if (dragElm.up('LI') == dropElm){ // return; // } // Add class name dropElm.addClassName('droppable'); dragElm.oldDropElm = dropElm; }, /** * Internal function called by _initDragDrop() * * @param dragElm Dragged element * @param dropElm Dropped element * @param event the Drag/Drop event */ _onDrop: function(dragElm, dropElm, event){ // Stop event handler // JcmsLogger.debug('TreeCat','Stop dragObserver'); // Event.stopObserving(dragElm, 'click', Ajax.Tree._stopEvent); // Remove previous droppable if item changes if (dragElm.oldDropElm){ dragElm.oldDropElm.removeClassName('droppable'); } // Do not work on parent element if (dragElm.fastUp('LI') === dropElm){ return; } // Ask question var dnd = top.confirm(I18N.glp('msg.confirm.dragdrop')); // Make AJAX Call if (dnd){ dragElm.hide(); // Hide element Ajax.Tree.setParent(Ajax.Tree.getAjaxSuffix(dragElm), Ajax.Tree.getCategoryId(dragElm), Ajax.Tree.getCategoryId(dropElm)); } } } // ------------------------------------------------------------------------------------ // EVENT OBSERVER // ------------------------------------------------------------------------------------ Event.observe(window, 'load' , function() { Ajax.Tree._initTreeCat();} ); if (navigator.appVersion.match(/\bMSIE\b/)){ Event.observe(window, 'unload', function() { Ajax.Tree.dispose(); }, false); }