// ---------------------------------------------------- // AJAX COMMON JSON-OBJECT PACKAGE // ---------------------------------------------------- /** * Usage: * ----- * * // Init Json Request * var jsonRequest = new JcmsJsonRequest(); * * // Init RPC with jsonRequest callback * var funcRPC = function(){ * // Do stuff * jsonRequest.asyncJsonCallBack(value); * * // Or call JSON method * JcmsJsContext.getJsonRPC().callRPCMethod(jsonRequest.asyncJsonCallBack(value).bind(jsonRequest), param1, param2); * } * * // Init CallBack * var funcCallBack = function(returnValue, returnEffect){ * // Do stuff * } * * // Init Effect (Optional) * var functEffect = function(){ * // Do Stuff * jsonRequest.asyncEffectCallBack(effect); * } * * jsonRequest.rpc = funcRPC; * jsonRequest.callback = funcCallBack; * jsonRequest.effect = functEffect; * jsonRequest.asyncJsonCall(); * * Note: * - jsonRequest.exception = funcException; // Handle custom rpc exception. * - jsonRequest.waitState = false; // Do not display wait state * - jsonRequest.asyncJsonCallPeriodical(10); // Periodical RPC * - jsonRequest.timeout = 10000; // Request timeout */ JcmsJsonRequest = Class.create(); JcmsJsonRequest.prototype = { initialize: function(elm) { this.elm = elm; this.effect = null; this.exception = null; this.callback = null; this.rpc = null; this._periodexec = null; this.waitState = true; // Display wait state this.timeout = 20000; this.isOk = false; this.isDone = false; this.isUpdate = false; this.isEffect = false; this.isTimeout = false; }, asyncJsonCall: function(){ // Init request status this.isOk = false; this.isDone = false; this.isUpdate = false; this.isEffect = false; this.isTimeout = false; // Set browser to waiting mode if (this.waitState){ Ajax.setWaitState(true,this.elm); } try{ // Call Effect if (this.effect) this.effect(); else this.isEffect = true; // Call Timeout if (this._timeoutFunc){ clearTimeout(this._timeoutFunc); } this._timeoutFunc = setTimeout(function(){ this.isTimeout = true; this._asyncResponseCallBack(); }.bind(this),this.timeout); // Call Json if (this.rpc) this.rpc(); } catch(ex){ this._handleException(); } }, asyncJsonCallPeriodical: function(frequency){ // Todo: Should check if PeriodicalExecuter is running twice ? this._periodexec = new PeriodicalExecuter( (function(){ if (this.isDone) return; this.asyncJsonCall(); }).bind(this), frequency); }, stopPeriodical: function(){ if (this._periodexec){ this._periodexec.stop(); } this.dispose(); }, /** * Must be Called by given function JSON * @param value the return value */ asyncJsonCallBack: function(value, exception){ if (value){ this.returnValue = value; } else{ this.isOk = true; } // Handle server exception if (exception){ this.returnException = exception; } // Response is done this.isDone = true; // Call response callback this._asyncResponseCallBack(); if (this._periodexec){ // Reset response for PeriodicalUpdater this.isDone = false; } }, /** * Must be Called by given function Effect * @param effect the running effect */ asyncEffectCallBack: function(effect){ // Effect is finished this.isEffect = true; this.workingEffect = effect; // Call response callback this._asyncResponseCallBack(); }, /** * Called by both Effect or JSON to do the * real callbaack */ _asyncResponseCallBack: function(){ // Handle Timeout if (this.isTimeout){ if (!this._timeoutFunc){ return; } this._handleException(); return; } if (!this.isDone) return; if (this.isUpdate) return; if (!this.isEffect) return; // Handle Exception if (this.returnException){ JcmsLogger.error('JcmsJsonRequest', this.returnException.message, ' Error: ['+this.returnException.code+']', this.returnException.name , '\n'+this.returnException.javaStack); this._handleException(); return; } if (this._timeoutFunc) clearTimeout(this._timeoutFunc); this.isUpdate = true; // Call callback try { if (this.callback){ this.callback(this.returnValue, this.workingEffect); } } catch (ex){ /* Trap CallBack error to clean JsonRequest */ JcmsLogger.error('JcmsJsonRequest',ex); } this._disposeResponse(); }, _disposeResponse: function(){ // Remove browser from waiting mode if (this.waitState){ Ajax.setWaitState(false,this.elm); } // Clean stuff if (!this._periodexec){ this.dispose(); } }, _handleException: function(){ alert(I18N.glp('warn.json.sessiontimeout')); if (this.exception){ this.exception(this.returnException); } this._disposeResponse(); }, /** * Clean pointer to reduce memory leaks * and helps Garbage Collector */ dispose: function(){ this.elm = null; this.effect = null; this.exception = null; this.callback = null; this.rpc = null; this._timeoutFunc= null; this._periodexec = null; this.timeout = 20000; } }; // ---------------------------------------------------- // AJAX COMMON PACKAGE // ---------------------------------------------------- if (!window.Ajax) { var Ajax = new Object(); } Object.extend(Ajax,{ version: '$Revision: 20046 $', // ---------------------------- // Common usefull AJAX Method // ---------------------------- setWaitState: function(wait, elm){ var wnd = parent ? parent : window; var doc = parent ? parent.document : document; var body = doc.getElementsByTagName('body')[0]; if (!Ajax.waitDiv){ Ajax.waitDiv = doc.createElement('DIV'); Ajax.waitDiv.innerHTML = I18N.glp('info.msg.loading'); Ajax.waitDiv.className = 'ajaxwait'; Ajax.waitDiv.style.display = 'none'; body.appendChild(Ajax.waitDiv); } // Set wait cursor if (wait){ if (elm) elm.style.cursor = 'wait'; Ajax.waitDiv.style.display = 'block'; body.style.cursor = 'wait'; wnd.status = I18N.glp('info.msg.loading'); } else{ // Reset cursor if (elm) elm.style.cursor = ''; Ajax.waitDiv.style.display = 'none'; body.style.cursor = ''; wnd.status=''; } } }); /** * Register AJAX Responder to handle Prototype AJAX Error Requests */ Ajax.Responders.register({ onException: function(){ alert(I18N.glp('warn.json.sessiontimeout')); } }); // ---------------------------------------------------- // INPUT UTIL // ---------------------------------------------------- if (!window.InputUtil) { var InputUtil = new Object(); } Object.extend(InputUtil,{ /** * Return an object reprensenting a cross-browser selection * @param input the working input * @param trim indicate to trim or not selection */ getSelection: function(input, trim){ var inputSelection = new Object(); inputSelection.input = input; // Gecko browser if (input.setSelectionRange) { inputSelection.start = input.selectionStart; inputSelection.end = input.selectionEnd; inputSelection.value = input.value.substring(input.selectionStart, input.selectionEnd); inputSelection.gecko = true; inputSelection.scrolltop = input.scrollTop; inputSelection.scrollleft = input.scrollLeft; } // Internet Explorer else if (input.createTextRange) { if (! input.ownerDocument) { input.ownerDocument = document; } var selection = input.ownerDocument.selection.createRange(); if ( selection.parentElement().tagName!="TEXTAREA" ) { inputSelection.start = 0; inputSelection.end = 0; } // Insert a text to find caret position else if (selection.text.length == 0) { var backup = input.value ; var bookmark = "~JCMSwiki~"; selection.text = bookmark ; var index = input.value.search( bookmark ); input.value = backup ; index -= input.value.substr(0,index).split('\n').length - 1; inputSelection.value = ""; InputUtil._setSelection(inputSelection,index,index); // Update selection inputSelection.start = index; inputSelection.end = index; } // Check selected text else { var bookmark = selection.getBookmark(); var range = input.createTextRange(); var selectionRange = input.createTextRange(); var fulltext = range.text; // Select and retrieve selected text selectionRange.moveToBookmark(bookmark); var text = selectionRange.text; // Retrieve text before selection range.setEndPoint('EndToStart', selectionRange); var start = range.text.length; // Find the real location of text var index = fulltext.indexOf(text,start); // Find the end location of text var end = index + text.length; // Count \n in text before index var startLines = fulltext.substring(0,index).split('\n').length - 1; // Count \n in text before ends of selection var endLines = startLines + text.split('\n').length - 1; // Update start and end postion start = index - startLines; end = end - endLines; // Backup selection inputSelection.start = start; inputSelection.end = end; inputSelection.value = selectionRange.text; // Reset selection InputUtil._setSelection(inputSelection,start,end); } } // Logger if (JcmsLogger.isDebug && JcmsLogger.InputUtil){ JcmsLogger.debug('InputUtil',"getSelection: "+inputSelection.start+","+inputSelection.end+": "+inputSelection.value+"("+inputSelection.scrolltop+","+inputSelection.scrollleft+")"); } return trim ? InputUtil._trimSelection(inputSelection) : inputSelection; }, /** * Replace the current selection by the given string. * If called by internal method the third parameter is selection (avoid to compute twice) * @param input the working input * @param replace the text to replace * @param trim indicate to trim or not selection * @param selection the object return by getSelection() set by internal method call */ replaceSelection: function(input, replace, trim, selection){ input.focus(); var selection = selection || InputUtil.getSelection(input,trim); var inputValue = input.value; if (selection.gecko){ input.value = inputValue.substring(0, selection.start) + replace + inputValue.substring(selection.end); var oldLength = selection.value ? selection.value.length : 0; InputUtil._setSelection(selection, selection.start, selection.end + (replace.length-oldLength)); } else{ var range = input.ownerDocument.selection.createRange(); var isCollapsed = (range.text.length == 0); range.text = replace; if (!isCollapsed){ // Make a better selection var rlen = replace.length - (replace.split('\n').length - 1); range.moveStart('character', -rlen); range.select(); } } }, /** * Check the current selection if it match "match" then * replace it by "replace" otherwise replace it by "backward" * @param input the working input * @param match a regular expression to check * @param backward the txt to replace if regexp doesn't match (work on .+) * @param replace the txt to replace if regexp match * @param trim true to trim selection * @param caret true to work even if there is no selection (use backward without $1) * @param selection the object return by getSelection() set by internal method call */ replaceRegexp: function(input, match, replace, backward, trim, caret, selection){ var selection = selection || InputUtil.getSelection(input,trim); var selectText = selection.value; if (!selectText && !caret) return; var replaceText = selectText; if (!selectText){ replaceText = backward.replace(/\$1/g,''); } else if (selectText.match(match)){ replaceText = selectText.replace(match,replace); } else { replaceText = selectText.replace(/\s*([\S ]+)(\s*)/g,backward+"$2"); } if (replaceText == selectText) return; InputUtil.replaceSelection(input, replaceText, trim, selection); }, /** * Selects the selection from start to end * @param selection the object return by getSelection() set by internal method call * @param start the new selection start point * @param end the new selection end point */ _setSelection: function(selection, start, end){ selection.input.focus(); if (selection.gecko){ // Set caret selection.input.setSelectionRange(start, start); // Fix Scrollbar if (selection.scrollleft){ selection.input.scrollLeft = selection.scrollleft; } if (selection.scrolltop){ selection.input.scrollTop = selection.scrolltop; } else{// Fake event 'esc' key to set scroll var evt = document.createEvent('KeyEvents'); evt.initKeyEvent('keypress',true,true,window,false ,false,false,false,27,null); selection.input.dispatchEvent(evt); } selection.input.setSelectionRange(start, end); } else if (selection.input.createTextRange){ var range = selection.input.createTextRange(); var text = selection.input.value; range.move('character', start); range.moveEnd('character', end-start); range.select(); } var substart = start-selection.start; var subend = selection.value.length - (selection.end-end); selection.value = selection.value.substring(substart, subend); selection.start = start; selection.end = end; }, /** * Trims white space in selection * and adjsute to new size * @param selection the object return by getSelection() set by internal method call */ _trimSelection: function(selection){ if (selection.start >= selection.end){ return selection; } // Trim spaces var start = 0; var end = 0; while (selection.value.charAt(start) == " ") { start++; } while (selection.value.charAt(selection.value.length-end-1) == " ") { end++; } if ((start ==0) && (end ==0)){ return selection; } // Adjuste new selection area InputUtil._setSelection(selection, selection.start+start, selection.end-end); return selection; } }); // ---------------------------------------------------- // FORM UTIL // ---------------------------------------------------- if (!window.FormUtil) { var FormUtil = new Object(); } Object.extend(FormUtil,{ // disable or enable all the input of the given array, by finding them in the // given form name according to the boolean toggleInputs: function(formName, inputsArray, enable) { $A(inputsArray).each(function(str, idx) { Form.getInputs(formName, false, str).each(function(element) { element.disabled = !enable; }); }); }, // Sets some values of text input in the given form, // using a map of key/value (key being the input name, value // the new value for the input) setInputValues: function(formName, inputToValueMap) { Form.getInputs(formName, 'text', false).each(function(element) { var newValue = inputToValueMap[element.name]; if (newValue != undefined) { element.value = newValue; } }); } }); // ---------------------------------------------------- // JCMSPREFS OBJECT // ---------------------------------------------------- if (!window.JcmsPrefs) { var JcmsPrefs = new Object(); } Object.extend(JcmsPrefs,{ items: null, put: function(key,value){ JcmsPrefs._init(); JcmsPrefs.items[key] = value; JcmsPrefs._store(); }, get: function(key,defaultValue){ JcmsPrefs._init(); if (JcmsPrefs.items[key] != undefined){ return JcmsPrefs.items[key]; } return defaultValue; }, _init: function(){ // Escape if nothing if (JcmsPrefs.items){ return; } // Init cookie value var cookieString = Cookie.get('JcmsPrefs'); if (!cookieString){ JcmsPrefs.items = new Object(); return; } // Unmarshal JSON Value eval("JcmsPrefs.items = "+cookieString); }, _store: function(){ if (!JcmsPrefs.items){ return; } var cookieString = toJSON(JcmsPrefs.items); //alert(cookieString); Cookie.write([{ name: 'JcmsPrefs', value: cookieString, expires: new Date((new Date()).getTime() + (1000 * 60 * 60 * 24 * 30)), // 30 days path: '/', domain: '' }]); } }); // ---------------------------------------------------- // UTIL OBJECT // ---------------------------------------------------- // Util 'static Class' if (!window.Util) { var Util = new Object(); } Object.extend(Util,{ /** * Convert the given object in to a boolean value, or use * default value if object cannot be converted */ toBoolean: function(object, defaultValue) { if (typeof object == 'boolean') { return object; } if (object == 'false' || object == 'no' ) { return false; } if (object == 'true' || object == 'yes') { return true; } return defaultValue; }, /** * Remove all child nodes under the given element. * Current implementation call: removeChild() recursively. * * @param parentElm the root element to work with * @param deep should this method called recursively */ cleanDOMElements: function(parentElm, deep){ var children = parentElm.childNodes; if (!children){ return; } $A(children).each(function(elm, idx){ if (deep){ Util.cleanDOMElements(elm); } parentElm.onclick = null; parentElm.removeChild(elm); }); }, /** * This is much faster than using (el.innerHTML = str) when there are many * existing descendants, because in some browsers, innerHTML spends much longer * removing existing elements than it does creating new ones. * http://ajaxian.com/archives/replacehtml-for-when-innerhtml-dogs-you-down */ replaceHtml : function(el, html) { var oldEl = (typeof el === "string" ? document.getElementById(el) : el); var newEl = document.createElement(oldEl.nodeName); // Preserve the element's id and class (other properties are lost) newEl.id = oldEl.id; newEl.className = oldEl.className; // Replace the old with the new newEl.innerHTML = html; oldEl.parentNode.replaceChild(newEl, oldEl); /* Since we just removed the old element from the DOM, return a reference to the new element, which can be used to restore variable references. */ return newEl; }, /** * Retrieves "position" and "dimension" of the given window's * viewport (or the current window if no window argument is given). * * returns an object containing the following value : * obj.x = viewport X position in the screen * obj.y = viewport Y position in the screen * obj.width = viewport width * obj.height = viewport height * * Warning: the position returned in internet explorer is the position * of the window not the viewport (i.e. the viewport position being the * position of the window + title and toolbars offset) * * cf. http://www.quirksmode.org/viewport/compatibility.html */ getViewportBounds: function(win) { var vpWidth = 0; var vpHeight = 0; var vpXposInScreen = 0; var vpYposInScreen = 0; if (!win) { win = self; } var doc = win.document; // 1. Viewport Position // all but mozilla if (win.screenTop){ vpXposInScreen = win.screenLeft; vpYposInScreen = win.screenTop; } // mozilla else if (win.screenX){ vpXposInScreen = win.screenX; vpYposInScreen = win.screenY; } // 2. Viewport Dimension // all except Explorer if (win.innerHeight) { vpWidth = win.innerWidth; vpHeight = win.innerHeight; } // Explorer 6 Strict Mode else if (doc.documentElement && win.document.documentElement.clientHeight) { vpWidth = doc.documentElement.clientWidth; vpHeight = doc.documentElement.clientHeight; } // other Explorers else if (document.body) { vpWidth = doc.body.clientWidth; vpHeight = doc.body.clientHeight; } return { x: vpXposInScreen, y: vpYposInScreen, width: vpWidth, height: vpHeight }; } }); // ---------------------------------------------------- // LOGGER // ---------------------------------------------------- var JcmsLogger = { // Debug levels used by info(), debug(), warn() LEVEL_INFO: "info", LEVEL_DEBUG: "debug", LEVEL_WARN: "warn", LEVEL_ERROR: "error", // Debug status for each scope isDebug: true && window.console && window.console["debug"], // Component debug status CtxMenuManager: false, CtxMenu: false, WikiToolbar: false, InputUtil: false, Autochooser: false, JcmsJsContext: false, Table: false, TinyMCE_JcmsPlugin: false, DocChooser: false, TreeCat: false, JcmsJsonRequest: false, // -------------------- // INTERNAL // -------------------- /** * A generic function to log message in Firebug console * http://www.joehewitt.com/software/firebug/docs.php * * @param level the message level (default is DEBUG) */ _log: function(level,args){ var scope = args[0]; var msg = args[1]; if (!JcmsLogger._checkScope(level, scope, msg)) return; // Default on DEBUG level level = (level == undefined) ? JcmsLogger.LEVEL_DEBUG : level; // The message to log args[1] = "["+level+"]["+scope+"] "+msg; args = $A(args).slice(1,args.length); // The function call if (window.console && window.console[level]) window.console[level].apply(window.console,args); }, _checkScope: function(level, scope, msg){ // General check scope if ((!msg) || !JcmsLogger.isDebug) return false; // Default on DEBUG level level = (level == undefined) ? JcmsLogger.LEVEL_DEBUG : level; // Check scope if ((!scope) || (!JcmsLogger[scope] && level==JcmsLogger.LEVEL_DEBUG)) return false; return true; }, // -------------------- // FUNCTIONS // -------------------- info: function(scope, msg){ JcmsLogger._log(JcmsLogger.LEVEL_INFO,arguments); }, debug: function(scope, msg){ JcmsLogger._log(JcmsLogger.LEVEL_DEBUG,arguments); }, warn: function(scope, msg){ JcmsLogger._log(JcmsLogger.LEVEL_WARN,arguments); }, error: function(scope, msg){ JcmsLogger._log(JcmsLogger.LEVEL_ERROR,arguments); } }; // ---------------------------------------------------- // POPUP OBJECT // see also: // - http://www.quirksmode.org/js/popup.html // - http://www.w3schools.com/htmldom/met_win_open.asp // Note: Title must not contains spaces ! // ---------------------------------------------------- var Popup = { popupWindow: function(url, title, w, h, status, resizable, scrollbars, reuse, winOpener){ if (!status) status="no"; if (!w) w=320; if (!h) h=260; resizable = "resizable=" + (Util.toBoolean(resizable, true) ? "yes" : "no"); scrollbars = "scrollbars=" + (Util.toBoolean(scrollbars, true) ? "yes" : "no"); if (reuse == undefined) { reuse = true; } if (!navigator.jalios) { navigator.jalios = new Object(); } var pWnd = navigator.jalios.popupWindow; // shorter convenient var // Set window opener if (winOpener == undefined) { winOpener = window; } // Set a title if (!title && !winOpener.opener) { title = '_blank'; } // Update title else if (!reuse && pWnd){ navigator.jalios.popupCounter = navigator.jalios.popupCounter ? navigator.jalios.popupCounter+1 : 1; title = title + "_"+ navigator.jalios.popupCounter; } // close previous popup if needed if (reuse && pWnd && pWnd.close) { pWnd.close(); } // Check popup blocker try { var params = 'status=' + status + ',width=' + w+ ',height=' + h + ',menubar=no,'+ resizable + ',' + scrollbars; navigator.jalios.popupWindow = winOpener.open(url, title, params); pWnd = navigator.jalios.popupWindow; if (!pWnd){ alert(I18N.glp('warn.popup.blocker')); } } catch(ex){ alert(I18N.glp('warn.popup.blocker')); } // Set the focus if opener have the focus if (winOpener.focus && pWnd){ pWnd.focus(); } }, /** * Resize the current window to the size of the given div. * * @param divID the div of which to retrieve dimension and to use as * a reference for the new window size * @param offsetHeight an integer value that will be added to the new window height, * use this value when you want to add some margin to the div height (default is 55 if not given) * @param minimumHeight an integer value indicating the minimum height to used after resize (default is 50). */ autoResize: function(divID, offsetHeight, minimumHeight) { if (!offsetHeight) { offsetHeight = 55; } if (!minimumHeight) { minimumHeight = 50; } //var elementDim = $(document.body).getDimensions(); var elementDim = $(divID).getDimensions(); var vpBounds = Util.getViewportBounds(); // { x, y, width, height } // Compute the new height var newWinHeight = elementDim.height + offsetHeight; newWinHeight = Math.min(newWinHeight, self.screen.availHeight); // Make sure the window is not too high if (vpBounds.y && (newWinHeight + vpBounds.y > self.screen.availHeight) ) { newWinHeight = self.screen.availHeight - vpBounds.y; } // Resize the window window.resizeTo(vpBounds.width, Math.max(minimumHeight, newWinHeight)); } } // ---------------------------------------------------- // CONVENIENT // ---------------------------------------------------- document.getElementsBySelector = function(selector) { return $$(selector); }