Skip to content
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

Element selector #44

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 31 additions & 42 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
'use strict';

// Dependencies
const Events = require('events');
const render = require('./lib/render');
const state = require('./lib/state');
"use strict";

/*
* Returns a flow-view instance
Expand Down Expand Up @@ -47,43 +42,37 @@ const state = require('./lib/state');
* }
* }
*/
module.exports = config => {
let self = new Events();

// create the config object
config = config || {};
self.config = {};
self.config.states = config.states || {};
self.config.templates = config.templates || {};

// caches
self.templates = {};
self.htmls = {};

// flow-view methods
self.state = state;
self.render = render;
self.addStates = states => {
Object.keys(states).forEach(stateName => {
self.config.states[stateName] = states[stateName];
});
};
self.addTemplates = templates => {
Object.keys(templates).forEach(templateName => {
self.config.templates[templateName] = templates[templateName]
});
};
self.close = () => {
window.close();
};
self.dom = key => {
let path = key.split(':');

// get find an element in the document
path[0] = document.querySelector(path[0]);
// Dependencies
const render = require("./lib/render");
const state = require("./lib/state");

return path[0][path[1]] !== undefined ? path[0][path[1]] : path[0];
// TODO use a service worker
const View = {
templates: {},
snippets: {},
listeners: [],
close: window.close,
dom: (key) => {
key = key.split(":");
key[0] = document.querySelector(key[0]);
return key[0][key[1]] !== undefined ? key[0][key[1]] : key[0];
}

return self;
};
View.render = render(View);
View.state = state(View);

module.exports = View;

/*
self.addStates = states => {
Object.keys(states).forEach(stateName => {
self.config.states[stateName] = states[stateName];
});
};
self.addTemplates = templates => {
Object.keys(templates).forEach(templateName => {
self.config.templates[templateName] = templates[templateName]
});
};
*/
169 changes: 99 additions & 70 deletions lib/render.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'use strict';
"use strict"

// Dependencies
const DOMPurify = require('dompurify');
Expand All @@ -18,88 +18,103 @@ const DEFAULT_ELEMENT_NAME = 'element';
const RENDER_ESCAPE = {'&': '&amp;', '"': '&quot;', '<': '&lt;', '>': '&gt;'};

/*
* Render templates
*
*
* Render templates
*/
module.exports = function (_options, data, callback) {
let self = this;

// define options
let options = {};
Object.keys(DEFAULT_OPTIONS).forEach(optionName => {
options[optionName] = _options[optionName] || DEFAULT_OPTIONS[optionName];
});

// check if config object for template exists
if (!self.config.templates[options.tmpl]) {
return callback(new Error('Template config "' + options.tmpl + '" not found.'))
}
module.exports = (context) => {
return (template_name, _options, data) => {

// get template
template.call(self, options.tmpl, self.config.templates[options.tmpl], (err, tmpl) => {
// define options
let options = {};
Object.keys(DEFAULT_OPTIONS).forEach(optionName => {
options[optionName] = _options[optionName] || DEFAULT_OPTIONS[optionName];
});

if (err) {
return callback(err);
// check if config object for template exists
if (!context.templates[template_name]) {
return Promise.reject(new Error('Template config "' + template_name + '" not found.'));
}

doRender.call(self, tmpl, options, data);
callback(null);
});
return template(context, template_name, context.templates[template_name])
.then(doRender(context, options, data));
};
};

function doRender (tmpl, options, data) {
let self = this;

// set document title
if (tmpl.title) {
document.title = tmpl.title;
}

let html;
function doRender (self, options, data) {
return (tmpl) => {

// create html
// TODO cache rendered html if data is the same?
if (tmpl.render) {
if (data && data instanceof Array) {
html = '';
data.forEach(function (item) {
html += DOMPurify.sanitize(tmpl.render(item, options, escFn));
});
} else {
html = DOMPurify.sanitize(tmpl.render(data, options, escFn));
// set default render options
if (tmpl.options) {
for (let prop in tmpl.options) {
options[prop] = tmpl.options[prop];
}
}
}

if (!html) {
if (tmpl.events) {
events.call(self, tmpl, options, document, data);
// set document title
if (tmpl.title) {
document.title = tmpl.title;
}
return;
}

// render html
if (typeof tmpl.to === 'object') {
if (tmpl.render) {
return mergeData(tmpl, options, data).then((html) => {

// clear html before appending
if (options.clearList) {
tmpl.to.innerHTML = '';
}
if (!html) {

// append dom events
if (!tmpl.events) {
tmpl.to.insertAdjacentHTML(options.position, html);
} else {
let tmpElm = document.createElement(tmpl.to.tagName);
tmpElm.innerHTML = html;
// attch events to document
if (tmpl.events) {
events.call(self, tmpl, options, document, data);
}

// render html
} else if (typeof tmpl.to === 'object') {

// clear html before appending
if (options.clearList) {
tmpl.to.innerHTML = '';
}

// setup flow event streams
events.call(self, tmpl, options, tmpElm, data);
html = DOMPurify.sanitize(html);

Array.from(tmpElm.children).forEach(function (elm) {
tmpl.to.appendChild(elm);
// append dom events
if (!tmpl.events) {
tmpl.to.insertAdjacentHTML(options.position, html);
} else {
let tmpElm = document.createElement(tmpl.to.tagName);
tmpElm.innerHTML = html;

// setup flow event streams
events.call(self, tmpl, options, tmpElm, data);

Array.from(tmpElm.children).forEach(function (elm) {
tmpl.to.appendChild(elm);
});
}
}

return html || "";
});
}

return Promise.resolve("");
};
}

function mergeData (tmpl, options, data) {
if (tmpl.render) {
if (data && data instanceof Array) {
let jobs = [];
data.forEach(function (item) {
jobs.push(tmpl.render(item, options, escFn));
});
return Promise.all(jobs).then((values) => {
let html = "";
values.forEach((snippet) => {
html += snippet;
});
return html;
});
} else {
return tmpl.render(data, options, escFn);
}
}
}

Expand All @@ -111,12 +126,26 @@ function doRender (tmpl, options, data) {
* @param {object} The data object.
* @param {string} The data key.
*/
function escFn (data, key, options) {
function escFn (self, data, key, options) {

let template = false;

// check if it's a template name
if (key.charAt(0) === "[") {
//console.log(options)
template = true;
key = key.slice(1, -1);
}

// get the string value
var str = key.indexOf('.') > 0 ? getPathValue(key, data) : (data[key] || null);
let str = key.indexOf('.') > 0 ? getPathValue(key, data) : (data[key] || null);

// if str is null or undefined
str = str === null ? (options.leaveKeys ? '{' + key + '}' : '') : str;
str = str === undefined ? (options.leaveKeys ? '{' + key + '}' : '') : str;

if (template) {
return render.call(self, str, options, data);
}

if (typeof str === 'object') {
str = JSON.stringify(str, null, '\t');
Expand All @@ -126,12 +155,12 @@ function escFn (data, key, options) {

// escape html chars
if (!options.dontEscape) {
return str.replace(/[&\"<>]/g, (_char) => {
str = str.replace(/[&\"<>]/g, (_char) => {
return RENDER_ESCAPE[_char];
});
}

return str;
return Promise.resolve(str);
}

/**
Expand Down
Loading