Permalink
Browse files

Better filter parsing/compiling into templates. A million times bette…

…r, and with full (as close as can be) date format support.
  • Loading branch information...
1 parent e285cdd commit 7b53ae4d6e280fb4dc08a3eb16a80f1299cb2b08 @paularmstrong paularmstrong committed Aug 7, 2011
Showing with 210 additions and 161 deletions.
  1. +3 −2 index.js
  2. +133 −111 lib/filters.js
  3. +26 −2 lib/parser.js
  4. +48 −46 tests/filters.test.js
View
@@ -8,6 +8,7 @@ var fs = require("fs"),
tags = require("tags"),
parser = require("parser"),
widgets = require("widgets"),
+ filters = require('filters'),
CACHE = {},
DEBUG = false,
@@ -50,7 +51,7 @@ createTemplate = function (data, id) {
}
// The compiled render function - this is all we need
- render = new Function("__context", "__parents", "__widgets",
+ render = new Function("__context", "__parents", "__filters", "__widgets",
[ '__parents = __parents ? __parents.slice() : [];'
// Prevents circular includes (which will crash node without warning)
, 'for (var i=0, j=__parents.length; i<j; ++i) {'
@@ -67,7 +68,7 @@ createTemplate = function (data, id) {
);
template.render = function (context, parents) {
- return render.call(this, context, parents, widgets);
+ return render.call(this, context, parents, filters, widgets);
};
return template;
View
@@ -1,6 +1,6 @@
var helpers = require('./helpers'),
escape = helpers.escape,
- _filters, _dateFormats;
+ _dateFormats;
_dateFormats = {
_months: {
@@ -13,144 +13,166 @@ _dateFormats = {
alt: {'-1': 'Yesterday', 0: 'Today', 1: 'Tomorrow'}
},
// Day
- d: "function () { return (this.getDate() < 10 ? '0' : '') + this.getDate(); }",
- D: "function () { return _dateFormats._days.abbr[this.getDay()]; }",
- j: "function () { return this.getDate(); }",
- l: "function () { return _dateFormats._days.full[this.getDay()]; }",
- N: "function () { return this.getDay(); }",
- S: "function () { return (this.getDate() % 10 === 1 && this.getDate() !== 11 ? 'st' : (this.getDate() % 10 === 2 && this.getDate() !== 12 ? 'nd' : (this.getDate() % 10 === 3 && this.getDate() !== 13 ? 'rd' : 'th'))); }",
- w: "function () { return this.getDay() - 1; }",
- //z: function () { return ''; },
+ d: function (input) {
+ return (input.getDate() < 10 ? '0' : '') + input.getDate();
+ },
+ D: function (input) {
+ return _dateFormats._days.abbr[input.getDay()];
+ },
+ j: function (input) {
+ return input.getDate();
+ },
+ l: function (input) {
+ return _dateFormats._days.full[input.getDay()];
+ },
+ N: function (input) {
+ return input.getDay();
+ },
+ S: function (input) {
+ return (input.getDate() % 10 === 1 && input.getDate() !== 11 ? 'st' : (input.getDate() % 10 === 2 && input.getDate() !== 12 ? 'nd' : (input.getDate() % 10 === 3 && input.getDate() !== 13 ? 'rd' : 'th')));
+ },
+ w: function (input) {
+ return input.getDay() - 1;
+ },
+ //z = function (input) { return ''; },
// Week
- //W: function () { return ''; },
+ //W = function (input) { return ''; },
// Month
- F: "function () { return _dateFormats._months.full[this.getMonth()]; }",
- m: "function () { return (this.getMonth() < 8 ? '0' : '') + (this.getMonth() + 1); }",
- M: "function () { return _dateFormats._months.abbr[this.getMonth()]; }",
- n: "function () { return this.getMonth() + 1; }",
- //t: function () { return ''; },
+ F: function (input) {
+ return _dateFormats._months.full[input.getMonth()];
+ },
+ m: function (input) {
+ return (input.getMonth() < 8 ? '0' : '') + (input.getMonth() + 1);
+ },
+ M: function (input) {
+ return _dateFormats._months.abbr[input.getMonth()];
+ },
+ n: function (input) {
+ return input.getMonth() + 1;
+ },
+ //t = function (input) { return ''; },
// Year
- //L: function () { return ''; },
- //o: function () { return ''; },
- Y: "function () { return this.getFullYear(); }",
- y: "function () { return ('' + this.getFullYear()).substr(2); }",
+ //L = function (input) { return ''; },
+ //o = function (input) { return ''; },
+ Y: function (input) {
+ return input.getFullYear();
+ },
+ y: function (input) {
+ return ('' + input.getFullYear()).substr(2);
+ },
// Time
- a: "function () { return this.getHours() < 12 ? 'am' : 'pm'; }",
- A: "function () { return this.getHours() < 12 ? 'AM' : 'PM'; }",
- //B: function () { return ''; },
- g: "function () { return this.getHours() === 0 ? 12 : (this.getHours() > 12 ? this.getHours() - 12 : this.getHours()); }",
- G: "function () { return this.getHours(); }",
- h: "function () { return (this.getHours() < 10 || (12 < this.getHours() < 22) ? '0' : '') + (this.getHours() < 10 ? this.getHours() : this.getHours() - 12); }",
- H: "function () { return (this.getHours() < 10 ? '0' : '') + this.getHours(); }",
- i: "function () { return (this.getMinutes() < 10 ? '0' : '') + this.getMinutes(); }",
- s: "function () { return (this.getSeconds() < 10 ? '0' : '') + this.getSeconds(); }",
- //u: function () { return ''; },
-
- // Timezone
- //e: function () { return ''; },
- //I: function () { return ''; },
- O: "function () { return (this.getTimezoneOffset() < 0 ? '-' : '+') + (this.getTimezoneOffset() / 60 < 10 ? '0' : '') + (this.getTimezoneOffset() / 60) + '00'; }",
- //T: function () { return ''; },
- Z: "function () { return this.getTimezoneOffset() * 60; }",
-
- // Full Date/Time
- //c: function () { return ''; },
- r: "function () { return this.toString(); }",
- U: "function () { return this.getTime() / 1000; }"
-};
-
-_filters = {
- lower: function () {
- return '(function () { return ' + this + '.toString().toLowerCase(); })()';
+ a: function (input) {
+ return input.getHours() < 12 ? 'am' : 'pm';
},
-
- upper: function () {
- return '(function () { return ' + this + '.toString().toUpperCase(); })()';
+ A: function (input) {
+ return input.getHours() < 12 ? 'AM' : 'PM';
},
-
- capitalize: function () {
- return '(function () { return ' + this + '.toString().charAt(0).toUpperCase() + ' + this + '.toString().substr(1).toLowerCase(); })()';
+ //B = function () { return ''; },
+ g: function (input) {
+ return input.getHours() === 0 ? 12 : (input.getHours() > 12 ? input.getHours() - 12 : input.getHours());
},
-
- title: function () {
- return '(function () { return ' + this + '.toString().replace(/\\w\\S*/g, function (str) { return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase(); }); })()';
+ G: function (input) {
+ return input.getHours();
},
-
- join: function (separator) {
- return '(function () { if (Array.isArray(' + this + ')) { return ' + this + '.join(' + separator + '); } else { return ' + this + ' } })()';
+ h: function (input) {
+ return (input.getHours() < 10 || (12 < input.getHours() < 22) ? '0' : '') + (input.getHours() < 10 ? input.getHours() : input.getHours() - 12);
},
-
- length: function () {
- return '(function () { return ' + this + '.length; })()';
+ H: function (input) {
+ return (input.getHours() < 10 ? '0' : '') + input.getHours();
},
-
- url_encode: function () {
- return '(function () { return encodeURIComponent(' + this + '); })()';
+ i: function (input) {
+ return (input.getMinutes() < 10 ? '0' : '') + input.getMinutes();
},
-
- url_decode: function () {
- return '(function () { return decodeURIComponent(' + this + '); })()';
+ s: function (input) {
+ return (input.getSeconds() < 10 ? '0' : '') + input.getSeconds();
},
+ //u = function () { return ''; },
- json_encode: function () {
- return '(function () { return JSON.stringify(' + this + '); })()';
+ // Timezone
+ //e = function () { return ''; },
+ //I = function () { return ''; },
+ O: function (input) {
+ return (input.getTimezoneOffset() < 0 ? '-' : '+') + (input.getTimezoneOffset() / 60 < 10 ? '0' : '') + (input.getTimezoneOffset() / 60) + '00';
},
-
- striptags: function () {
- return '(function () { return ' + this + '.toString().replace(/(<([^>]+)>)/ig, ""); })()';
+ //T = function () { return ''; },
+ Z: function (input) {
+ return input.getTimezoneOffset() * 60;
},
- date: function (format) {
- var formatters = [], l = format.length, cur, t;
- while (l--) {
- cur = format.charAt(l);
- formatters.push((_dateFormats.hasOwnProperty(cur)) ? 'b = ' + _dateFormats[cur] : cur);
- }
- formatters.shift();
- formatters.pop();
- return [
- '(function () {'
- , 'var out = "", i = 0, cur,'
- , 'formatters = ' + JSON.stringify(formatters) + ','
- , 'date = new Date(' + this + ');'
- , 'for (i; i < ' + formatters.length + '; i += 1) {'
- , 'if (typeof eval(formatters[i]) === "function") {'
- , 'out += eval(formatters[i]).call(date);'
- , '} else {'
- , 'out += formatters[i];'
- , '}'
- , '}'
- , 'return out;'
- , '})()'
- ].join('');
+ // Full Date/Time
+ //c = function () { return ''; },
+ r: function (input) {
+ return input.toString();
+ },
+ U: function (input) {
+ return input.getTime() / 1000;
}
};
-function wrapFilter(variable, filter) {
- var matches = filter.match(/(\w*)\((.*)\)/),
- output = variable;
- if (matches && matches.length && _filters.hasOwnProperty(matches[1])) {
- output = _filters[matches[1]].call(variable, matches[2]);
+exports.lower = function (input) {
+ return input.toString().toLowerCase();
+};
+
+exports.upper = function (input) {
+ return input.toString().toUpperCase();
+};
+
+exports.capitalize = function (input) {
+ return input.toString().charAt(0).toUpperCase() + input.toString().substr(1).toLowerCase();
+};
+
+exports.title = function (input) {
+ return input.toString().replace(/\w\S*/g, function (str) {
+ return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase();
+ });
+};
+
+exports.join = function (input, separator) {
+ if (Array.isArray(input)) {
+ return input.join(separator);
} else {
- output = _filters[filter].call(variable);
+ return input;
}
+};
- return output;
-}
+exports.length = function (input) {
+ return input.length;
+};
-exports.wrap = function (variable, filters, context) {
- var output = escape(variable, context);
+exports.url_encode = function (input) {
+ return encodeURIComponent(input);
+};
- if (filters && filters.length > 0) {
- filters.forEach(function (filter) {
- output = wrapFilter(output, filter);
- });
- }
+exports.url_decode = function (input) {
+ return decodeURIComponent(input);
+};
- return output;
+exports.json_encode = function (input) {
+ return JSON.stringify(input);
+};
+
+exports.striptags = function (input) {
+ return input.toString().replace(/(<([^>]+)>)/ig, "");
+};
+
+exports.date = function (input, format) {
+ var l = format.length,
+ date = new Date(input),
+ cur, i = 0,
+ out = '';
+
+ for (i; i < l; i += 1) {
+ cur = format.charAt(i);
+ if (_dateFormats.hasOwnProperty(cur)) {
+ out += _dateFormats[cur](date);
+ } else {
+ out += cur;
+ }
+ }
+ return out;
};
View
@@ -75,6 +75,30 @@ exports.parse = function (data, tags) {
return stack[index];
};
+function wrapFilter(variable, filter) {
+ var matches = filter.match(/(\w*)\((.*)\)/),
+ output = variable;
+
+ if (matches && matches.length && filters.hasOwnProperty(matches[1])) {
+ output = '__filters["' + matches[1] + '"](' + variable + ', ' + matches[2] + ')';
+ } else {
+ output = '__filters["' + filter + '"](' + variable + ')';
+ }
+
+ return output;
+}
+
+function wrapFilters(variable, filters, context) {
+ var output = helpers.escape(variable, context);
+
+ if (filters && filters.length > 0) {
+ filters.forEach(function (filter) {
+ output = wrapFilter(output, filter);
+ });
+ }
+
+ return output;
+}
exports.compile = function compile(indent) {
var code = [''],
@@ -132,9 +156,9 @@ exports.compile = function compile(indent) {
varOutput = token.name.split('|');
return code.push(
'if (' + check(varOutput[0]) + ') {'
- , ' __output.push(' + filters.wrap(varOutput[0], varOutput.slice(1)) + ');'
+ , ' __output.push(' + wrapFilters(varOutput[0], varOutput.slice(1)) + ');'
, '} else if (' + check(varOutput[0], '__context') + ') {'
- , ' __output.push(' + filters.wrap(varOutput[0], varOutput.slice(1), '__context') + ');'
+ , ' __output.push(' + wrapFilters(varOutput[0], varOutput.slice(1), '__context') + ');'
, '}'
);
}
Oops, something went wrong.

0 comments on commit 7b53ae4

Please sign in to comment.