Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
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...
commit 7b53ae4d6e280fb4dc08a3eb16a80f1299cb2b08 1 parent e285cdd
@paularmstrong paularmstrong authored
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
5 index.js
@@ -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
244 lib/filters.js
@@ -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
28 lib/parser.js
@@ -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') + ');'
, '}'
);
}
View
94 tests/filters.test.js
@@ -2,112 +2,114 @@ var filters = require('../lib/filters');
exports.lower = function (test) {
var input = 'BaR';
- test.strictEqual('bar', eval(filters.wrap('input', ['lower'])));
+ test.strictEqual('bar', filters.lower(input));
input = 345;
- test.strictEqual('345', eval(filters.wrap('input', ['lower'])));
+ test.strictEqual('345', filters.lower(input));
test.done();
};
exports.upper = function (test) {
var input = 'bar';
- test.strictEqual('BAR', eval(filters.wrap('input', ['upper'])));
+ test.strictEqual('BAR', filters.upper(input));
input = 345;
- test.strictEqual('345', eval(filters.wrap('input', ['upper'])));
+ test.strictEqual('345', filters.upper(input));
test.done();
};
exports.capitalize = function (test) {
var input = 'awesome sauce.';
- test.strictEqual('Awesome sauce.', eval(filters.wrap('input', ['capitalize'])));
+ test.strictEqual('Awesome sauce.', filters.capitalize(input));
input = 345;
- test.strictEqual('345', eval(filters.wrap('input', ['capitalize'])));
+ test.strictEqual('345', filters.capitalize(input));
test.done();
};
exports.title = function (test) {
var input = 'this is title case';
- test.strictEqual('This Is Title Case', eval(filters.wrap('input', ['title'])));
+ test.strictEqual('This Is Title Case', filters.title(input));
test.done();
};
exports.join = function (test) {
var input = [1, 2, 3];
- test.strictEqual('1+2+3', eval(filters.wrap('input', ['join(\'+\')'])));
- test.strictEqual('1 * 2 * 3', eval(filters.wrap('input', ['join(\' * \')'])));
+ test.strictEqual('1+2+3', filters.join(input, '+'));
+ test.strictEqual('1 * 2 * 3', filters.join(input, ' * '));
input = 'asdf';
- test.strictEqual('asdf', eval(filters.wrap('input', ['join(\'-\')'])), 'Non-array input is not joined.');
+ test.strictEqual('asdf', filters.join(input, '-'), 'Non-array input is not joined.');
test.done();
};
exports.length = function (test) {
var input = [1, 2, 3];
- test.strictEqual(3, eval(filters.wrap('input', ['length'])));
+ test.strictEqual(3, filters.length(input));
input = 'foobar';
- test.strictEqual(6, eval(filters.wrap('input', ['length'])));
+ test.strictEqual(6, filters.length(input));
test.done();
};
exports.url_encode = function (test) {
var input = "param=1&anotherParam=2";
- test.strictEqual("param%3D1%26anotherParam%3D2", eval(filters.wrap('input', ['url_encode'])));
+ test.strictEqual("param%3D1%26anotherParam%3D2", filters.url_encode(input));
test.done();
};
exports.url_decode = function (test) {
var input = "param%3D1%26anotherParam%3D2";
- test.strictEqual("param=1&anotherParam=2", eval(filters.wrap('input', ['url_decode'])));
+ test.strictEqual("param=1&anotherParam=2", filters.url_decode(input));
test.done();
};
exports.json_encode = function (test) {
var input = { foo: 'bar', baz: [1, 2, 3] };
- test.strictEqual('{"foo":"bar","baz":[1,2,3]}', eval(filters.wrap('input', ['json_encode'])));
+ test.strictEqual('{"foo":"bar","baz":[1,2,3]}', filters.json_encode(input));
test.done();
};
exports.striptags = function (test) {
var input = '<h1>foo</h1> <div class="blah">hi</div>';
- test.strictEqual('foo hi', eval(filters.wrap('input', ['striptags'])));
+ test.strictEqual('foo hi', filters.striptags(input));
test.done();
};
exports.multiple = function (test) {
var input = ['aWEsoMe', 'sAuCe'];
- test.strictEqual('Awesome Sauce', eval(filters.wrap('input', ['join(\' \')', 'title'])));
+ test.strictEqual('Awesome Sauce', filters.title(filters.join(input, ' ')));
test.done();
};
exports.date = function (test) {
var input = 'Sat Aug 06 2011 09:05:02 GMT-0700 (PDT)';
- test.strictEqual('06', eval(filters.wrap('input', ['date("d")'])), 'format: d http://www.php.net/date');
- // test.strictEqual('Sat', eval(filters.wrap('input', ['date("D")'])), 'format: D http://www.php.net/date');
- test.strictEqual('6', eval(filters.wrap('input', ['date("j")'])), 'format: j http://www.php.net/date');
- // test.strictEqual('Saturday', eval(filters.wrap('input', ['date("l")'])), 'format: l http://www.php.net/date');
- test.strictEqual('6', eval(filters.wrap('input', ['date("N")'])), 'format: N http://www.php.net/date');
- test.strictEqual('th', eval(filters.wrap('input', ['date("S")'])), 'format: S http://www.php.net/date');
- test.strictEqual('5', eval(filters.wrap('input', ['date("w")'])), 'format: w http://www.php.net/date');
- // test.strictEqual('August', eval(filters.wrap('input', ['date("F")'])), 'format: F http://www.php.net/date');
- test.strictEqual('08', eval(filters.wrap('input', ['date("m")'])), 'format: m http://www.php.net/date');
- // test.strictEqual('Aug', eval(filters.wrap('input', ['date("M")'])), 'format: M http://www.php.net/date');
- test.strictEqual('8', eval(filters.wrap('input', ['date("n")'])), 'format: n http://www.php.net/date');
-
- test.strictEqual('2011', eval(filters.wrap('input', ['date("Y")'])), 'format: Y http://www.php.net/date');
- test.strictEqual('11', eval(filters.wrap('input', ['date("y")'])), 'format: y http://www.php.net/date');
- test.strictEqual('2011', eval(filters.wrap('input', ['date("Y")'])), 'format: Y http://www.php.net/date');
- test.strictEqual('am', eval(filters.wrap('input', ['date("a")'])), 'format: a http://www.php.net/date');
- test.strictEqual('AM', eval(filters.wrap('input', ['date("A")'])), 'format: A http://www.php.net/date');
- test.strictEqual('9', eval(filters.wrap('input', ['date("g")'])), 'format: g http://www.php.net/date');
- test.strictEqual('9', eval(filters.wrap('input', ['date("G")'])), 'format: G http://www.php.net/date');
- test.strictEqual('09', eval(filters.wrap('input', ['date("h")'])), 'format: h http://www.php.net/date');
- test.strictEqual('09', eval(filters.wrap('input', ['date("H")'])), 'format: H http://www.php.net/date');
- test.strictEqual('05', eval(filters.wrap('input', ['date("i")'])), 'format: i http://www.php.net/date');
- test.strictEqual('02', eval(filters.wrap('input', ['date("s")'])), 'format: s http://www.php.net/date');
-
- test.strictEqual('+0700', eval(filters.wrap('input', ['date("O")'])), 'format: O http://www.php.net/date');
- test.strictEqual('25200', eval(filters.wrap('input', ['date("Z")'])), 'format: Z http://www.php.net/date');
- test.strictEqual('Sat Aug 06 2011 09:05:02 GMT-0700 (PDT)', eval(filters.wrap('input', ['date("r")'])), 'format: r http://www.php.net/date');
- test.strictEqual('1312646702', eval(filters.wrap('input', ['date("U")'])), 'format: U http://www.php.net/date');
+ test.strictEqual('06', filters.date(input, "d"), 'format: d http://www.php.net/date');
+ test.strictEqual('Sat', filters.date(input, "D"), 'format: D http://www.php.net/date');
+ test.strictEqual('6', filters.date(input, "j"), 'format: j http://www.php.net/date');
+ test.strictEqual('Saturday', filters.date(input, "l"), 'format: l http://www.php.net/date');
+ test.strictEqual('6', filters.date(input, "N"), 'format: N http://www.php.net/date');
+ test.strictEqual('th', filters.date(input, "S"), 'format: S http://www.php.net/date');
+ test.strictEqual('5', filters.date(input, "w"), 'format: w http://www.php.net/date');
+ test.strictEqual('August', filters.date(input, "F"), 'format: F http://www.php.net/date');
+ test.strictEqual('08', filters.date(input, "m"), 'format: m http://www.php.net/date');
+ test.strictEqual('Aug', filters.date(input, "M"), 'format: M http://www.php.net/date');
+ test.strictEqual('8', filters.date(input, "n"), 'format: n http://www.php.net/date');
+
+ test.strictEqual('2011', filters.date(input, "Y"), 'format: Y http://www.php.net/date');
+ test.strictEqual('11', filters.date(input, "y"), 'format: y http://www.php.net/date');
+ test.strictEqual('2011', filters.date(input, "Y"), 'format: Y http://www.php.net/date');
+ test.strictEqual('am', filters.date(input, "a"), 'format: a http://www.php.net/date');
+ test.strictEqual('AM', filters.date(input, "A"), 'format: A http://www.php.net/date');
+ test.strictEqual('9', filters.date(input, "g"), 'format: g http://www.php.net/date');
+ test.strictEqual('9', filters.date(input, "G"), 'format: G http://www.php.net/date');
+ test.strictEqual('09', filters.date(input, "h"), 'format: h http://www.php.net/date');
+ test.strictEqual('09', filters.date(input, "H"), 'format: H http://www.php.net/date');
+ test.strictEqual('05', filters.date(input, "i"), 'format: i http://www.php.net/date');
+ test.strictEqual('02', filters.date(input, "s"), 'format: s http://www.php.net/date');
+
+ test.strictEqual('+0700', filters.date(input, "O"), 'format: O http://www.php.net/date');
+ test.strictEqual('25200', filters.date(input, "Z"), 'format: Z http://www.php.net/date');
+ test.strictEqual('Sat Aug 06 2011 09:05:02 GMT-0700 (PDT)', filters.date(input, "r"), 'format: r http://www.php.net/date');
+ test.strictEqual('1312646702', filters.date(input, "U"), 'format: U http://www.php.net/date');
+
+ test.strictEqual('06-08-2011', filters.date(input, "d-m-Y"));
test.done();
-};
+};
Please sign in to comment.
Something went wrong with that request. Please try again.