Skip to content

Commit

Permalink
Merge branch 'release/v1.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
joeworkman committed Jul 21, 2020
2 parents 6b78100 + 49b87a0 commit c13e215
Show file tree
Hide file tree
Showing 16 changed files with 5,696 additions and 221 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/node_modules
*.DS_Store
test/fixtures/_build
*.swp
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Inky

[![Build Status](https://travis-ci.org/zurb/inky.svg?branch=master)](https://travis-ci.org/zurb/inky) [![npm version](https://badge.fury.io/js/inky.svg)](https://badge.fury.io/js/inky)
[![Build Status](https://travis-ci.org/foundation/inky.svg?branch=master)](https://travis-ci.org/foundation/inky) [![npm version](https://badge.fury.io/js/inky.svg)](https://badge.fury.io/js/inky)

Inky is an HTML-based templating language that converts simple HTML into complex, responsive email-ready HTML. Designed for [Foundation for Emails](http://foundation.zurb.com/emails), a responsive email framework from [ZURB](http://zurb.com).
Inky is an HTML-based templating language that converts simple HTML into complex, responsive email-ready HTML. Designed for [Foundation for Emails](https://get.foundation/emails).

Give Inky simple HTML like this:

Expand Down Expand Up @@ -75,14 +75,7 @@ function parse() {

### Command Line

Install [inky-cli](https://github.com/zurb/inky-cli) to get the `inky` command. The first option is a glob of input files, and the second option is a folder to output them to. Add the `--watch` flag to re-compile when files are added or changed.

```bash
npm install inky-cli --global
inky src/pages/**/*.html dist --watch
```

Doesn't support advanced settings at the moment.
Install [foundation-cli](https://github.com/foundation/foundation-cli) to get the `foundation` command.

## Plugin Settings

Expand Down
43 changes: 43 additions & 0 deletions bin/inky-browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
var cheerio = require('cheerio');
var Inky = require('../lib/inky');

var inky;

window.setupInky = function(opts, cb) {
opts = opts || {};
opts.cheerio = Inky.mergeCheerioOpts(opts.cheerio);
if (typeof inky === 'undefined') {
inky = new Inky(opts);
}

// This transform function takes in an element and calls a callback.
function transform(html, callback) {
var convertedHtml = inky.releaseTheKraken(html, opts.cheerio);
callback(null, convertedHtml);
}

cb(transform);
}

if(typeof(window) !== 'undefined') {
window.runInky = function(opts, elem) {
if(typeof(elem) === 'undefined') {
elem = opts;
opts = {};
}
window.setupInky(opts, function(transform) {
transform(elem.outerHTML, function(err, html) {
if(err === null) {
elem.outerHTML = html;
} else {
console.log(err);
}
});
});
}
var elems = document.body.getElementsByTagName('container')
for(var i = 0; i < elems.length; i++) {
window.runInky(elems[i]);
}
}

8 changes: 8 additions & 0 deletions dist/inky-browser.js

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');

gulp.task('browser', function() {
var b = browserify({
entries: 'bin/inky-browser.js',
debug: false
});

return b.bundle()
.pipe(source('inky-browser.js'))
.pipe(buffer())
.pipe(uglify())
.pipe(gulp.dest('./dist/'));
});

4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = function(opts, cb) {
var stream;
opts = opts || {};
opts.cheerio = Inky.mergeCheerioOpts(opts.cheerio);

if (typeof inky === 'undefined') {
inky = new Inky(opts);
}
Expand All @@ -34,9 +35,8 @@ module.exports = function(opts, cb) {
// This transform function takes in a Vinyl HTML file, converts the code from Inky to HTML, and returns the modified file.
function transform() {
return through.obj(function(file, enc, callback) {
var html = cheerio.load(file.contents.toString(), opts.cheerio);

var convertedHtml = inky.releaseTheKraken(html).html();
var convertedHtml = inky.releaseTheKraken(file.contents.toString(), opts.cheerio);

file.contents = new Buffer(convertedHtml);

Expand Down
59 changes: 43 additions & 16 deletions lib/componentFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@ var getAttrs = require('./util/getAttrs');
module.exports = function(element) {
var inner = element.html();
var attrs = getAttrs(element);

switch (element[0].name) {
// <hr>
case this.components.hLine:
var classes = ['h-line'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}
return format('<table class="%s"><tr><th>&nbsp;</th></tr></table>', classes.join(' '))

// <column>
case this.components.columns:
return this.makeColumn(element, 'columns');
Expand All @@ -37,7 +45,7 @@ module.exports = function(element) {

// If we have the href attribute we can create an anchor for the inner of the button;
if (element.attr('href')) {
inner = format('<a href="%s"%s>%s</a>', element.attr('href'), target, inner);
inner = format('<a %s href="%s"%s>%s</a>', attrs, element.attr('href'), target, inner);
}

// If the button is expanded, it needs a <center> tag around the content
Expand All @@ -52,7 +60,7 @@ module.exports = function(element) {
classes = classes.concat(element.attr('class').split(' '));
}

return format('<table class="%s"><tr><td><table><tr><td>%s</td></tr></table></td>%s</tr></table>', classes.join(' '), inner, expander);
return format('<table class="%s"><tbody><tr><td><table><tbody><tr><td>%s</td></tr></tbody></table></td>%s</tr></tbody></table>', classes.join(' '), inner, expander);

// <container>
case this.components.container:
Expand All @@ -61,7 +69,7 @@ module.exports = function(element) {
classes = classes.concat(element.attr('class').split(' '));
}

return format('<table %s class="%s"><tbody><tr><td>%s</td></tr></tbody></table>', attrs, classes.join(' '), inner);
return format('<table %s align="center" class="%s"><tbody><tr><td>%s</td></tr></tbody></table>', attrs, classes.join(' '), inner);

// <inky>
case this.components.inky:
Expand All @@ -73,24 +81,28 @@ module.exports = function(element) {
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}
return format('<table class="%s"><tr>%s</tr></table>', classes.join(' '), inner);
return format('<table class="%s"><tbody><tr>%s</tr></tbody></table>', classes.join(' '), inner);

// <menu>
case this.components.menu:
var classes = ['menu'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}
var centerAttr = element.attr('align') ? 'align="center"' : '';
return format('<table %s class="%s"%s><tr><td><table><tr>%s</tr></table></td></tr></table>', attrs, classes.join(' '), centerAttr, inner);
return format('<table %s class="%s"><tbody><tr><td><table><tbody><tr>%s</tr></tbody></table></td></tr></tbody></table>', attrs, classes.join(' '), inner);

// <item>
case this.components.menuItem:
// Prepare optional target attribute for the <a> element
var target = '';
if (element.attr('target')) {
target = ' target=' + element.attr('target');
}
var classes = ['menu-item'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}
return format('<th %s class="%s"><a href="%s">%s</a></th>', attrs, classes.join(' '), element.attr('href'), inner);
return format('<th %s class="%s"><a href="%s"%s>%s</a></th>', attrs, classes.join(' '), element.attr('href'), target, inner);

// <center>
case this.components.center:
Expand All @@ -104,7 +116,7 @@ module.exports = function(element) {

element.attr('data-parsed', '');

return format('%s', $.html(element));
return format('%s', $.html(element, this.cheerioOpts));

// <callout>
case this.components.callout:
Expand All @@ -113,20 +125,35 @@ module.exports = function(element) {
classes = classes.concat(element.attr('class').split(' '));
}

return format('<table %s class="callout"><tr><th class="%s">%s</th><th class="expander"></th></tr></table>', attrs, classes.join(' '), inner);
return format('<table %s class="callout"><tbody><tr><th class="%s">%s</th><th class="expander"></th></tr></tbody></table>', attrs, classes.join(' '), inner);

// <spacer>
case this.components.spacer:
var classes = ['spacer'];
var size = 16;
var size;
var html = '';
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}
if (element.attr('size')) {
size = (element.attr('size'));
if (element.attr('size-sm') || element.attr('size-lg')) {
if (element.attr('size-sm')) {
size = (element.attr('size-sm'));
html += format('<table %s class="%s hide-for-large"><tbody><tr><td height="'+size+'" style="font-size:'+size+'px;line-height:'+size+'px;">&nbsp;</td></tr></tbody></table>', attrs);
}
if (element.attr('size-lg')) {
size = (element.attr('size-lg'));
html += format('<table %s class="%s show-for-large"><tbody><tr><td height="'+size+'" style="font-size:'+size+'px;line-height:'+size+'px;">&nbsp;</td></tr></tbody></table>', attrs);
}
} else {
size = (element.attr('size')) || 16;
html += format('<table %s class="%s"><tbody><tr><td height="'+size+'" style="font-size:'+size+'px;line-height:'+size+'px;">&nbsp;</td></tr></tbody></table>', attrs);
}

if( element.attr('size-sm') && element.attr('size-lg') ) {
return format(html, classes.join(' '), classes.join(' '), inner);
}

return format('<table %s class="%s"><tbody><tr><td height="'+size+'px" style="font-size:'+size+'px;line-height:'+size+'px;">&#xA0;</td></tr></tbody></table>', attrs, classes.join(' '), inner);
return format(html, classes.join(' '), inner);

// <wrapper>
case this.components.wrapper:
Expand All @@ -135,10 +162,10 @@ module.exports = function(element) {
classes = classes.concat(element.attr('class').split(' '));
}

return format('<table %s class="%s" align="center"><tr><td class="wrapper-inner">%s</td></tr></table>', attrs, classes.join(' '), inner);
return format('<table %s class="%s" align="center"><tbody><tr><td class="wrapper-inner">%s</td></tr></tbody></table>', attrs, classes.join(' '), inner);

default:
// If it's not a custom component, return it as-is
return format('<tr><td>%s</td></tr>', $.html(element));
return format('<tr><td>%s</td></tr>', $.html(element, this.cheerioOpts));
}
}
48 changes: 42 additions & 6 deletions lib/inky.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = Inky;
function Inky(options) {
options = options || {};

this.cheerioOpts = options.cheerio;
// HTML tags for custom components
this.components = extend({
button: 'button',
Expand All @@ -24,7 +25,8 @@ function Inky(options) {
menuItem: 'item',
center: 'center',
spacer: 'spacer',
wrapper: 'wrapper'
wrapper: 'wrapper',
hLine: 'h-line'
}, options.components || {});

// Column count for grid
Expand All @@ -34,14 +36,22 @@ function Inky(options) {
}

/**
* Awww yiss. Kickstarts the whole parser. Takes in HTML loaded via Cheerio as an argument, checks if there are any custom components. If there are, it replaces the nested components, traverses the DOM and replaces them with email markup.
* @param {object} $ - Input HTML as a Cheerio object
* @returns {object} Modified HTML as a Cheerio object
* Awww yiss. Kickstarts the whole parser. Takes in HTML as a string, checks if there are any custom components. If there are, it replaces the nested components, traverses the DOM and replaces them with email markup.
* @param {object} $ - Input HTML as a string
* @returns {object} Modified HTML as a string
*/
Inky.prototype.releaseTheKraken = function($) {
Inky.prototype.releaseTheKraken = function(xmlString, cheerioOpts) {
// This large compound selector looks for any custom tag loaded into Inky
// <center> is an exception: the selector is center:not([data-parsed])
// Otherwise the parser gets caught in an infinite loop where it continually tries to process the same <center> tags
//
// backwards compatible with old versions that pass in cheerio
if(typeof(xmlString) !== 'string') {
xmlString = xmlString.html();
}
var set = Inky.extractRaws(xmlString);
var raws = set[0], string = set[1];
var $ = cheerio.load(string, Inky.mergeCheerioOpts(cheerioOpts));
var tags = this.componentTags.map(function(tag) {
if (tag == 'center') {
return tag + ':not([data-parsed])';
Expand All @@ -56,7 +66,11 @@ Inky.prototype.releaseTheKraken = function($) {
elem.replaceWith(newHtml);
}

return $;
// Remove data-parsed attributes created for <center>
$('[data-parsed]').removeAttr('data-parsed');

string = $.html();
return Inky.reInjectRaws(string, raws);
}

Inky.mergeCheerioOpts = function(opts) {
Expand All @@ -67,6 +81,28 @@ Inky.mergeCheerioOpts = function(opts) {
return opts;
};

Inky.extractRaws = function(string) {
var raws = [];
var i = 0;
var raw;
var str = string
var regex = /\< *raw *\>(.*?)\<\/ *raw *\>/i;
while(raw = str.match(regex)) {
raws[i] = raw[1];
str = str.replace(regex, '###RAW' + i + '###');
i = i+1;
}
return [raws, str];
};

Inky.reInjectRaws = function(string, raws) {
var str = string;
for (var i in raws) {
str = str.replace('###RAW' + i + '###', raws[i])
}
return str;
};

Inky.prototype.componentFactory = require('./componentFactory');

Inky.prototype.makeColumn = require('./makeColumn');
11 changes: 1 addition & 10 deletions lib/makeColumn.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
var format = require('util').format;
var multiline = require('multiline');
var $ = require('cheerio');
var getAttrs = require('./util/getAttrs');

Expand Down Expand Up @@ -47,15 +46,7 @@ module.exports = function(col) {
}

// Final HTML output
output = multiline(function() {/*
<th class="%s" %s>
<table>
<tr>
<th>%s</th>%s
</tr>
</table>
</th>
*/});
output = '<th class="%s" %s><table><tbody><tr><th>%s</th>%s</tr></tbody></table></th>';

return format(output, classes.join(' '), attrs, inner, expander);
}
4 changes: 2 additions & 2 deletions lib/util/getAttrs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
// @returns {string} attributes pulled from inky objects
module.exports = function(el) {
var attrs = el.attr();
var ignoredAttributes = ['class', 'id', 'href', 'size', 'large', 'no-expander', 'small'];
var ignoredAttributes = ['class', 'id', 'href', 'size', 'size-sm', 'size-lg', 'large', 'no-expander', 'small', 'target'];
var result = '';

for (var key in attrs) {
if (ignoredAttributes.indexOf(key) == -1) result += (' ' + key + '=' + '"' + attrs[key] + '"');
}
return result;
}
}
Loading

0 comments on commit c13e215

Please sign in to comment.