Skip to content

Commit

Permalink
More filters
Browse files Browse the repository at this point in the history
  • Loading branch information
Anders Hellerup Madsen committed Dec 28, 2009
1 parent baf18d6 commit 61c3ed0
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 49 deletions.
85 changes: 48 additions & 37 deletions template/template_defaults.js
@@ -1,8 +1,8 @@
"use strict";
/*jslint laxbreak: true, eqeqeq: true, undef: true, regexp: false */
/*global require, process, exports */
/*jslint eqeqeq: true, undef: true, regexp: false */
/*global require, process, exports, escape */

var sys = require('sys')
var sys = require('sys');

var template = require('./template');
var utils = require('../utils/utils');
Expand All @@ -18,29 +18,27 @@ var utils = require('../utils/utils');
safeseq
Not implemented (yet):
slugify
stringformat
striptags
time
timesince
timeuntil
title
truncatewords
truncatewords_html
unordered_list
upper
urlencode
urlize
urlizetrunc
wordcount
wordwrap
yesno
NOTE:
date() filter is not lozalized and has a few gotchas...
stringformat() filter is regular sprintf compliant and doesn't have real python syntax
*/

var filters = exports.filters = {
add: function (value, arg) {
value = value - 0, arg = arg - 0;
value = value - 0;
arg = arg - 0;
return (isNaN(value) || isNaN(arg)) ? '' : (value + arg);
},
addslashes: function (value, arg) { return utils.string.add_slashes("" + value); },
Expand Down Expand Up @@ -105,30 +103,25 @@ var filters = exports.filters = {
// TODO: implement iriencode filter
throw "iri encoding is not implemented";
},
join: function (value, arg) { return (value instanceof Array) ? value.join(arg) : '' },
last: function (value, arg) { return (value instanceof Array && value.length) ? value[value.length - 1] : ''; },
join: function (value, arg) { return (value instanceof Array) ? value.join(arg) : ''; },
last: function (value, arg) { return ((value instanceof Array) && value.length) ? value[value.length - 1] : ''; },
length: function (value, arg) { return value.hasOwnProperty('length') ? value.length : 0; },
length_is: function (value, arg) { return value.hasOwnProperty('length') && value.length === arg; },
linebreaks: function (value, arg) { return utils.html.linebreaks("" + value); },
linebreaksbr: function (value, arg) { return "" + value.replace(/\n/g, '<br />'); },
linenumbers: function (value, arg) {
var lines = ("" + value).split('\n');
var zeroes = "", len = ("" + lines.length).length;
while (len--) { zeroes += "0"; }

lines = lines.map( function (s, idx) {
var num = "" + (idx + 1);
return zeroes.slice(0, zeroes.length - num.length) + num + '. ' + s;
});
return lines.join('\n');
var lines = String(value).split('\n');
var len = String(lines.length).length;
return lines
.map(function (s, idx) { return utils.string.sprintf('%0' + len + 'd. %s', idx + 1, s); })
.join('\n');
},
ljust: function (value, arg) {
if (typeof arg !== 'number') { return ''; }
if (arg <= value.length) { return value.slice(0, arg); }

var spaces = "", len = arg - value.length;
while (len--) { spaces += ' '; }
return value + spaces;
try {
return utils.string.sprintf('%-' + arg + 's', value).substr(0, arg);
} catch (e) {
return '';
}
},
lower: function (value, arg) { return typeof value === 'string' ? value.toLowerCase() : ''; },
make_list: function (value, arg) { return String(value).split(''); },
Expand All @@ -152,15 +145,16 @@ var filters = exports.filters = {
return (value instanceof Array) ? value[ Math.floor( Math.random() * 4 ) ] : '';
},
removetags: function (value, arg) {
return String(value).replace(/<(.|\n)*?>/g, '');
arg = String(arg).replace(/\s+/g, '|');
var re = new RegExp( '</?\\s*(' + arg + ')\\b[^>]*/?>', 'ig');
return String(value).replace(re, '');
},
rjust: function (value, arg) {
if (typeof arg !== 'number') { return ''; }
if (arg <= value.length) { return value.slice(0, arg); }

var spaces = "", len = arg - value.length;
while (len--) { spaces += ' '; }
return spaces + value;
try {
return utils.string.sprintf('%' + arg + 's', value).substr(0, arg);
} catch (e) {
return '';
}
},
safe: function (value, arg) {
// TODO: implement autoescaping
Expand Down Expand Up @@ -189,10 +183,27 @@ var filters = exports.filters = {
return out;

},
slugify: function (value, arg) {
return String(value).toLowerCase().replace(/[^\w\s]/g, '').replace(/\s+/g, '-');
},
stringformat: function (value, arg) {
try { return utils.string.sprintf('%' + arg, value); } catch (e) { return ''; }
},
striptags: function (value, arg) {
return String(value).replace(/<(.|\n)*?>/g, '');
},
title: function (value, arg) {
throw "Not implemented"; /* http://ejohn.org/blog/title-capitalization-in-javascript/ */
return utils.string.titleCaps( String(value) );
},
truncatewords: function (value, arg) {
return String(value).split(/\s+/g).slice(0, arg).join(' ') + ' ...';
},
upper: function (value, arg) {
return (value + '').toUpperCase();
},
urlencode: function (value, arg) {
return escape(value);
}

};


Expand Down
37 changes: 35 additions & 2 deletions template/template_defaults.test.js
Expand Up @@ -248,7 +248,8 @@ testcase('random');
});
testcase('removetags');
test('should remove tags', function () {
assertEquals('jeg har en dejlig hest.', filters.removetags('<p>jeg har en <strong\n>dejlig</strong> hest.</p>'));
assertEquals('Joel <button>is</button> a slug',
filters.removetags('<b>Joel</b> <button>is</button> a <span\n>slug</span>', 'b span'));
});
testcase('rjust')
test('should right justify value in correctly sized field', function () {
Expand All @@ -270,6 +271,38 @@ testcase('slice')
assertEquals([0,1,2,3,4,5,6,7,8,9], filters.slice(arr, 'hest'));
assertEquals([0,1,2,3,4,5,6,7,8,9], filters.slice(arr));
});

