diff --git a/.jshintrc b/.jshintrc
index b8c39d2f02..44e550bc7f 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -23,7 +23,9 @@
"Document": true,
"window": true,
"$":true,
+ "t7": true,
"Framework7":true,
+ "Template7":true,
"Event":true,
"DocumentTouch":true,
"app":true
diff --git a/Gruntfile.js b/Gruntfile.js
index 3a0c73cd18..50a15d8986 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -63,7 +63,8 @@ module.exports = function (grunt) {
'src/js/proto-support.js',
'src/js/proto-device.js',
'src/js/proto-plugins.js',
- 'src/js/wrap-end.js'
+ 'src/js/wrap-end.js',
+ 'src/js/template7.js'
];
// Project configuration.
@@ -208,7 +209,7 @@ module.exports = function (grunt) {
if (filename.indexOf('.js') >= 0) {
var addIndent = ' ';
filename = filename.replace('src/js/', '');
- if (filename === 'wrap-start.js' || filename === 'wrap-end.js') {
+ if (filename === 'wrap-start.js' || filename === 'wrap-end.js' || filename === 'template7.js') {
addIndent = '';
}
var add4spaces = ('f7-intro.js f7-outro.js proto-device.js proto-plugins.js proto-support.js dom7-intro.js dom7-outro.js').split(' ');
diff --git a/modules.json b/modules.json
index 838de03c0e..5e2777212b 100644
--- a/modules.json
+++ b/modules.json
@@ -43,7 +43,8 @@
"src/js/proto-support.js",
"src/js/proto-device.js",
"src/js/proto-plugins.js",
- "src/js/wrap-end.js"
+ "src/js/wrap-end.js",
+ "src/js/template7.js"
],
"less" : [
"src/less/login-screen.less",
diff --git a/src/js/f7-intro.js b/src/js/f7-intro.js
index 75d1c8b58e..e8fbd32fc8 100644
--- a/src/js/f7-intro.js
+++ b/src/js/f7-intro.js
@@ -56,15 +56,6 @@ window.Framework7 = function (params) {
swipePanelThreshold: 0,
panelsCloseByOutside: true,
// Modals
- modalTemplate: '
',
- modalActionsTemplate: '{{buttons}}
',
modalButtonOk: 'OK',
modalButtonCancel: 'Cancel',
modalUsernamePlaceholder: 'Username',
@@ -96,6 +87,9 @@ window.Framework7 = function (params) {
// DOM lib
var $ = Dom7;
+ // Template7 lib
+ var t7 = Template7;
+
// Touch events
app.touchEvents = {
start: app.support.touch ? 'touchstart' : 'mousedown',
diff --git a/src/js/modals.js b/src/js/modals.js
index b9b21439c3..efdb8b903a 100644
--- a/src/js/modals.js
+++ b/src/js/modals.js
@@ -4,39 +4,24 @@
var _modalTemplateTempDiv = document.createElement('div');
app.modal = function (params) {
params = params || {};
- /* @params example
- {
- title: 'Modal title',
- text: 'Modal text',
- afterText: 'Custom content after text',
- buttons: [{
- text:'Cancel',
- bold: true,
- onClick: function (){},
- close:false
- }],
- onClick: function(index){}
- }
- */
- var buttonsHTML = '';
- if (params.buttons && params.buttons.length > 0) {
- for (var i = 0; i < params.buttons.length; i++) {
- buttonsHTML += '' + params.buttons[i].text + '';
- }
- }
- var modalTemplate = app.params.modalTemplate;
- if (!params.title) {
- modalTemplate = modalTemplate.split('{{if title}}')[0] + modalTemplate.split('{{/if title}}')[1];
+ var modalHTML = '';
+ if (app.params.modalTemplate) {
+ modalHTML = t7(app.params.modalTemplate, params);
}
else {
- modalTemplate = modalTemplate.replace(/{{if\ title}}/g, '').replace(/{{\/if\ title}}/g, '');
+ var buttonsHTML = '';
+ if (params.buttons && params.buttons.length > 0) {
+ for (var i = 0; i < params.buttons.length; i++) {
+ buttonsHTML += '' + params.buttons[i].text + '';
+ }
+ }
+ var titleHTML = params.title ? '' + params.title + '
' : '';
+ var textHTML = params.title ? '' + params.text + '
' : '';
+ var afterTextHTML = params.afterText ? params.afterText : '';
+ var noButtons = !params.buttons || params.buttons.length === 0 ? 'modal-no-buttons' : '';
+ modalHTML = '';
}
- var modalHTML = modalTemplate
- .replace(/{{title}}/g, params.title || '')
- .replace(/{{text}}/g, params.text || '')
- .replace(/{{afterText}}/g, params.afterText || '')
- .replace(/{{buttons}}/g, buttonsHTML)
- .replace(/{{noButtons}}/g, !params.buttons || params.buttons.length === 0 ? 'modal-no-buttons' : '');
+
_modalTemplateTempDiv.innerHTML = modalHTML;
var modal = $(_modalTemplateTempDiv).children();
@@ -180,21 +165,27 @@ app.actions = function (params) {
if (params.length > 0 && !$.isArray(params[0])) {
params = [params];
}
+ var modalHTML;
- var actionsTemplate = app.params.modalActionsTemplate;
- var buttonsHTML = '';
- for (var i = 0; i < params.length; i++) {
- for (var j = 0; j < params[i].length; j++) {
- if (j === 0) buttonsHTML += '';
- var button = params[i][j];
- var buttonClass = button.label ? 'actions-modal-label' : 'actions-modal-button';
- if (button.bold) buttonClass += ' actions-modal-button-bold';
- if (button.color) buttonClass += ' color-' + button.color;
- buttonsHTML += '' + button.text + '';
- if (j === params[i].length - 1) buttonsHTML += '
';
+ if (app.params.modalActionsTemplate) {
+ modalHTML = t7(app.params.modalActionsTemplate, params);
+ }
+ else {
+ var buttonsHTML = '';
+ for (var i = 0; i < params.length; i++) {
+ for (var j = 0; j < params[i].length; j++) {
+ if (j === 0) buttonsHTML += '';
+ var button = params[i][j];
+ var buttonClass = button.label ? 'actions-modal-label' : 'actions-modal-button';
+ if (button.bold) buttonClass += ' actions-modal-button-bold';
+ if (button.color) buttonClass += ' color-' + button.color;
+ buttonsHTML += '' + button.text + '';
+ if (j === params[i].length - 1) buttonsHTML += '
';
+ }
}
+ modalHTML = '' + buttonsHTML + '
';
}
- var modalHTML = actionsTemplate.replace(/{{buttons}}/g, buttonsHTML);
+
_modalTemplateTempDiv.innerHTML = modalHTML;
var modal = $(_modalTemplateTempDiv).children();
@@ -406,7 +397,7 @@ app.closeModal = function (modal) {
overlay.removeClass('modal-overlay-visible');
modal.trigger('close');
-
+
if (!isPopover) {
modal.removeClass('modal-in').addClass('modal-out').transitionEnd(function (e) {
if (modal.hasClass('modal-out')) modal.trigger('closed');
@@ -414,7 +405,9 @@ app.closeModal = function (modal) {
if (isPopup || isLoginScreen) {
modal.removeClass('modal-out').hide();
- if (removeOnClose && modal.length > 0) modal.remove();
+ if (removeOnClose && modal.length > 0) {
+ modal.remove();
+ }
}
else {
modal.remove();
@@ -423,7 +416,9 @@ app.closeModal = function (modal) {
}
else {
modal.removeClass('modal-in modal-out').trigger('closed').hide();
- if (removeOnClose) modal.remove();
+ if (removeOnClose) {
+ modal.remove();
+ }
}
return true;
};
diff --git a/src/js/template7.js b/src/js/template7.js
new file mode 100644
index 0000000000..874ce33d6b
--- /dev/null
+++ b/src/js/template7.js
@@ -0,0 +1,341 @@
+/*===========================
+Template7 Template engine
+===========================*/
+window.Template7 = (function () {
+ 'use strict';
+ function isArray(arr) {
+ return Object.prototype.toString.apply(arr) === '[object Array]';
+ }
+ function isObject(obj) {
+ return obj instanceof Object;
+ }
+ function isFunction(func) {
+ return typeof func === 'function';
+ }
+ var cache = {};
+ function stringToBlocks(string) {
+ var blocks = [], i, j, k;
+ if (!string) return [];
+ var _blocks = string.split(/({{[^{^}]*}})/);
+ for (i = 0; i < _blocks.length; i++) {
+ var block = _blocks[i];
+ if (block === '') continue;
+ if (block.indexOf('{{') < 0) {
+ blocks.push({
+ type: 'plain',
+ content: block
+ });
+ }
+ else {
+ if (block.indexOf('{/') >= 0) {
+ continue;
+ }
+ if (block.indexOf('{#') < 0 && block.indexOf(' ') < 0 && block.indexOf('else') < 0) {
+ // Simple variable
+ blocks.push({
+ type: 'variable',
+ contextName: block.replace(/[{}]/g, '')
+ });
+ continue;
+ }
+ // Helpers
+ var helperSlices = block.replace(/[{}#}]/g, '').split(' ');
+ var helperName = helperSlices[0];
+ var helperContext = helperSlices[1];
+ var helperHash = {};
+
+ if (helperSlices.length > 2) {
+ var hashRes;
+ var reg = new RegExp(/\b(\w+)=["']([^"']+)(?=["'])/g);
+ while ((hashRes = reg.exec(block)) !== null) {
+ helperHash[hashRes[1]] = hashRes[2] === 'false' ? false : hashRes[2];
+ }
+ }
+
+ if (block.indexOf('{#') >= 0) {
+ // Condition/Helper
+ var helperStartIndex = i;
+ var helperContent = '';
+ var elseContent = '';
+ var toSkip = 0;
+ var shiftIndex;
+ var foundClosed = false, foundElse = false, foundClosedElse = false, depth = 0;
+ for (j = i + 1; j < _blocks.length; j++) {
+ if (_blocks[j].indexOf('{{#') >= 0) {
+ depth ++;
+ }
+ if (_blocks[j].indexOf('{{/') >= 0) {
+ depth --;
+ }
+ if (_blocks[j].indexOf('{{#' + helperName) >= 0) {
+ helperContent += _blocks[j];
+ if (foundElse) elseContent += _blocks[j];
+ toSkip ++;
+ }
+ else if (_blocks[j].indexOf('{{/' + helperName) >= 0) {
+ if (toSkip > 0) {
+ toSkip--;
+ helperContent += _blocks[j];
+ if (foundElse) elseContent += _blocks[j];
+ }
+ else {
+ shiftIndex = j;
+ foundClosed = true;
+ break;
+ }
+ }
+ else if (_blocks[j].indexOf('else') >= 0 && depth === 0) {
+ foundElse = true;
+ }
+ else {
+ if (!foundElse) helperContent += _blocks[j];
+ if (foundElse) elseContent += _blocks[j];
+ }
+
+ }
+ if (foundClosed) {
+ if (shiftIndex) i = shiftIndex;
+ blocks.push({
+ type: 'helper',
+ helperName: helperName,
+ contextName: helperContext,
+ content: helperContent,
+ inverseContent: elseContent,
+ hash: helperHash
+ });
+ }
+ }
+ else if (block.indexOf(' ') > 0) {
+ blocks.push({
+ type: 'helper',
+ helperName: helperName,
+ contextName: helperContext,
+ hash: helperHash
+ });
+ }
+ }
+ }
+ return blocks;
+ }
+ var Template7 = function (template, data) {
+ var t = this;
+ t.template = template;
+ t.context = data;
+
+ function getContext(contextName, context, privateData) {
+ if (contextName === 'this' || typeof contextName === 'undefined') {
+ return context;
+ }
+
+ if (contextName.indexOf('@') >= 0) {
+ //private data
+ var privateVarName = contextName.replace(/[@ ]/g, '');
+ if (privateData && typeof privateData[privateVarName] !== 'undefined') return privateData[privateVarName];
+ }
+
+ if (contextName.indexOf('.') > 0 && contextName.indexOf('../') < 0) {
+ var dataPath = contextName.split('.');
+ var _context = context;
+ for (var j = 0; j < dataPath.length; j++) {
+ if (dataPath[j] === 'this') {
+ _context = context;
+ }
+ else if (typeof _context !== 'undefined' && isObject(context) && dataPath[j] in _context) {
+ _context = _context[dataPath[j]];
+ }
+ else {
+ _context = undefined;
+ }
+ }
+ context = _context;
+ }
+ else if (contextName.indexOf('../') >= 0) {
+ }
+ else if (context[contextName]) {
+ context = context[contextName];
+ }
+ else {
+ context = undefined;
+ }
+ return context;
+ }
+ function createOptions(block, privateData) {
+ return {
+ fn: function (newContext, privateData) {
+ return render(block.content, newContext, privateData);
+ },
+ inverse: function (newContext, privateData) {
+ return block.inverseContent ? render(block.inverseContent, newContext, privateData) : '';
+ },
+ content: block.content,
+ inverseContent: block.inverseContent,
+ hash: block.hash || {},
+ helpers: t.helpers,
+ data: privateData
+ };
+ }
+ function render(template, data, privateData) {
+ template = template || t.template;
+ var scope = data || t.context;
+ var blocks = stringToBlocks(template);
+ var resultString = '';
+ var i, j, context;
+ for (i = 0; i < blocks.length; i++) {
+ var block = blocks[i];
+ // Plain block
+ if (block.type === 'plain') {
+ resultString += block.content;
+ continue;
+ }
+ // Variable block
+ if (block.type === 'variable') {
+ context = getContext(block.contextName, scope, privateData);
+ if (typeof context !== 'undefined') {
+ resultString += context.toString();
+ }
+ }
+ // Helpers block
+ if (block.type === 'helper') {
+ context = getContext(block.contextName, scope, privateData);
+ // Create options
+ var options = createOptions(block, privateData);
+ var helperResult;
+ var helperName = block.helperName;
+ if (block.helperName in t.helpers) {
+ helperResult = t.helpers[helperName].call(scope, context, options);
+ }
+ else {
+ if (!block.contextName && (helperName === 'this' || (isObject(context) && helperName in context))) {
+ var _context = helperName === 'this' ? context : context[helperName];
+ if (isArray(_context)) helperResult = t.helpers.each.call(scope, context[helperName], options);
+ else helperResult = render(block.content, context[helperName]);
+ }
+ else {
+ throw new Error('Missing helper: "' + helperName + '"');
+ }
+ }
+ if (typeof helperResult !== 'undefined') {
+ resultString += helperResult.toString();
+ }
+ }
+ }
+ return resultString;
+ }
+ t.render = function (data) {
+ t.context = data;
+ t.prevContext = undefined;
+ var rendered, cached, id;
+ if (t.options.cache) {
+ for (var key in cache) {
+ if (key.indexOf('template_') >= 0 && cache[key] === t.template) {
+ id = key.split('template_')[1];
+ if (cache['data_' + id] === JSON.stringify(data)) cached = cache['rendered_' + id];
+ }
+ }
+ if (cached) return cached;
+ }
+ rendered = render(t.template, data);
+ if (t.options.cache) {
+ id = new Date().getTime();
+ cache['template_' + id] = t.template;
+ cache['data_' + id] = JSON.stringify(data);
+ cache['rendered_' + id] = rendered;
+ }
+ return rendered;
+ };
+ };
+ Template7.prototype = {
+ options: {
+ cache: true
+ },
+ helpers: {
+ 'if': function (context, options) {
+ if (isFunction(context)) { context = context.call(this); }
+ if (context) {
+ return options.fn(this, options.data);
+ }
+ else {
+ return options.inverse(this, options.data);
+ }
+ },
+ 'unless': function (context, options) {
+ if (isFunction(context)) { context = context.call(this); }
+ if (!context) {
+ return options.fn(this, options.data);
+ }
+ else {
+ return options.inverse(this, options.data);
+ }
+ },
+ 'each': function (context, options) {
+ var ret = '', i = 0;
+ if (isFunction(context)) { context = context.call(this); }
+ if (isArray(context)) {
+ if (options.hash.reverse && !context.t7Reversed) {
+ context = context.reverse();
+ context.t7Reversed = true;
+ }
+ if (!options.hash.reverse && context.t7Reversed) {
+ context = context.reverse();
+ context.t7Reversed = false;
+ }
+ for (i = 0; i < context.length; i++) {
+ ret += options.fn(context[i], {first: i === 0, last: i === context.length - 1, index: i});
+ }
+ }
+ else {
+ for (var key in context) {
+ i++;
+ ret += options.fn(context[key], {key: key});
+ }
+ }
+ if (i > 0) return ret;
+ else return options.inverse(this);
+ },
+ 'with': function (context, options) {
+ if (isFunction(context)) { context = context.call(this); }
+ return options.fn(context);
+ },
+ 'join': function (context, options) {
+ if (isFunction(context)) { context = context.call(this); }
+ return context.join(options.hash.delimeter);
+ }
+ }
+ };
+ var t7 = function (template, data) {
+ if (arguments.length === 2) {
+ var instance = new Template7(template);
+ var rendered = instance.render(data);
+ instance = null;
+ return (rendered);
+ }
+ else return new Template7(template, data);
+ };
+ t7.registerHelper = function (name, fn) {
+ Template7.prototype.helpers[name] = fn;
+ };
+ t7.cache = cache;
+ t7.clearCache = function (arg) {
+ if (arguments.length === 0) {
+ cache = {};
+ return;
+ }
+ var key, id;
+ // Clear by passed data
+ for (key in cache) {
+ var lookFor = typeof arg === 'string' ? 'template_' : 'data_';
+ var compareWith = typeof arg === 'string' ? arg : JSON.strigify(arg);
+ if (key.indexOf(lookFor) >= 0 && cache[key] === arg) {
+ id = key.split('_')[1];
+ }
+ }
+ if (id) {
+ delete cache['template_' + id];
+ delete cache['data_' + id];
+ delete cache['rendered_' + id];
+ }
+ };
+ t7.options = Template7.prototype.options;
+ t7.helpers = Template7.prototype.helpers;
+ return t7;
+})();
\ No newline at end of file
diff --git a/src/js/wrap-end.js b/src/js/wrap-end.js
index 158693a025..0319a0fe5f 100644
--- a/src/js/wrap-end.js
+++ b/src/js/wrap-end.js
@@ -1 +1 @@
-})();
\ No newline at end of file
+})();