Skip to content

Added possibility of extending and creating new elements #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,30 @@ Here are the names of the defaults:
}
```

## Extending and Creating Elements

You can extend current elements or create new ones.

```js
var Inky = require('inky').Inky;
var format = require('util').format;

Inky.prototype.componentLibrary.box = function (element) {
var classes = ['container'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}

return format('<table class="box-wrapper"><tbody><tr><td><table class="%s"><tbody><tr><td>%s</td></tr></tbody></table></td></tr></tbody></table>', classes.join(' '), element.html());
};

var inky = new Inky({
components: {
box: 'box'
}
});
```

## Programmatic Use

The Inky parser can be accessed directly for programmatic use. It takes in a [Cheerio](https://github.com/cheeriojs/cheerio) object of HTML, and gives you back a converted Cheerio object.
Expand Down
142 changes: 17 additions & 125 deletions lib/componentFactory.js
Original file line number Diff line number Diff line change
@@ -1,136 +1,28 @@
var format = require('util').format;
var $ = require('cheerio');

var getKeyByValue = function(obj, val) {
for (var key in obj) {
if (obj[key] === val) {
return key;
}
}
return false;
};

/**
* Returns output for desired custom element
* @param {object} element - Element as a Cheerio object.
* @returns {string} HTML converted from a custom element to table syntax.
*/
module.exports = function(element) {
var inner = element.html();

switch (element[0].name) {
// <column>
case this.components.columns:
return this.makeColumn(element, 'columns');

// <row>
case this.components.row:
var classes = ['row'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}

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

// <button>
case this.components.button:
var expander = '';

// 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</a>', element.attr('href'), inner);
}

// If the button is expanded, it needs a <center> tag around the content
if (element.hasClass('expand')) {
inner = format('<center>%s</center>', inner);
expander = '\n<td class="expander"></td>';
}

// The .button class is always there, along with any others on the <button> element
var classes = ['button'];
if (element.attr('class')) {
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);

// <container>
case this.components.container:
var classes = ['container'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}

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

// <inky>
case this.components.inky:
return '<tr><td><img src="https://raw.githubusercontent.com/arvida/emoji-cheat-sheet.com/master/public/graphics/emojis/octopus.png" /></tr></td>';

// <block-grid>
case this.components.blockGrid:
var classes = ['block-grid', 'up-'+element.attr('up')];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}
return format('<table class="%s"><tr>%s</tr></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 class="%s"%s><tr><td><table><tr>%s</tr></table></td></tr></table>', classes.join(' '), centerAttr, inner);

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

// <center>
case this.components.center:
if (element.children().length > 0) {
element.children().each(function() {
$(this).attr('align', 'center');
$(this).addClass('float-center');
});
element.find('item, .menu-item').addClass('float-center');
}

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

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

// <callout>
case this.components.callout:
var classes = ['callout-inner'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}

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

// <spacer>
case this.components.spacer:
var classes = ['spacer'];
var size = 16;
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}
if (element.attr('size')) {
size = (element.attr('size'));
}

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

// <wrapper>
case this.components.wrapper:
var classes = ['wrapper'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}

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

default:
// If it's not a custom component, return it as-is
return format('<tr><td>%s</td></tr>', $.html(element));
var tag = element[0].name;
var componentKey = getKeyByValue(this.components, tag);

if (componentKey && this.componentTags.indexOf(tag) > -1 && typeof this.componentLibrary[componentKey] === 'function') {
return this.componentLibrary[componentKey].call(this, element);
} else {
// If it's not a custom component, return it as-is
return format('<tr><td>%s</td></tr>', $.html(element));
}
}
141 changes: 141 additions & 0 deletions lib/componentLibrary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
var format = require('util').format;
var $ = require('cheerio');

/**
* Returns output for desired custom element
* @param {object} element - Element as a Cheerio object.
* @returns {string} HTML converted from a custom element to table syntax.
*/
module.exports = {
// <column>
columns: function (element) {
return this.makeColumn(element, 'columns');
},

// <row>
row: function (element) {
var classes = ['row'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}

return format('<table class="%s"><tbody><tr>%s</tr></tbody></table>', classes.join(' '), element.html());
},

// <button>
button: function (element) {
var expander = '';
var inner = element.html();

// 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</a>', element.attr('href'), inner);
}

// If the button is expanded, it needs a <center> tag around the content
if (element.hasClass('expand') || element.hasClass('expanded')) {
inner = format('<center>%s</center>', inner);
expander = '\n<td class="expander"></td>';
}

// The .button class is always there, along with any others on the <button> element
var classes = ['button'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}

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>
container: function (element) {
var classes = ['container'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}

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

// <inky>
inky: function (element) {
return '<tr><td><img src="https://raw.githubusercontent.com/arvida/emoji-cheat-sheet.com/master/public/graphics/emojis/octopus.png" /></tr></td>';
},

// <block-grid>
blockGrid: function (element) {
var classes = ['block-grid', 'up-'+element.attr('up')];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}
return format('<table class="%s"><tbody><tr>%s</tr></tbody></table>', classes.join(' '), element.html());
},

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

// <item>
menuItem: function (element) {
var classes = ['menu-item'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}
return format('<th class="%s"><a href="%s">%s</a></th>', classes.join(' '), element.attr('href'), element.html());
},

// <center>
center: function (element) {
if (element.children().length > 0) {
element.children().each(function() {
$(this).attr('align', 'center');
$(this).addClass('float-center');
});
element.find('item, .menu-item').addClass('float-center');
}

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

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

// <callout>
callout: function (element) {
var classes = ['callout-inner'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}

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

// <spacer>
spacer: function (element) {
var classes = ['spacer'];
var size = 16;
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}
if (element.attr('size')) {
size = (element.attr('size'));
}

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

// <wrapper>
wrapper: function (element) {
var classes = ['wrapper'];
if (element.attr('class')) {
classes = classes.concat(element.attr('class').split(' '));
}

return format('<table class="%s" align="center"><tbody><tr><td class="wrapper-inner">%s</td></tr></tbody></table>', classes.join(' '), element.html());
}
};
2 changes: 2 additions & 0 deletions lib/inky.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ Inky.prototype.releaseTheKraken = function($) {

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

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

Inky.prototype.makeColumn = require('./makeColumn');
2 changes: 1 addition & 1 deletion test/components.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var compare = require('./lib/compare');

describe('Center', () => {
it('applies a text-center class and center alignment attribute to the first child', () => {
it('applies a float-center class and center alignment attribute to the first child', () => {
var input = `
<center>
<div></div>
Expand Down