/** * jTemplates 0.8.4 (http://jtemplates.tpython.com) * Copyright (c) 2007-2013 Tomasz Gloc (http://www.tpython.com) * * Dual licensed under the MIT (MIT-LICENSE.txt) * and/or GPL (GPL-LICENSE.txt) licenses. * * Id: $Id: jquery-jtemplates_uncompressed.js 203 2013-02-03 13:28:34Z tom $ */ /** * @fileOverview Template engine in JavaScript. * @name jTemplates * @author Tomasz Gloc * @date $Date: 2013-02-03 14:28:34 +0100 (N, 03 lut 2013) $ */ if (window.jQuery && !window.jQuery.createTemplate) { (function (jQuery) { /** * [abstract] * @name BaseNode * @class Abstract node. [abstract] */ /** * Process node and get the html string. [abstract] * @name get * @function * @param {object} d data * @param {object} param parameters * @param {Element} element a HTML element * @param {Number} deep * @return {String} * @memberOf BaseNode */ /** * [abstract] * @name BaseArray * @augments BaseNode * @class Abstract array/collection. [abstract] */ /** * Add node 'e' to array. * @name push * @function * @param {BaseNode} e a node * @memberOf BaseArray */ /** * See (http://jquery.com/). * @name jQuery * @class jQuery Library (http://jquery.com/) */ /** * See (http://jquery.com/) * @name fn * @class jQuery Library (http://jquery.com/) * @memberOf jQuery */ /** * Create new template from string s. * @name Template * @class A template or multitemplate. * @param {string} s A template string (like: "Text: {$T.txt}."). * @param {array} [includes] Array of included templates. * @param {object} [settings] Settings. * @config {boolean} [disallow_functions] Do not allow use function in data (default: true). * @config {boolean} [filter_data] Enable filter data using escapeHTML (default: true). * @config {boolean} [filter_params] Enable filter parameters using escapeHTML (default: false). * @config {boolean} [runnable_functions] Automatically run function (from data) inside {} [default: false]. * @config {boolean} [clone_data] Clone input data [default: true] * @config {boolean} [clone_params] Clone input parameters [default: true] * @config {Function} [f_cloneData] Function used to data cloning * @config {Function} [f_escapeString] Function used to escape strings * @config {Function} [f_parseJSON] Function used to parse JSON * @augments BaseNode */ var Template = function (s, includes, settings) { this._tree = []; this._param = {}; this._includes = null; this._templates = {}; this._templates_code = {}; //default parameters this.settings = jQuery.extend({ disallow_functions: false, filter_data: true, filter_params: false, runnable_functions: false, clone_data: true, clone_params: true }, settings); //set handlers this.f_cloneData = (this.settings.f_cloneData !== undefined) ? (this.settings.f_cloneData) : (TemplateUtils.cloneData); this.f_escapeString = (this.settings.f_escapeString !== undefined) ? (this.settings.f_escapeString) : (TemplateUtils.escapeHTML); this.f_parseJSON = (this.settings.f_parseJSON !== undefined) ? (this.settings.f_parseJSON) : ((this.settings.disallow_functions) ? (jQuery.parseJSON) : (TemplateUtils.parseJSON)); if (s == null) { return; } //split multiteplate this.splitTemplates(s, includes); if (s) { //set main template this.setTemplate(this._templates_code['MAIN'], includes, this.settings); } this._templates_code = null; }; /** * jTemplates version * @type string */ Template.version = '0.8.4'; /** * Debug mode (all errors are on), default: off * @type Boolean */ Template.DEBUG_MODE = false; /** * Foreach loop limit (enable only when DEBUG_MODE = true) * @type integer */ Template.FOREACH_LOOP_LIMIT = 10000; /** * Global guid * @type integer */ Template.guid = 0; /** * Split multitemplate into multiple templates. * @param {string} s A template string (like: "Text: {$T.txt}."). * @param {array} includes Array of included templates. */ Template.prototype.splitTemplates = function (s, includes) { var reg = /\{#template *(\w+) *(.*?) *\}/g, //split multitemplate into subtemplates iter, tname, se, lastIndex = null, _template_settings = [], i; //while find new subtemplate while ((iter = reg.exec(s)) !== null) { lastIndex = reg.lastIndex; tname = iter[1]; se = s.indexOf('{#/template ' + tname + '}', lastIndex); if (se === -1) { throw new Error('jTemplates: Template "' + tname + '" is not closed.'); } //save a subtemplate and parse options this._templates_code[tname] = s.substring(lastIndex, se); _template_settings[tname] = TemplateUtils.optionToObject(iter[2]); } //when no subtemplates, use all as main template if (lastIndex === null) { this._templates_code['MAIN'] = s; return; } //create a new object for every subtemplates for (i in this._templates_code) { if (i !== 'MAIN') { this._templates[i] = new Template(); } } for (i in this._templates_code) { if (i !== 'MAIN') { this._templates[i].setTemplate(this._templates_code[i], jQuery.extend({}, includes || {}, this._templates || {}), jQuery.extend({}, this.settings, _template_settings[i])); this._templates_code[i] = null; } } }; /** * Parse template. (should be template, not multitemplate). * @param {string} s A template string (like: "Text: {$T.txt}."). * @param {array} includes Array of included templates. * @param {object} [settings] Settings. */ Template.prototype.setTemplate = function (s, includes, settings) { if (s == undefined) { this._tree.push(new TextNode('', 1, this)); return; } s = s.replace(/[\n\r]/g, ''); //remove endlines s = s.replace(/\{\*.*?\*\}/g, ''); //remove comments this._includes = jQuery.extend({}, this._templates || {}, includes || {}); this.settings = new Object(settings); var node = this._tree, op = s.match(/\{#.*?\}/g), //find operators ss = 0, se = 0, e, literalMode = 0, i, l; //loop operators for (i = 0, l = (op) ? (op.length) : (0); i < l; ++i) { var this_op = op[i]; //when literal mode is on, treat operator like a text if (literalMode) { se = s.indexOf('{#/literal}', ss); //find end of block if (se === -1) { throw new Error("jTemplates: No end of literal."); } if (se > ss) { node.push(new TextNode(s.substring(ss, se), 1, this)); } ss = se + 11; //strlen '{#/literal}' literalMode = 0; while (i < l && op[i] !== '{#/literal}') { //skip all operators until literal end i++; } continue; } se = s.indexOf(this_op, ss); if (se > ss) { node.push(new TextNode(s.substring(ss, se), literalMode, this)); } this_op.match(/\{#([\w\/]+).*?\}/); //find operator name var op_ = RegExp.$1; switch (op_) { case 'elseif': node.addCond(this_op); break; case 'if': e = new opIF(node, this); e.addCond(this_op); node.push(e); node = e; break; case 'else': node.switchToElse(); break; case '/if': case '/for': case '/foreach': node = node.getParent(); break; case 'foreach': e = new opFOREACH(this_op, node, this); node.push(e); node = e; break; case 'for': e = opFORFactory(this_op, node, this); node.push(e); node = e; break; case 'continue': case 'break': node.push(new JTException(op_)); break; case 'include': node.push(new Include(this_op, this._includes, this)); break; case 'param': node.push(new UserParam(this_op, this)); break; case 'var': node.push(new UserVariable(this_op, this)); break; case 'cycle': node.push(new Cycle(this_op)); break; case 'ldelim': node.push(new TextNode('{', 1, this)); break; case 'rdelim': node.push(new TextNode('}', 1, this)); break; case 'literal': literalMode = 1; break; case '/literal': if (Template.DEBUG_MODE) { throw new Error("jTemplates: Missing begin of literal."); } break; default: if (Template.DEBUG_MODE) { throw new Error('jTemplates: unknown tag: ' + op_ + '.'); } } ss = se + this_op.length; } if (s.length > ss) { node.push(new TextNode(s.substr(ss), literalMode, this)); } }; /** * Process template and get the html string. * @param {object} d data * @param {object} param parameters * @param {Element} element a HTML element * @param {Number} deep * @return {String} */ Template.prototype.get = function (d, param, element, deep) { ++deep; if (deep == 1 && element != undefined) { jQuery.removeData(element, "jTemplatesRef"); } var $T = d, $P, ret = ''; //create clone of data if (this.settings.clone_data) { $T = this.f_cloneData(d, { escapeData: (this.settings.filter_data && deep == 1), noFunc: this.settings.disallow_functions }, this.f_escapeString); } //create clone of parameters if (!this.settings.clone_params) { $P = jQuery.extend({}, this._param, param); } else { $P = jQuery.extend({}, this.f_cloneData(this._param, { escapeData: (this.settings.filter_params), noFunc: false }, this.f_escapeString), this.f_cloneData(param, { escapeData: (this.settings.filter_params && deep == 1), noFunc: false }, this.f_escapeString)); } for (var i = 0, l = this._tree.length; i < l; ++i) { ret += this._tree[i].get($T, $P, element, deep); } this.EvalObj = null; --deep; return ret; }; /** * Create and return EvalClass object * @return {EvalClass} */ Template.prototype.getBin = function () { if (this.EvalObj == null) { this.EvalObj = new EvalClass(this); } return this.EvalObj; }; /** * Set to parameter 'name' value 'value'. * @param {string} name * @param {object} value */ Template.prototype.setParam = function (name, value) { this._param[name] = value; }; /** * Template utilities. * @namespace Template utilities. */ TemplateUtils = function () { }; /** * Replace chars &, >, <, ", ' with html entities. * To disable function set settings: filter_data=false, filter_params=false * @param {string} string * @return {string} * @static * @memberOf TemplateUtils */ TemplateUtils.escapeHTML = function (txt) { return txt.replace(/&/g, '&').replace(/>/g, '>').replace(/= 0) { result += this._template.getBin().evaluateContent(d, param, element, s.substring(sText, rm + 1)); sText = -1; sExpr = i + 1; } } else if (nested < 0) { nested = 0; } } } if (sExpr > -1) { result += s.substr(sExpr); } return result; }; /** * Virtual context for eval() (internal class) * @name EvalClass * @class Virtual bin for eval() evaluation * @param {Template} t template * @private */ EvalClass = function (t) { this.__templ = t; }; /** * Evaluate expression (template content) * @param {object} $T data * @param {object} $P parameters * @param {object} $Q element * @param {String} __value Template content * @return {String} */ EvalClass.prototype.evaluateContent = function ($T, $P, $Q, __value) { try { var result = eval(__value); if (jQuery.isFunction(result)) { if (this.__templ.settings.disallow_functions || !this.__templ.settings.runnable_functions) { return ''; } result = result($T, $P, $Q); } return (result === undefined) ? ("") : (String(result)); } catch (e) { if (Template.DEBUG_MODE) { if (e instanceof JTException) { e.type = "subtemplate"; } throw e; } return ""; } }; /** * Evaluate expression (simple eval) * @param {object} $T data * @param {object} $P parameters * @param {object} $Q element * @param {String} __value content to evaluate * @return {String} */ EvalClass.prototype.evaluate = function ($T, $P, $Q, __value) { return eval(__value); }; /** * Create a new conditional node. * @name opIF * @class A class represent: {#if}. * @param {object} par parent node * @param {Template} templ template * @augments BaseArray */ var opIF = function (par, templ) { this._parent = par; this._templ = templ; this._cond = []; //conditions this._tree = []; //conditions subtree this._curr = null; //current subtree }; /** * Add node 'e' to array. * @param {BaseNode} e a node */ opIF.prototype.push = function (e) { this._curr.push(e); }; /** * Get a parent node. * @return {BaseNode} */ opIF.prototype.getParent = function () { return this._parent; }; /** * Add condition * @param {string} oper content of operator {#..} */ opIF.prototype.addCond = function (oper) { oper.match(/\{#(?:else)*if (.*?)\}/); this._cond.push(RegExp.$1); this._curr = []; this._tree.push(this._curr); }; /** * Switch to else */ opIF.prototype.switchToElse = function () { this._cond.push(true); //else is the last condition and its always true this._curr = []; this._tree.push(this._curr); }; /** * Process node depend on conditional and get the html string. * @param {object} d data * @param {object} param parameters * @param {Element} element a HTML element * @param {Number} deep * @return {String} */ opIF.prototype.get = function (d, param, element, deep) { var ret = ''; //result try { //foreach condition for (var ci = 0, cl = this._cond.length; ci < cl; ++ci) { //if condition is true if (this._templ.getBin().evaluate(d, param, element, this._cond[ci])) { //execute and exit var t = this._tree[ci]; for (var i = 0, l = t.length; i < l; ++i) { ret += t[i].get(d, param, element, deep); } return ret; } } } catch (e) { if (Template.DEBUG_MODE || (e instanceof JTException)) { throw e; } } return ret; }; /** * Handler for a tag 'FOR'. Create new and return relative opFOREACH object. * @name opFORFactory * @class Handler for a tag 'FOR'. Create new and return relative opFOREACH object. * @param {string} oper content of operator {#..} * @param {object} par parent node * @param {Template} template a pointer to Template object * @return {opFOREACH} */ opFORFactory = function (oper, par, template) { //create operator FOREACH with function as iterator if (oper.match(/\{#for (\w+?) *= *(\S+?) +to +(\S+?) *(?:step=(\S+?))*\}/)) { var f = new opFOREACH(null, par, template); f._name = RegExp.$1; f._option = { 'begin': (RegExp.$2 || 0), 'end': (RegExp.$3 || -1), 'step': (RegExp.$4 || 1), 'extData': '$T' }; f._runFunc = (function (i) { return i; }); return f; } else { throw new Error('jTemplates: Operator failed "find": ' + oper); } }; /** * Create a new loop node. * @name opFOREACH * @class A class represent: {#foreach}. * @param {string} oper content of operator {#..} * @param {object} par parent node * @param {Template} template a pointer to Template object * @augments BaseArray */ var opFOREACH = function (oper, par, template) { this._parent = par; this._template = template; if (oper != null) { oper.match(/\{#foreach +(.+?) +as +(\w+?)( .+)*\}/); this._arg = RegExp.$1; this._name = RegExp.$2; this._option = RegExp.$3 || null; this._option = TemplateUtils.optionToObject(this._option); } this._onTrue = []; this._onFalse = []; this._currentState = this._onTrue; //this._runFunc = null; }; /** * Add node 'e' to array. * @param {BaseNode} e */ opFOREACH.prototype.push = function (e) { this._currentState.push(e); }; /** * Get a parent node. * @return {BaseNode} */ opFOREACH.prototype.getParent = function () { return this._parent; }; /** * Switch from collection onTrue to onFalse. */ opFOREACH.prototype.switchToElse = function () { this._currentState = this._onFalse; }; /** * Process loop and get the html string. * @param {object} d data * @param {object} param parameters * @param {Element} element a HTML element * @param {Number} deep * @return {String} */ opFOREACH.prototype.get = function (d, param, element, deep) { try { //array of elements in foreach (or function) var fcount = (this._runFunc === undefined) ? (this._template.getBin().evaluate(d, param, element, this._arg)) : (this._runFunc); if (fcount === $) { throw new Error("jTemplate: Variable '$' cannot be used as loop-function"); } var key = []; //only for objects var mode = typeof fcount; if (mode == 'object') { //transform object to array var arr = []; jQuery.each(fcount, function (k, v) { key.push(k); arr.push(v); }); fcount = arr; } //setup primary iterator, iterator can get data from options (using by operator FOR) or from data "$T" var extData = (this._option.extData !== undefined) ? (this._template.getBin().evaluate(d, param, element, this._option.extData)) : ((d != null) ? (d) : ({})); if (extData == null) { extData = {}; } //start, end and step var s = Number(this._template.getBin().evaluate(d, param, element, this._option.begin) || 0), e; //start, end var step = Number(this._template.getBin().evaluate(d, param, element, this._option.step) || 1); if (mode != 'function') { e = fcount.length; } else { if (this._option.end === undefined || this._option.end === null) { e = Number.MAX_VALUE; } else { e = Number(this._template.getBin().evaluate(d, param, element, this._option.end)) + ((step > 0) ? (1) : (-1)); } } var ret = ''; //result string var i, l; //local iterators if (this._option.count) { //limit number of loops var tmp = s + Number(this._template.getBin().evaluate(d, param, element, this._option.count)); e = (tmp > e) ? (e) : (tmp); } if ((e > s && step > 0) || (e < s && step < 0)) { var iteration = 0; var _total = (mode != 'function') ? (Math.ceil((e - s) / step)) : undefined; var ckey, cval; //current key, current value var loopCounter = 0; for (; ((step > 0) ? (s < e) : (s > e)); s += step, ++iteration, ++loopCounter) { if (Template.DEBUG_MODE && loopCounter > Template.FOREACH_LOOP_LIMIT) { throw new Error("jTemplate: Foreach loop limit was exceed"); } ckey = key[s]; if (mode != 'function') { cval = fcount[s]; //get value from array } else { cval = fcount(s); //calc function //if no result from function then stop foreach if (cval === undefined || cval === null) { break; } } if ((typeof cval == 'function') && (this._template.settings.disallow_functions || !this._template.settings.runnable_functions)) { continue; } if ((mode == 'object') && (ckey in Object) && (cval === Object[ckey])) { continue; } //backup on value var prevValue = extData[this._name]; //set iterator properties extData[this._name] = cval; extData[this._name + '$index'] = s; extData[this._name + '$iteration'] = iteration; extData[this._name + '$first'] = (iteration === 0); extData[this._name + '$last'] = (s + step >= e); extData[this._name + '$total'] = _total; extData[this._name + '$key'] = (ckey !== undefined && ckey.constructor == String) ? (this._template.f_escapeString(ckey)) : (ckey); extData[this._name + '$typeof'] = typeof cval; for (i = 0, l = this._onTrue.length; i < l; ++i) { try { ret += this._onTrue[i].get(extData, param, element, deep); } catch (ex) { if (ex instanceof JTException) { switch (ex.type) { case 'continue': i = l; //force skip to next node break; case 'break': i = l; //force skip to next node s = e; //force skip outsite foreach break; default: throw ex; } } else { throw ex; } } } //restore values delete extData[this._name + '$index']; delete extData[this._name + '$iteration']; delete extData[this._name + '$first']; delete extData[this._name + '$last']; delete extData[this._name + '$total']; delete extData[this._name + '$key']; delete extData[this._name + '$typeof']; delete extData[this._name]; extData[this._name] = prevValue; } } else { //no items to loop ("foreach->else") for (i = 0, l = this._onFalse.length; i < l; ++i) { ret += this._onFalse[i].get(d, param, element, deep); } } return ret; } catch (e) { if (Template.DEBUG_MODE || (e instanceof JTException)) { throw e; } return ""; } }; /** * Template-control exceptions * @name JTException * @class A class used internals for a template-control exceptions * @param type {string} Type of exception * @augments Error * @augments BaseNode */ var JTException = function (type) { this.type = type; }; JTException.prototype = Error; /** * Throw a template-control exception * @throws It throws itself */ JTException.prototype.get = function (d) { throw this; }; /** * Create a new entry for included template. * @name Include * @class A class represent: {#include}. * @param {string} oper content of operator {#..} * @param {array} includes * @param {Template} templ template * @augments BaseNode */ var Include = function (oper, includes, templ) { oper.match(/\{#include (.*?)(?: root=(.*?))?\}/); this._template = includes[RegExp.$1]; if (this._template == undefined) { if (Template.DEBUG_MODE) { throw new Error('jTemplates: Cannot find include: ' + RegExp.$1); } } this._root = RegExp.$2; this._mainTempl = templ; }; /** * Run method get on included template. * @param {object} d data * @param {object} param parameters * @param {Element} element a HTML element * @param {Number} deep * @return {String} */ Include.prototype.get = function (d, param, element, deep) { try { //run a subtemplates with a new root node return this._template.get(this._mainTempl.getBin().evaluate(d, param, element, this._root), param, element, deep); } catch (e) { if (Template.DEBUG_MODE || (e instanceof JTException)) { throw e; } } return ''; }; /** * Create new node for {#param}. * @name UserParam * @class A class represent: {#param}. * @param {string} oper content of operator {#..} * @param {Template} templ template * @augments BaseNode */ var UserParam = function (oper, templ) { oper.match(/\{#param name=(\w*?) value=(.*?)\}/); this._name = RegExp.$1; this._value = RegExp.$2; this._templ = templ; }; /** * Return value of selected parameter. * @param {object} d data * @param {object} param parameters * @param {Element} element a HTML element * @param {Number} deep * @return {String} empty string */ UserParam.prototype.get = function (d, param, element, deep) { try { param[this._name] = this._templ.getBin().evaluate(d, param, element, this._value); } catch (e) { if (Template.DEBUG_MODE || (e instanceof JTException)) { throw e; } param[this._name] = undefined; } return ''; }; /** * Create new node for {#var}. * @name UserVariable * @class A class represent: {#var}. * @param {string} oper content of operator {#..} * @param {Template} templ template * @augments BaseNode */ var UserVariable = function (oper, templ) { oper.match(/\{#var (.*?)\}/); this._id = RegExp.$1; this._templ = templ; }; /** * Return value of selected variable. * @param {object} d data * @param {object} param parameters * @param {Element} element a HTML element * @param {Number} deep * @return {String} calling of function ReturnRefValue (as text string) */ UserVariable.prototype.get = function (d, param, element, deep) { try { if (element == undefined) { return ""; } var obj = this._templ.getBin().evaluate(d, param, element, this._id); var refobj = jQuery.data(element, "jTemplatesRef"); if (refobj == undefined) { refobj = { guid: (++Template.guid), d: [] }; } var i = refobj.d.push(obj); jQuery.data(element, "jTemplatesRef", refobj); return "(TemplateUtils.ReturnRefValue(this," + refobj.guid + "," + (i - 1) + "))"; } catch (e) { if (Template.DEBUG_MODE || (e instanceof JTException)) { throw e; } return ''; } }; /** * Create a new cycle node. * @name Cycle * @class A class represent: {#cycle}. * @param {string} oper content of operator {#..} * @augments BaseNode */ var Cycle = function (oper) { oper.match(/\{#cycle values=(.*?)\}/); this._values = eval(RegExp.$1); this._length = this._values.length; if (this._length <= 0) { throw new Error('jTemplates: no elements for cycle'); } this._index = 0; this._lastSessionID = -1; }; /** * Do a step on cycle and return value. * @param {object} d data * @param {object} param parameters * @param {Element} element a HTML element * @param {Number} deep * @return {String} */ Cycle.prototype.get = function (d, param, element, deep) { var sid = jQuery.data(element, 'jTemplateSID'); if (sid != this._lastSessionID) { this._lastSessionID = sid; this._index = 0; } var i = this._index++ % this._length; return this._values[i]; }; /** * Add a Template to HTML Elements. * @param {Template/string} s a Template or a template string * @param {array} [includes] Array of included templates. * @param {object} [settings] Settings (see Template) * @return {jQuery} chainable jQuery class * @memberOf jQuery.fn */ jQuery.fn.setTemplate = function (s, includes, settings) { return jQuery(this).each(function () { var t = (s && s.constructor == Template) ? s : new Template(s, includes, settings); jQuery.data(this, 'jTemplate', t); jQuery.data(this, 'jTemplateSID', 0); }); }; /** * Add a Template (from URL) to HTML Elements. * @param {string} url_ URL to template * @param {array} [includes] Array of included templates. * @param {object} [settings] Settings (see Template) * @return {jQuery} chainable jQuery class * @memberOf jQuery.fn */ jQuery.fn.setTemplateURL = function (url_, includes, settings) { var s = jQuery.ajax({ url: url_, dataType: 'text', async: false, type: 'GET' }).responseText; return jQuery(this).setTemplate(s, includes, settings); }; /** * Create a Template from element's content. * @param {string} elementName an ID of element * @param {array} [includes] Array of included templates. * @param {object} [settings] Settings (see Template) * @return {jQuery} chainable jQuery class * @memberOf jQuery.fn */ jQuery.fn.setTemplateElement = function (elementName, includes, settings) { var s = jQuery('#' + elementName).val(); if (s == null) { s = jQuery('#' + elementName).html(); s = s.replace(/</g, "<").replace(/>/g, ">"); } s = jQuery.trim(s); s = s.replace(/^<\!\[CDATA\[([\s\S]*)\]\]>$/im, '$1'); s = s.replace(/^<\!--([\s\S]*)-->$/im, '$1'); return jQuery(this).setTemplate(s, includes, settings); }; /** * Check it HTML Elements have a template. Return count of templates. * @return {number} Number of templates. * @memberOf jQuery.fn */ jQuery.fn.hasTemplate = function () { var count = 0; jQuery(this).each(function () { if (jQuery.getTemplate(this)) { ++count; } }); return count; }; /** * Remote Template from HTML Element(s) * @return {jQuery} chainable jQuery class */ jQuery.fn.removeTemplate = function () { jQuery(this).processTemplateStop(); return jQuery(this).each(function () { jQuery.removeData(this, 'jTemplate'); }); }; /** * Set to parameter 'name' value 'value'. * @param {string} name * @param {object} value * @return {jQuery} chainable jQuery class * @memberOf jQuery.fn */ jQuery.fn.setParam = function (name, value) { return jQuery(this).each(function () { var t = jQuery.getTemplate(this); if (t != null) { t.setParam(name, value); } else if (Template.DEBUG_MODE) { throw new Error('jTemplates: Template is not defined.'); } }); }; /** * Process template using data 'd' and parameters 'param'. Update HTML code. * @param {object} d data * @param {object} [param] parameters * @option {object} [options] internal use only * @return {jQuery} chainable jQuery class * @memberOf jQuery.fn */ jQuery.fn.processTemplate = function (d, param, options) { return jQuery(this).each(function () { var t = jQuery.getTemplate(this); if (t != null) { if (options != undefined && options.StrToJSON) { d = t.f_parseJSON(d); } jQuery.data(this, 'jTemplateSID', jQuery.data(this, 'jTemplateSID') + 1); jQuery(this).html(t.get(d, param, this, 0)); } else if (Template.DEBUG_MODE) { throw new Error('jTemplates: Template is not defined.'); } }); }; /** * Process template using data from URL 'url_' (only format JSON) and parameters 'param'. Update HTML code. * @param {string} url_ URL to data (in JSON) * @param {object} [param] parameters * @param {object} options options (over ajaxSettings) and callbacks * @return {jQuery} chainable jQuery class * @memberOf jQuery.fn */ jQuery.fn.processTemplateURL = function (url_, param, options) { var that = this; var o = jQuery.extend({ cache: false }, jQuery.ajaxSettings); o = jQuery.extend(o, options); jQuery.ajax({ url: url_, type: o.type, data: o.data, dataFilter: o.dataFilter, async: o.async, headers: o.headers, cache: o.cache, timeout: o.timeout, dataType: 'text', success: function (d) { var r = jQuery(that).processTemplate(d, param, { StrToJSON: true }); if (o.on_success) { o.on_success(r); } }, error: o.on_error, complete: o.on_complete }); return this; }; /** * Create new Updater. * @name Updater * @class This class is used for 'Live Refresh!'. * @param {string} url A destination URL * @param {object} param Parameters (for template) * @param {number} interval Time refresh interval * @param {object} args Additional URL parameters (in URL alter ?) as assoc array. * @param {array} objs An array of HTMLElement which will be modified by Updater. * @param {object} options options and callbacks */ var Updater = function (url, param, interval, args, objs, options) { this._url = url; this._param = param; this._interval = interval; this._args = args; this.objs = objs; this.timer = null; this._options = options || {}; var that = this; jQuery(objs).each(function () { jQuery.data(this, 'jTemplateUpdater', that); }); this.run(); }; /** * Create new HTTP request to server, get data (as JSON) and send it to templates. Also check does HTMLElements still exists in Document. */ Updater.prototype.run = function () { //remove deleted node this.objs = jQuery.grep(this.objs, function (elem) { return (jQuery.contains(document.body, elem.jquery ? elem[0] : elem)); }); //if no node then do nothing if (this.objs.length === 0) { return; } //ajax call var that = this; jQuery.ajax({ url: this._url, dataType: 'text', data: this._args, cache: false, headers: that._options.headers, success: function (d) { try { var r = jQuery(that.objs).processTemplate(d, that._param, { StrToJSON: true }); if (that._options.on_success) { that._options.on_success(r); //callback } } catch (ex) { } } }); //schedule next run this.timer = setTimeout(function () { that.run(); }, this._interval); }; /** * Start 'Live Refresh!'. * @param {string} url A destination URL * @param {object} param Parameters (for template) * @param {number} interval Time refresh interval * @param {object} args Additional URL parameters (in URL alter ?) as assoc array. * @param {object} options options and callbacks * @return {Updater} an Updater object * @memberOf jQuery.fn */ jQuery.fn.processTemplateStart = function (url, param, interval, args, options) { return new Updater(url, param, interval, args, this, options); }; /** * Stop 'Live Refresh!'. * @return {jQuery} chainable jQuery class * @memberOf jQuery.fn */ jQuery.fn.processTemplateStop = function () { return jQuery(this).each(function () { var updater = jQuery.data(this, 'jTemplateUpdater'); if (updater == null) { return; } var that = this; updater.objs = jQuery.grep(updater.objs, function (o) { return o != that; }); jQuery.removeData(this, 'jTemplateUpdater'); }); }; jQuery.extend(/** @scope jQuery.prototype */{ /** * Create new Template. * @param {string} s A template string (like: "Text: {$T.txt}."). * @param {array} includes Array of included templates. * @param {object} settings Settings. (see Template) * @return {Template} */ createTemplate: function (s, includes, settings) { return new Template(s, includes, settings); }, /** * Create new Template from URL. * @param {string} url_ URL to template * @param {array} includes Array of included templates. * @param {object} settings Settings. (see Template) * @return {Template} */ createTemplateURL: function (url_, includes, settings) { var s = jQuery.ajax({ url: url_, dataType: 'text', async: false, type: 'GET' }).responseText; return new Template(s, includes, settings); }, /** * Get a Template for HTML node * @param {Element} HTML node * @return {Template} a Template or "undefined" */ getTemplate: function (element) { return jQuery.data(element, 'jTemplate'); }, /** * Process template and return text content. * @param {Template} template A Template * @param {object} data data * @param {object} param parameters * @return {string} Content of template */ processTemplateToText: function (template, data, parameter) { return template.get(data, parameter, undefined, 0); }, /** * Set Debug Mode * @param {Boolean} value */ jTemplatesDebugMode: function (value) { Template.DEBUG_MODE = value; } }); })(jQuery); };