testcase('slugify');
test('should slugify correctly', function () {
assertEquals('joel-is-a-slug', filters.slugify('Joel is a slug'));
assertEquals('s-str-verden-da-ikke-lngere', filters.slugify('Så står Verden da ikke længere!'));
assertEquals('super_max', filters.slugify('Super_Max'));
});
testcase('stringformat');
test('return expected results', function () {
assertEquals('002', filters.stringformat(2, '03d'));
assertEquals('Hest', filters.stringformat('Hest', 's'));
assertEquals('', filters.stringformat('Hest', ''));
assertEquals('Hest ', filters.stringformat('Hest', '-10s'));
});
testcase('striptags');
test('should remove tags', function () {
assertEquals('jeg har en dejlig hest.', filters.striptags('<p>jeg har en <strong\n>dejlig</strong> hest.</p>'));
});
testcase('title');
test('should titlecase correctly', function () {
assertEquals('This Is Correct', filters.title('This is correct'));
});
testcase('truncatewords');
test('should truncate', function () {
assertEquals('Joel is ...', filters.truncatewords('Joel is a slug', 2));
});
testcase('upper');
test('should uppercase correctly', function () {
assertEquals('JOEL IS A SLUG', filters.upper('Joel is a slug'));
});
testcase('urlencode');
test('should encode urls', function () {
assertEquals('%22Aardvarks%20lurk%2C%20OK%3F%22', filters.urlencode('"Aardvarks lurk, OK?"'));
});
run();

126 changes: 116 additions & 10 deletions utils/string.js
Expand Up @@ -40,20 +40,126 @@ function cap_first(s) {
}
exports.cap_first = cap_first;

