/**
* WebJS server module
* @module webjs-server
*/
/**
* JQuery extensions for templating and returning the result
* @class jQuery
*/
jQuery.fn.extend(
/** @lends jQuery */
{
contextPath: function(attr, prefix) {
return this.attr(attr, function() { return request.contextPath + this[attr].replace(prefix, ""); });
},
print: function(doctype) {
doctype = doctype || "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
return doctype
+ "\n"
+ this[0].innerHTML;
},
/**
* Loop template addition to jQuery.<br/>
* For example, if you want to copy a div.blog-entry using an array of entries :<br/>
* <pre>$(".blog-entry").template(entries, function(entry, index) {
* this.appendTo(".blog");
* this.attr("id", "blog-entry-"+entry.id);
* this.attr("data-pk", entry.id);
* this.find(".blog-entry-title").text(entry.title);
* this.find(".blog-entry-author").text(entry.author);
* this.find(".blog-entry-text").text(entry.text);
* });</pre>
* @param {Array} list
* @param {function} handler
*/
template: function(list, handler) {
var template = this.remove();
list.forEach(function() {
handler.apply(template.clone(), arguments);
});
},
list: function(list, separator, handler) {
var result = [];
list.forEach(function(entry, index) {
result.push( handler(entry, index) );
});
return this.html( result.join(separator) );
},
validate: function() {
}
});
// String formatting
String.format = function(source, params) {
if ( arguments.length == 1 )
return function() {
var args = jQuery.makeArray(arguments);
args.unshift(source);
return String.format.apply( this, args );
};
if ( arguments.length > 2 && params.constructor != Array ) {
params = jQuery.makeArray(arguments).slice(1);
}
if ( params.constructor != Array ) {
params = [ params ];
}
jQuery.each(params, function(i, n) {
source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
});
return source;
};
/**
* Prints given arguments to standard output
* @param {Array} arguments Strings to be displayed
*/
function print() {
java.lang.System.out.println($.makeArray(arguments).join(", "));
}
/**
* Loads and executes a list of JavaScripts files
* @function module:webjs-server~load
* @param {Array} scripts List of scripts to be loaded
* @see Defined in Java code fr.dz.webjs.scripts.WebJSScriptLibrary
*/
/**
* Reads a file and returns its content
* @function module:webjs-server~readFile
* @param {String} filename The file name
* @return {String} The file content
* @see Defined in Java code fr.dz.webjs.scripts.WebJSScriptLibrary
*/
/**
* Gets a message from the resource bundles using client locale
* @function module:webjs-server~getMessage
* @param {String} key The message key
* @param {Array} parameters The message parameters
* @return {String} The message
* @see Defined in Java code fr.dz.webjs.scripts.WebJSScriptLibrary
*/
/**
* Gets a Log4J logger from JS. A JS exception is needed to get script name and script line.
* @function module:webjs-server~internalGetLogger
* @param {String} exception The exception
* @see Defined in Java code fr.dz.webjs.scripts.WebJSScriptLibrary
* @private
* @deprecated Use {@link module:messages-server~Messages} instead
*/
/**
* Gets a Log4J logger from JS.
* @function module:webjs-server~getLogger
* @return {Object} A Log4J logger reflecting javascript.<script_file_name>:<line_number>
* @deprecated Use {@link module:messages-server~Messages} instead
*/
function getLogger() {
try {
throw new Packages.java.lang.Exception("JS logger exception");
} catch(e) {
return internalGetLogger(e);
}
}
/**
* WebJS server utility
* @namespace
*/
var WebJS = {
/**
* Default WebJS client scripts added to generated web pages
* @type {Array}
* @constant
*/
CLIENT_SCRIPTS: ["webjs-static/js/moment.js", "webjs-static/js/jquery.js", "webjs-static/js/jquery-ui.js",
"webjs-generated/js/configuration-client.js", "webjs-static/js/webjs-client.js",
"webjs-static/js/messages-client.js" ],
/**
* Default WebJS CSSs added to generated web pages
* @type {Array}
* @constant
*/
CSS: ["webjs-static/css/webjs.css", "webjs-static/css/messages.css" ],
/**
* WebJS initializations :
* <ul><li>loads the given template, if exists</li>
* <li>adds default WebJS client scripts</li>
* <li>relocate links to match request</li>
* <li>feeds messages</li></ul>
* @private
*/
init: function() {
this.loadTemplate();
this.addHeaders();
this.relocateLinks();
},
/**
* Loads the associated template
* @private
*/
loadTemplate: function() {
var template = $("html").attr("webjs:template");
if ( template !== undefined ) {
var templateDocument = $(readFile(template));
// Fills headers
var templateHead = templateDocument.find("head");
$("head").children().each(function(){
templateHead.append($(this).clone());
});
// Fills include tags
templateDocument.find(WebJS.escape("webjs:include")).each(function(){
var include = $(this);
var source = $(WebJS.escapeId(include.attr("id")));
include.replaceWith(source.clone());
});
// Replaces document by the new one
$(document).find("head").replaceWith(templateDocument.find("head"));
$(document).find("body").replaceWith(templateDocument.find("body"));
// Deletes template attribute
$("html").removeAttr("webjs:template");
}
},
/**
* If links are relative, adds the request base URI before
* @private
*/
relocateLinks: function() {
$("link, a").each(function() {
var jq = $(this);
var href = jq.attr("href");
if ( href && href != '' && href.indexOf(":") == -1 && href.indexOf("#") != 0 ) {
jq.attr("href", request.contextRoot + "/" + href );
}
});
$("script, img").each(function() {
var jq = $(this);
var src = jq.attr("src");
if ( src && src != '' && src.indexOf(":") == -1 && src.indexOf("#") != 0 ) {
jq.attr("src", request.contextRoot + "/" + src );
}
});
},
/**
* Add WebJS client scripts and CSS to HTML headers
* @private
*/
addHeaders: function() {
$.makeArray(this.CLIENT_SCRIPTS).reverse().forEach(function(clientScript, index) {
var script = $("<script/>")
.attr("src", clientScript)
.attr("type", "text/javascript");
$("head").prepend(script);
});
$.makeArray(this.CSS).reverse().forEach(function(css, index) {
var link = $("<link/>")
.attr("href", css)
.attr("rel", "stylesheet");
$("head").prepend(link);
});
// Theme CSS
var link = $("<link/>")
.attr("href", "webjs-static/themes/"+this.getTheme()+"/jquery-ui.css")
.attr("rel", "stylesheet");
$("head").prepend(link);
},
/**
* Feeds localized messages using resources
* @param jqRoot jQuery root
* @private
*/
feedLocalizedMessages: function(jqRoot) {
jqRoot.find(WebJS.escape("webjs:message")).each(function(){
var messageNode = $(this);
var id = messageNode.attr("id");
var parameters = [];
messageNode.find(WebJS.escape("webjs:parameter")).each(function(){
parameters.push($(this).attr("value"));
});
var message = getMessage(id, parameters);
if ( message ) {
messageNode.replaceWith(message);
} else {
messageNode.replaceWith("/!\\ "+id+" /!\\");
}
});
},
/**
* Returns the page result :
* <ul><li>feeds messages</li>
* <li>fixes textareas</li></ul>
* @private
*/
returnResult: function() {
// The whole page need to be rendered (no ajax)
if ( this.isWholePageRendered() ) {
// Feed localization messages
this.feedLocalizedMessages($("html"));
// Send messages to the client
var append = $("body").append("<messages/>");
for ( var i = 0; i < request.messages.size(); i++ ) {
append.append($("<message><level>"+request.messages.get(i).level+"</level><text>"+request.messages.get(i).message+"</text></message>"));
}
// Fix some HTML tags
return this.fixHTMLTags($(document).print());
}
// Else, it's an Ajax result, so we need to generate a result
else {
var result = $("<html><webpart><update></update><messages></messages></webpart></html>");
// HTML updates
var append = result.find("update");
request.renderSelector.split(",").forEach(function(selector,index) {
$(selector).each(function(){
append.append($(this).clone());
});
});
// Feed localization messages
this.feedLocalizedMessages(result);
// Sends messages to the client
append = result.find("messages");
for ( var i = 0; i < request.messages.size(); i++ ) {
append.append($("<message><level>"+request.messages.get(i).level+"</level><text>"+request.messages.get(i).message+"</text></message>"));
}
// Fix some HTML tags
return this.fixHTMLTags(result.html());
}
},
/**
* Replaces <textarea /> to <textarea></textarea> because browsers can't manage it...
* Idem for <div />
* @private
*/
fixHTMLTags: function(html) {
["textarea", "div"].forEach(function(tag, index){
html = html.replace(new RegExp("<"+tag+"([^>]*)\\/>","g"), "<"+tag+"$1></"+tag+">");
});
return html;
},
/**
* Returns true if the whole page is rendered
*/
isWholePageRendered: function() {
return request.renderSelector == null || request.renderSelector == '' || request.renderSelector == request.DEFAULT_RENDER_SELECTOR;
},
/**
* Tests the existence of bind variables
* @param {Array} bindNames List of bind variables names
* @param {function} callbackExists Function executed if the bindings exist
* @param {function} callbackNotExists Function executed if the bindings not exist
*/
checkBindings: function(bindNames, callbackExists, callbackNotExists) {
var allExist = true;
$.makeArray(bindNames).forEach(function(bindName, index) {
allExist = allExist && eval("typeof("+bindName+")") != "undefined";
});
if ( allExist && callbackExists && typeof(callbackExists) == "function" ) {
callbackExists();
}
if ( ! allExist && callbackNotExists && typeof(callbackNotExists) == "function" ) {
callbackNotExists();
}
},
/**
* Appends a static HTML file into a given node
* @param {Object} node jQuery Node to be appended
* @param {String} htmlFile HTML filename
*/
appendHtmlFile: function(node, htmlFile) {
var fileContent = $(readFile(htmlFile)).find("body");
node.append(fileContent);
},
/**
* Escapes an HTML id
* @param {String} id The id to be escaped
* @return {String} The escaped id
*/
escapeId: function(id) {
return "#" + this.escape(id);
},
/**
* Escapes an HTML class
* @param {String} clazz The class to be escaped
* @return {String} The escaped class
*/
escapeClass: function(clazz) {
return "." + this.escape(clazz);
},
/**
* Escapes a selector
* @param {String} selector The selector to be escaped
* @return {String} The escaped selector
* @todo To be completed
*/
escape: function(selector) {
return selector.replace(":", "\\:");
},
/**
* Return the used theme
* @return {String} The theme name
* @todo Parameterize theme for the user
*/
getTheme: function() {
return request.webJS.configuration.xmlConfiguration.clientBehavior.defaultTheme;
},
};
// WebJS initialization
WebJS.init();