Comparing version 0.1.4 to 0.1.5
240
mail.js
@@ -21,4 +21,13 @@ /** | ||
/* | ||
* Filters are not needed afaik ... ${ height | pixels } can be done as [pixels=$height] | ||
var builtinFilters = { | ||
pixels: function( pixels ) { | ||
return parseInt( pixels, 10 ); | ||
} | ||
}; | ||
*/ | ||
var builtinBinds = { | ||
title: '', | ||
title: '', | ||
headStyle: '' | ||
@@ -28,37 +37,20 @@ }; | ||
var builtinTemplates = { | ||
doctype: { | ||
src: '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' | ||
}, | ||
wrapper: { | ||
html: | ||
'[html]' + | ||
'[head/]' + | ||
'<body>', | ||
/* | ||
* Utility Templates | ||
* ================= | ||
*/ | ||
htmlClose: | ||
'</body>' + | ||
'</html>' | ||
/* | ||
* [pixels=300px] yields "300" | ||
*/ | ||
pixels: { | ||
binds: { | ||
pixels: '' | ||
}, | ||
src: function( scope ) { | ||
return '' + scope.pixels( scope.binding( 'pixels' ) ); | ||
} | ||
}, | ||
html: { | ||
html: | ||
'<html xmlns="http://www.w3.org/1999/xhtml">', | ||
htmlClose: | ||
'</html>' | ||
}, | ||
head: { | ||
html: | ||
'<head>' + | ||
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' + | ||
'<meta name="viewport" content="width=device-width, initial-scale=1.0"/>' + | ||
'<title>$title</title>' + | ||
'<style>$headStyle</style>', | ||
htmlClose: | ||
'</head>' | ||
}, | ||
borderRadius: { | ||
@@ -68,3 +60,9 @@ html: '-moz-border-radius:$borderRadius;-webkit-border-radius:$borderRadius;border-radius:$borderRadius' | ||
/* | ||
* Component Templates | ||
* =================== | ||
*/ | ||
/* | ||
* Borders, cellspacing, and cellpadding should be cleared out since outlook doesn't handle tables properly. | ||
@@ -84,5 +82,63 @@ * http://www.emailonacid.com/blog/details/C13/removing_unwanted_spacing_or_gaps_between_tables_in_outlook_2007_2010 | ||
/* | ||
* Portable button that deals with padding correctly. Also supports optional corner radius in Outlook. | ||
* | ||
* inspired by http://www.industrydive.com/blog/how-to-make-html-email-buttons-that-rock/#outlook | ||
*/ | ||
button: { | ||
binds: { | ||
href: 'http://example.com', | ||
label: 'Example', | ||
color: '#ffffff', | ||
backgroundColor: '#d62828', | ||
fontSize: '16px', | ||
height: '40px', | ||
width: '300px', | ||
borderRadius: '', // set to '6px' for example | ||
}, | ||
html: function( scope ) { | ||
var borderRadius = scope.binding( 'borderRadius' ); | ||
if ( borderRadius ) { | ||
return ( | ||
'<div>' + | ||
'<!--\\[if mso\\]>' + | ||
'<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="$href" style="height:$height;v-text-anchor:middle;width:$width;" arcsize="10%" stroke="f" fillcolor="$backgroundColor">' + | ||
'<w:anchorlock/>' + | ||
'<center style="color:$color;font-family:sans-serif;font-size:$fontSize;font-weight:bold;">$label</center>' + | ||
'</v:roundrect>' + | ||
'<!\\[endif\\]-->' + | ||
'<!\\[if !mso\\]>' + | ||
'[table]' + | ||
'<tr>' + | ||
'<td align="center" width="[pixels=$width]" height="[pixels=$height]" bgcolor="$backgroundColor" style="[borderRadius=$borderRadius];color:$color;display:block;">' + | ||
'<a href="$href" style="color:$color;font-size:$fontSize;font-weight:bold;font-family:sans-serif;text-decoration:none;line-height:$height;width:100%;display:inline-block">$label</a>' + | ||
'</td>' + | ||
'</tr>' + | ||
'[/table]' + | ||
'<!\\[endif\\]>' + | ||
'</div>' | ||
); | ||
} else { | ||
return ( | ||
'[table]' + | ||
'<tr>' + | ||
'<td align="center" width="[pixels=$width]" height="[pixels=$height]" bgcolor="$backgroundColor" style="color:$color;display:block;">' + | ||
'<a href="$href" style="color:#ffffff;font-size:$fontSize;font-weight:bold;font-family:sans-serif;text-decoration:none;line-height:$height;width:100%;display:inline-block">$label</a>' + | ||
'</td>' + | ||
'</tr>' + | ||
'[/table]' | ||
); | ||
} | ||
}, | ||
text: | ||
'$label: $href' | ||
}, | ||
/* | ||
* Outlook doesn't support margins or padding on block-level elements, so it is often better to use a [tdiv] instead of a <div>. | ||
*/ | ||
tdiv: { | ||
binds: { | ||
style: '' | ||
}, | ||
html: function( scope ) { | ||
@@ -98,25 +154,62 @@ return ( | ||
'</tr>' + | ||
'[/table]', | ||
binds: { | ||
style: '' | ||
} | ||
'[/table]' | ||
}, | ||
p: { | ||
binds: { | ||
style: '' | ||
}, | ||
htmlOpen: '<br><br><p style="margin:0;$style">', | ||
htmlClose: '</p>', | ||
textOpen: '\n', | ||
textClose: '', | ||
binds: { | ||
style: '' | ||
} | ||
textClose: '' | ||
}, | ||
imglink: { | ||
binds: { | ||
}, | ||
html: '<a href="$href" style="border:none;"><img style="height:$height;" src="$src"></a>', | ||
text: '$href', | ||
binds: { | ||
} | ||
text: '$href' | ||
}, | ||
/* | ||
* Boilerplate Templates | ||
* ===================== | ||
*/ | ||
doctype: { | ||
src: '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' | ||
}, | ||
wrapper: { | ||
html: | ||
'[html]' + | ||
'[head/]' + | ||
'<body>', | ||
htmlClose: | ||
'</body>' + | ||
'</html>' | ||
}, | ||
html: { | ||
html: | ||
'<html xmlns="http://www.w3.org/1999/xhtml">', | ||
htmlClose: | ||
'</html>' | ||
}, | ||
head: { | ||
html: | ||
'<head>' + | ||
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' + | ||
'<meta name="viewport" content="width=device-width, initial-scale=1.0"/>' + | ||
'<title>$title</title>' + | ||
'<style>$headStyle</style>', | ||
htmlClose: | ||
'</head>' | ||
}, | ||
headStyles: { | ||
@@ -298,2 +391,6 @@ html: | ||
/* | ||
* mail.js | ||
* ======= | ||
*/ | ||
@@ -304,2 +401,7 @@ var whitespaceChar = /\s/, | ||
/* | ||
* Scope | ||
* ----- | ||
*/ | ||
function Scope( el, binds, template ) { | ||
@@ -320,2 +422,6 @@ this.el = el; | ||
// undocumented, not sure if this is a good idea | ||
Scope.prototype.pixels = function( pixels ) { | ||
return parseInt( pixels, 10 ); | ||
}; | ||
@@ -360,2 +466,14 @@ var mailjs = { | ||
/* | ||
resolveFilter: function( name, filters, template ) { | ||
var rslt = | ||
filters[ name ] || | ||
( template && template.filters && template.filters[ name ] ) || | ||
( this.opts.filters && this.opts.filters[ name ] ) || | ||
builtinFilters[ name ]; | ||
return rslt; | ||
}, | ||
*/ | ||
parseAttrs: function( s ) { | ||
@@ -365,5 +483,8 @@ var attrs = {}; | ||
s.replace( | ||
/([^\s=,]+)\s*=\s*(?:'([^']+)'|"([^"]+)"|(\S+))/g, | ||
/([^\s=,]+)\s*=\s*(?:'([^']*)'|"([^"]*)"|(\S+))/g, | ||
function( m, m1, m2, m3, m4 /*, offset, str */ ) { | ||
attrs[ m1 ] = m2 || m3 || m4; | ||
var v = m2 || m3 || m4; | ||
// three level priority ... !!v > '' > undefined | ||
attrs[ m1 ] = !v && ( m2 != null || m3 != null || m4 != null ) ? '' : v; | ||
} | ||
@@ -378,3 +499,3 @@ ); | ||
// note: this not only grabs the tag but also verifies that the entire element can be parsed, including the attributes | ||
/^\[(\/?)([^\s\]/]+)+(?:\s*(?:[^\s=,]+)\s*=\s*(?:'(?:[^']+)'|"(?:[^"]+)"|(?:[^'"\[\]\s]+)))*(\/?)\]/ | ||
/^\[(\/?)([^\s\]/]+)+(?:\s*(?:[^\s\]=,]+)\s*=\s*(?:'(?:[^']*)'|"(?:[^"]*)"|(?:[^'"\[\]\s]+)))*(\/?)\]/ | ||
); | ||
@@ -436,3 +557,3 @@ | ||
src, | ||
src = opts.src, | ||
dest = '', | ||
@@ -442,3 +563,3 @@ | ||
if ( template ) { | ||
if ( src == null && template ) { | ||
if ( html ) | ||
@@ -454,4 +575,2 @@ src = close ? template.htmlClose : template.html; | ||
throw new Error( 'Closing elements not supported for template: ' + ( el ? el.el : '' ) ); | ||
} else { | ||
src = opts.src; | ||
} | ||
@@ -486,2 +605,18 @@ | ||
// evaluate/render attributes before invoking the template | ||
var attrs = cEl.attrs; | ||
for ( var key in attrs ) { | ||
if ( attrs.hasOwnProperty( key ) ) { | ||
attrs[ key ] = mailjs.render({ | ||
el: el, | ||
template: template, | ||
src: attrs[ key ], | ||
binds: binds, | ||
html: html, | ||
templates: templates, | ||
inside: true | ||
}); | ||
} | ||
} | ||
dest += mailjs.render({ | ||
@@ -507,3 +642,4 @@ template: cTemplate, | ||
var matches = src.substring( si ).match( /^([a-zA-Z0-9_]+)|^\{([a-zA-Z0-9_]+)\}/ ); | ||
//var matches = src.substring( si ).match( /^([a-zA-Z0-9_]+)|^\{\s*([a-zA-Z0-9_]+)\s*(\|\s*[a-zA-Z0-9_|\s]+)?\}/ ); | ||
var matches = src.substring( si ).match( /^([a-zA-Z0-9_]+)|^\{\s*([a-zA-Z0-9_]+)\s*\}/ ); | ||
@@ -518,3 +654,3 @@ if ( !matches || matches.length < 3 ) | ||
if ( bind == null ) | ||
throw new Error( 'No bind definition for: ' + name ); | ||
throw new Error( 'No bind definition for $' + name + ' when processing' + ( el ? ' template ' + el.tag : '' ) + ' (mode=' + ( html ? 'HTML' : 'text' ) + '):\n\n' + src ); | ||
@@ -521,0 +657,0 @@ bind = mailjs.render({ |
{ | ||
"name": "mailjs", | ||
"version": "0.1.4", | ||
"version": "0.1.5", | ||
"description": "JavaScript HTML and text email renderer/formatter.", | ||
@@ -5,0 +5,0 @@ "main": "mail.js", |
@@ -111,5 +111,26 @@ | ||
{ el: '[email="support@apple.com"]', tag: 'email', attrs: { email: 'support@apple.com' } } | ||
); | ||
expect( | ||
mailjs.parseElement( '[pixels=300px]' ) | ||
).to.eql( | ||
{ el: '[pixels=300px]', tag: 'pixels', attrs: { pixels: '300px' } } | ||
); | ||
}); | ||
it( 'should parse empty attributes', function() { | ||
expect( | ||
mailjs.parseElement( '[button borderRadius=""]' ) | ||
).to.eql( | ||
{ el: '[button borderRadius=""]', tag: 'button', attrs: { borderRadius: '' } } | ||
) | ||
}); | ||
it( 'should parse only the first element', function() { | ||
expect( | ||
mailjs.parseElement( '[wrapper][pixels=300px]' ) | ||
).to.eql( | ||
{ el: '[wrapper]', tag: 'wrapper', attrs: {} } | ||
) | ||
}); | ||
}); | ||
@@ -180,3 +201,3 @@ | ||
mailjs.render({ | ||
src: 'Hello, ${firstName}${space}$lastName.', | ||
src: 'Hello, ${firstName}${ space }$lastName.', | ||
binds: { | ||
@@ -383,3 +404,2 @@ firstName: 'Jane', | ||
it( 'should work with Scope.if()', function() { | ||
@@ -400,2 +420,54 @@ expect( | ||
it( 'should handle redefining nested binds', function() { | ||
expect( | ||
mailjs.render({ | ||
src: function( scope ) { | ||
return '[b width="300px;"]' | ||
}, | ||
templates: { | ||
a: { | ||
src: '$width' | ||
}, | ||
b: { | ||
src: '[a width=$width]' | ||
} | ||
} | ||
}) | ||
).to.equal( | ||
'300px;' | ||
); | ||
}); | ||
it( 'should handle default attributes', function() { | ||
expect( | ||
mailjs.render({ | ||
src: function( scope ) { | ||
return '[b]' | ||
}, | ||
templates: { | ||
a: { | ||
src: '$width', | ||
}, | ||
b: { | ||
src: '[a width=$width]', | ||
binds: { | ||
width: '40px' | ||
} | ||
} | ||
} | ||
}) | ||
).to.equal( | ||
'40px' | ||
); | ||
}); | ||
it( 'should support Scope.pixels()', function() { | ||
expect( | ||
mailjs.render({ | ||
src: '[pixels=300px]' | ||
}) | ||
).to.equal( | ||
'300' | ||
); | ||
}); | ||
}); | ||
@@ -402,0 +474,0 @@ |
34749
991