/* Copyright (c) 2006-2007, The Xooki project http://xooki.sourceforge.net/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Some code is largely inspired by code found in the dojo toolkit, see http://dojotoolkit.org/ for more information. */ /* This script can be either embedded in a xooki page for in browser processing, or used in batch using rhino or java 6 javascript tool: jrunscript path/to/xooki.js inputFileFromXookiSite.html [path/to/dir/to/put/generated/file] Be sure to be in the directory where the html input to process is when running this command. */ var batchMode = (typeof arguments != 'undefined'); var xooki = {}; xooki.console = ""; // used for debugging purpose only, and only when the debug div is not yet created xooki.config = {}; xooki.c = xooki.config; function t(msg) { // returns the internationalized version of the message, or the message if no translation is available // t stands for translate // FIXME // if (typeof xooki.c == "object" // && typeof xooki.c.messages == "object" // && typeof xooki.c.messages[msg] == "string") { // msg = xooki.c.messages[msg]; // } var arr = []; for (var i=1; i'); document.write(''); }, evalURL: function( url, warnOnErrorUrl ) { script = this.loadURL(url, warnOnErrorUrl); if (script != null) { try { eval(script); } catch (e) { xooki.error(e, t("error while executing script from URL ${0}", url)); } } }, action: function(action) { // returns the url for an action on the same page loc = batchMode?'':xooki.pageURL; if (loc.indexOf("#") != -1) { loc = loc.substring(0, loc.indexOf("#")); } return loc+"?action="+action; } }; xooki.string = { substituteParams: function(/*string*/template, /* object - optional or ... */hash) { // borrowed from dojo // summary: // Performs parameterized substitutions on a string. Throws an exception if any parameter is unmatched. // // description: // For example, // dojo.string.substituteParams("File '${0}' is not found in directory '${1}'.","foo.html","/temp"); // returns // "File 'foo.html' is not found in directory '/temp'." // // template: the original string template with ${values} to be replaced // hash: name/value pairs (type object) to provide substitutions. Alternatively, substitutions may be // included as an array var map; if (typeof hash == "object" && hash.length) { // array map = {}; for (var i in hash) { map[i+""] = hash[i]; } } else { map = hash; } return template.replace(/\$\{(\w+)\}/g, function(match, key){ if(typeof(map[key]) != "undefined" && map[key] != null){ return map[key]; } xooki.warn("Substitution not found: " + key); return key; }); // string }, processTemplate: function(/*string*/template, /* object */hash) { if (typeof template.process == "function") { return template.process(hash); } else { return this.substituteParams(template, hash); } }, exceptionText: function(e, message) { var s = e.description ? e.description : e.toString(); return message ? message+":\n"+s : s; }, findXmlSection: function(str, element, from) { return this.findSection(str, new RegExp('<'+element+'(\\s*\\w+="[\\w\\s]*")*>'), new RegExp(''), from); }, find: function(/*string*/str, /*string or regexp*/exp, /*number, optional*/from) { // find an expression (string or regexp) in a string, from an optional index // the object returned has two properties: // begin: the index in str of the matching find // end: the index in str of the end of the matching find // returns null if no match is found if (typeof from != "number") { from = 0; } if (typeof exp == "string") { var result = {}; result.begin = str.indexOf(exp,from); if (result.begin >= 0) { result.end = result.begin + exp.length; return result; } } else { var m; if (from > 0) { // I haven't found any other way to start from the given index m = exp.exec(str.substring(from)); } else { m = exp.exec(str); } if (m != null) { var result = {}; result.begin = m.index + from; result.end = result.begin + m[0].length; return result; } } return null; }, findSection: function(/*string*/str, /*string or regexp*/open, /*string or regexp*/close, /*number, optional*/from) { // finds a section delimited by open and close tokens in the given string // the algorithm looks for matching open and close tokens // the returned object has the following properties: // outerStart: the index in str where the first open token was found // innerStart: the index in str just after the found open token // innerEnd: the index in str where the matching close token was found // outerEnd: the index in str just after the matching close token // children: an array of similar objects if nested sections where found // if no section is found (no open token, an open token with no matching // close token, or a close token before an open token), null is returned // // for instance if open=='(' and close==')' then the section will find // a section delimited by the first found open parenthesis and the matching // close parentethis, taking into account other opening parentethis // examples: // findSection("a(test)b", "(", ")") == {outerStart: 1, innerStart:2, innerEnd:6, outerEnd:7, children:[]} // findSection("a(te(s)(t))b", "(", ")") == {outerStart: 1, innerStart:2, innerEnd:10, outerEnd:11, // children:[ // {outerStart: 4, innerStart:5, innerEnd:6, outerEnd:7, children:[]}, // {outerStart: 7, innerStart:8, innerEnd:9, outerEnd:10, children:[]} // ]} var openResult = this.find(str, open, from); if (openResult == null) { return null; } var closeResult = this.find(str, close, from); if (closeResult == null || closeResult.begin < openResult.end) { return null; } if (openResult.end <= openResult.begin || closeResult.end <= closeResult.begin) { // empty match are not allowed return null; } var children = []; var child = this.findSection(str, open, close, openResult.end); while (child != null) { if (child.outerEnd > closeResult.begin) { closeResult = this.find(str, close, child.outerEnd); if (closeResult == null) { // unmatched open token return null; } } children.push(child); child = this.findSection(str, open, close, child.outerEnd); } return { outerStart: openResult.begin, innerStart: openResult.end, innerEnd: closeResult.begin, outerEnd: closeResult.end, children: children }; }, mul: function (/*string*/ s, /*int*/ n) { r = ''; for (var i=0; i < n; i++) { r += s; } return r; } }; xooki.json = { evalJson: function (str) { try { return eval("("+str+")"); } catch (e) { return null; } }, loadURL: function (url) { return this.evalJson(xooki.url.loadURL(url)); } }; // Displays an alert of an exception description with optional message xooki.warn = function(e, message) { xooki.display(xooki.string.exceptionText(e, message), "#eecccc"); } // Displays an alert of an exception description with optional message xooki.error = function(e, message) { xooki.display(xooki.string.exceptionText(e, message), "#ffdddd"); } xooki.info = function(message) { xooki.display(message, "#ddddff"); } xooki.display = function(message, background) { var messages = document.getElementById('xooki-messages'); if (messages) { messages.innerHTML = '
'+message+'
'; messages.style.background = background; messages.style.display = "inline"; } else { alert(message); } } xooki.debug = function(message) { var console = typeof document == 'undefined' ? false : document.getElementById('xooki-console'); if (console) { console.value += message + "\n"; } else { xooki.console += message + "\n"; } } xooki.debugShowDetail = function (message) { var detail = typeof document == 'undefined' ? false : document.getElementById('xooki-debug-detail'); if (detail) { detail.value=message; } else { alert(message); } } xooki.html = { hide: function(divid) { document.getElementById(divid).style.display = 'none'; }, show: function (divid) { document.getElementById(divid).style.display = ''; }, pageLink: function(page) { if (page.isAbstract) { return page.title; } else { return ''+page.title+''; } }, // insert the given header in the html head // can be used only when the browser is still in the head ! addHeader: function(/* string */ head) { document.write(head); }, setBody: function( /* string */ body) { document.body.innerHTML = body; } }; xooki.component = { childrenList: function () { if (xooki.page.children.length > 0) { childrenList = '\n"; return childrenList; } else { return ""; } }, menu: function () { var menu = '