macoslinuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-services
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
9.8 KiB
282 lines
9.8 KiB
/** |
|
* This class parses the XTemplate syntax and calls abstract methods to process the parts. |
|
* @private |
|
*/ |
|
Ext.define('Ext.util.XTemplateParser', { |
|
|
|
requires: [ |
|
'Ext.String' |
|
], |
|
|
|
constructor: function (config) { |
|
Ext.apply(this, config); |
|
}, |
|
|
|
/** |
|
* @property {Number} level The 'for' or 'foreach' loop context level. This is adjusted |
|
* up by one prior to calling {@link #doFor} or {@link #doForEach} and down by one after |
|
* calling the corresponding {@link #doEnd} that closes the loop. This will be 1 on the |
|
* first {@link #doFor} or {@link #doForEach} call. |
|
*/ |
|
|
|
/** |
|
* This method is called to process a piece of raw text from the tpl. |
|
* @param {String} text |
|
* @method doText |
|
*/ |
|
// doText: function (text) |
|
|
|
/** |
|
* This method is called to process expressions (like `{[expr]}`). |
|
* @param {String} expr The body of the expression (inside "{[" and "]}"). |
|
* @method doExpr |
|
*/ |
|
// doExpr: function (expr) |
|
|
|
/** |
|
* This method is called to process simple tags (like `{tag}`). |
|
* @method doTag |
|
*/ |
|
// doTag: function (tag) |
|
|
|
/** |
|
* This method is called to process `<tpl else>`. |
|
* @method doElse |
|
*/ |
|
// doElse: function () |
|
|
|
/** |
|
* This method is called to process `{% text %}`. |
|
* @param {String} text |
|
* @method doEval |
|
*/ |
|
// doEval: function (text) |
|
|
|
/** |
|
* This method is called to process `<tpl if="action">`. If there are other attributes, |
|
* these are passed in the actions object. |
|
* @param {String} action |
|
* @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). |
|
* @method doIf |
|
*/ |
|
// doIf: function (action, actions) |
|
|
|
/** |
|
* This method is called to process `<tpl elseif="action">`. If there are other attributes, |
|
* these are passed in the actions object. |
|
* @param {String} action |
|
* @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). |
|
* @method doElseIf |
|
*/ |
|
// doElseIf: function (action, actions) |
|
|
|
/** |
|
* This method is called to process `<tpl switch="action">`. If there are other attributes, |
|
* these are passed in the actions object. |
|
* @param {String} action |
|
* @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). |
|
* @method doSwitch |
|
*/ |
|
// doSwitch: function (action, actions) |
|
|
|
/** |
|
* This method is called to process `<tpl case="action">`. If there are other attributes, |
|
* these are passed in the actions object. |
|
* @param {String} action |
|
* @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). |
|
* @method doCase |
|
*/ |
|
// doCase: function (action, actions) |
|
|
|
/** |
|
* This method is called to process `<tpl default>`. |
|
* @method doDefault |
|
*/ |
|
// doDefault: function () |
|
|
|
/** |
|
* This method is called to process `</tpl>`. It is given the action type that started |
|
* the tpl and the set of additional actions. |
|
* @param {String} type The type of action that is being ended. |
|
* @param {Object} actions The other actions keyed by the attribute name (such as 'exec'). |
|
* @method doEnd |
|
*/ |
|
// doEnd: function (type, actions) |
|
|
|
/** |
|
* This method is called to process `<tpl for="action">`. If there are other attributes, |
|
* these are passed in the actions object. |
|
* @param {String} action |
|
* @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). |
|
* @method doFor |
|
*/ |
|
// doFor: function (action, actions) |
|
|
|
/** |
|
* This method is called to process `<tpl foreach="action">`. If there are other |
|
* attributes, these are passed in the actions object. |
|
* @param {String} action |
|
* @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). |
|
* @method doForEach |
|
*/ |
|
// doForEach: function (action, actions) |
|
|
|
/** |
|
* This method is called to process `<tpl exec="action">`. If there are other attributes, |
|
* these are passed in the actions object. |
|
* @param {String} action |
|
* @param {Object} actions Other actions keyed by the attribute name. |
|
* @method doExec |
|
*/ |
|
// doExec: function (action, actions) |
|
|
|
/** |
|
* This method is called to process an empty `<tpl>`. This is unlikely to need to be |
|
* implemented, so a default (do nothing) version is provided. |
|
* @method |
|
*/ |
|
doTpl: Ext.emptyFn, |
|
|
|
parse: function (str) { |
|
var me = this, |
|
len = str.length, |
|
aliases = { elseif: 'elif' }, |
|
topRe = me.topRe, |
|
actionsRe = me.actionsRe, |
|
index, stack, s, m, t, prev, frame, subMatch, begin, end, actions, |
|
prop, expectTplNext; |
|
|
|
me.level = 0; |
|
me.stack = stack = []; |
|
|
|
for (index = 0; index < len; index = end) { |
|
topRe.lastIndex = index; |
|
m = topRe.exec(str); |
|
|
|
if (!m) { |
|
me.doText(str.substring(index, len)); |
|
break; |
|
} |
|
|
|
begin = m.index; |
|
end = topRe.lastIndex; |
|
|
|
if (index < begin) { |
|
// In the case of a switch statement, we expect a tpl for each case. |
|
// However, if we have spaces they will get matched as plaintext, so |
|
// we want to skip over them here. |
|
s = str.substring(index, begin); |
|
if (!(expectTplNext && Ext.String.trim(s) === '')) { |
|
me.doText(s); |
|
} |
|
} |
|
|
|
expectTplNext = false; |
|
|
|
if (m[1]) { |
|
end = str.indexOf('%}', begin+2); |
|
me.doEval(str.substring(begin+2, end)); |
|
end += 2; |
|
} else if (m[2]) { |
|
end = str.indexOf(']}', begin+2); |
|
me.doExpr(str.substring(begin+2, end)); |
|
end += 2; |
|
} else if (m[3]) { // if ('{' token) |
|
me.doTag(m[3]); |
|
} else if (m[4]) { // content of a <tpl xxxxxx xxx> tag |
|
actions = null; |
|
while ((subMatch = actionsRe.exec(m[4])) !== null) { |
|
s = subMatch[2] || subMatch[3]; |
|
if (s) { |
|
s = Ext.String.htmlDecode(s); // decode attr value |
|
t = subMatch[1]; |
|
t = aliases[t] || t; |
|
actions = actions || {}; |
|
prev = actions[t]; |
|
|
|
if (typeof prev == 'string') { |
|
actions[t] = [prev, s]; |
|
} else if (prev) { |
|
actions[t].push(s); |
|
} else { |
|
actions[t] = s; |
|
} |
|
} |
|
} |
|
|
|
if (!actions) { |
|
if (me.elseRe.test(m[4])) { |
|
me.doElse(); |
|
} else if (me.defaultRe.test(m[4])) { |
|
me.doDefault(); |
|
} else { |
|
me.doTpl(); |
|
stack.push({ type: 'tpl' }); |
|
} |
|
} |
|
else if (actions['if']) { |
|
me.doIf(actions['if'], actions); |
|
stack.push({ type: 'if' }); |
|
} |
|
else if (actions['switch']) { |
|
me.doSwitch(actions['switch'], actions); |
|
stack.push({ type: 'switch' }); |
|
expectTplNext = true; |
|
} |
|
else if (actions['case']) { |
|
me.doCase(actions['case'], actions); |
|
} |
|
else if (actions['elif']) { |
|
me.doElseIf(actions['elif'], actions); |
|
} |
|
else if (actions['for']) { |
|
++me.level; |
|
|
|
// Extract property name to use from indexed item |
|
if (prop = me.propRe.exec(m[4])) { |
|
actions.propName = prop[1] || prop[2]; |
|
} |
|
me.doFor(actions['for'], actions); |
|
stack.push({ type: 'for', actions: actions }); |
|
} |
|
else if (actions['foreach']) { |
|
++me.level; |
|
|
|
// Extract property name to use from indexed item |
|
if (prop = me.propRe.exec(m[4])) { |
|
actions.propName = prop[1] || prop[2]; |
|
} |
|
me.doForEach(actions['foreach'], actions); |
|
stack.push({ type: 'foreach', actions: actions }); |
|
} |
|
else if (actions.exec) { |
|
me.doExec(actions.exec, actions); |
|
stack.push({ type: 'exec', actions: actions }); |
|
} |
|
/* |
|
else { |
|
// todo - error |
|
} |
|
*/ |
|
} else if (m[0].length === 5) { |
|
// if the length of m[0] is 5, assume that we're dealing with an opening tpl tag with no attributes (e.g. <tpl>...</tpl>) |
|
// in this case no action is needed other than pushing it on to the stack |
|
stack.push({ type: 'tpl' }); |
|
} else { |
|
frame = stack.pop(); |
|
me.doEnd(frame.type, frame.actions); |
|
if (frame.type == 'for' || frame.type == 'foreach') { |
|
--me.level; |
|
} |
|
} |
|
} |
|
}, |
|
|
|
// Internal regexes |
|
|
|
topRe: /(?:(\{\%)|(\{\[)|\{([^{}]+)\})|(?:<tpl([^>]*)\>)|(?:<\/tpl>)/g, |
|
actionsRe: /\s*(elif|elseif|if|for|foreach|exec|switch|case|eval|between)\s*\=\s*(?:(?:"([^"]*)")|(?:'([^']*)'))\s*/g, |
|
propRe: /prop=(?:(?:"([^"]*)")|(?:'([^']*)'))/, |
|
defaultRe: /^\s*default\s*$/, |
|
elseRe: /^\s*else\s*$/ |
|
});
|
|
|