function center(s, width) {
if (s.length > width) { return s; }
var right = Math.round((width - s.length) / 2);
var left = width - (s.length + right);
var out = '';

while (left--) { out += ' '; }
out += s;
while (right--) { out += ' '; }
/*************************************************************************
* sprintf() and str_repeat() from http://code.google.com/p/sprintf/
*/

return out;
/**
* sprintf() for JavaScript v.0.4
*
* Copyright (c) 2007 Alexandru Marasteanu <http://alexei.417.ro/>
* Thanks to David Baird (unit test and patch).
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/

function str_repeat(i, m) { for (var o = []; m > 0; o[--m] = i); return(o.join('')); }

function sprintf () {
var i = 0, a, f = arguments[i++], o = [], m, p, c, x;
while (f) {
if (m = /^[^\x25]+/.exec(f)) o.push(m[0]);
else if (m = /^\x25{2}/.exec(f)) o.push('%');
else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) throw("Too few arguments.");
if (/[^s]/.test(m[7]) && (typeof(a) != 'number'))
throw("Expecting number but found " + typeof(a));
switch (m[7]) {
case 'b': a = a.toString(2); break;
case 'c': a = String.fromCharCode(a); break;
case 'd': a = parseInt(a); break;
case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break;
case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break;
case 'o': a = a.toString(8); break;
case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break;
case 'u': a = Math.abs(a); break;
case 'x': a = a.toString(16); break;
case 'X': a = a.toString(16).toUpperCase(); break;
}
a = (/[def]/.test(m[7]) && m[2] && a > 0 ? '+' + a : a);
c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' ';
x = m[5] - String(a).length;
p = m[5] ? str_repeat(c, x) : '';
o.push(m[4] ? a + p : p + a);
}
else throw ("Huh ?!");
f = f.substring(m[0].length);
}
return o.join('');
}
exports.center = center;
/*************************************************************************/

/*************************************************************************
* titleCaps from http://ejohn.org/files/titleCaps.js (by John Resig)
*/

var small = "(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v[.]?|via|vs[.]?)";
var punct = "([!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]*)";

var titleCaps = function(title){
var parts = [], split = /[:.;?!] |(?: |^)["Ò]/g, index = 0;

while (true) {
var m = split.exec(title);

parts.push( title.substring(index, m ? m.index : title.length)
.replace(/\b([A-Za-z][a-z.'Õ]*)\b/g, function(all){
return /[A-Za-z]\.[A-Za-z]/.test(all) ? all : upper(all);
})
.replace(RegExp("\\b" + small + "\\b", "ig"), lower)
.replace(RegExp("^" + punct + small + "\\b", "ig"), function(all, punct, word){
return punct + upper(word);
})
.replace(RegExp("\\b" + small + punct + "$", "ig"), upper));

index = split.lastIndex;

if ( m ) parts.push( m[0] );
else break;
}

return parts.join("").replace(/ V(s?)\. /ig, " v$1. ")
.replace(/(['Õ])S\b/ig, "$1s")
.replace(/\b(AT&T|Q&A)\b/ig, function(all){
return all.toUpperCase();
});
};

function lower(word){
return word.toLowerCase();
}

function upper(word){
return word.substr(0,1).toUpperCase() + word.substr(1);
}

exports.titleCaps = titleCaps;


/*************************************************************************/

exports.sprintf = sprintf;
exports.str_repeat = str_repeat;


function center(s, width) {
if (s.length > width) { return s; }
var right = Math.round((width - s.length) / 2);
var left = width - (s.length + right);
return str_repeat(' ', left) + s + str_repeat(' ', right);
}
exports.center = center;

7 changes: 7 additions & 0 deletions utils/string.test.js
Expand Up @@ -18,5 +18,12 @@ testcase('string utility functions');
assertEquals(' centered ', center('centered', 17));
assertEquals('centered', center('centered', 3));
});
testcase('titleCaps')
test('should work as expected', function () {
assertEquals("Nothing to Be Afraid Of?", titleCaps("Nothing to Be Afraid of?"));
assertEquals("Q&A With Steve Jobs: 'That's What Happens in Technology'",
titleCaps("Q&A With Steve Jobs: 'That's What Happens In Technology'")
);
})

run();

0 comments on commit 61c3ed0

Please sign in to comment.