diff --git a/.eslintrc.json b/.eslintrc.json
index f588728..f246ece 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -6,7 +6,8 @@
"jquery": true
},
"parserOptions": {
- "ecmaVersion": 2017
+ "ecmaVersion": 2017,
+ "sourceType": "module"
},
"extends": "eslint:recommended",
"rules": {
@@ -37,7 +38,7 @@
"callback-return": "off",
"camelcase": "off",
"class-methods-use-this": "error",
- "comma-dangle": "error",
+ "comma-dangle": "warn",
"comma-spacing": "off",
"comma-style": [
"error",
@@ -131,6 +132,7 @@
"no-extra-parens": "off",
"no-floating-decimal": "error",
"no-global-assign": "error",
+ "no-cond-assign": "warn",
"no-implicit-globals": "error",
"no-implied-eval": "error",
"no-inline-comments": "off",
@@ -151,7 +153,7 @@
"no-multi-spaces": "off",
"no-multi-str": "error",
"no-multiple-empty-lines": "error",
- "no-negated-condition": "error",
+ "no-negated-condition": "warn",
"no-nested-ternary": "off",
"no-new": "error",
"no-new-func": "error",
@@ -252,7 +254,7 @@
"spaced-comment": "off",
"strict": "error",
"symbol-description": "error",
- "template-curly-spacing": "error",
+ "template-curly-spacing": "warn",
"unicode-bom": [
"error",
"never"
diff --git a/src/_includes/card.html b/src/_includes/card.html
index 3f494a7..080f61b 100644
--- a/src/_includes/card.html
+++ b/src/_includes/card.html
@@ -1,56 +1,57 @@
-
-
-
diff --git a/src/assets/javascript/apis.js b/src/assets/javascript/apis.js
index 7b112fb..3930bee 100644
--- a/src/assets/javascript/apis.js
+++ b/src/assets/javascript/apis.js
@@ -1,4 +1,4 @@
-'use strict';
+import * as tempura from "./tempura.js";
const dummy = {
loading: {
@@ -128,16 +128,15 @@ CardModel.prototype.fromAPIs = function(apis) {
return this;
};
-if (window.$) {
- $(document).ready(function () {
- var cardTemplateSrc = document.querySelector('script[type="text/dot-template"]').innerText;
- var cardTemplate = window.doT.compile(cardTemplateSrc);
+export function loadAPIs() {
+ var cardTemplateSrc = document.querySelector('script[type="text/tempura"]').innerText;
+ var cardTemplate = tempura.compile(cardTemplateSrc);
var updateCards = function(data) {
var fragment = $(document.createDocumentFragment());
$.each(data, function (name, apis) {
var model = new CardModel().fromAPIs(apis);
- var view = cardTemplate(model);
+ var view = cardTemplate({it:model});
fragment.append($(view));
});
@@ -232,5 +231,5 @@ if (window.$) {
$('#search-input').focus();
});
- });
}
+
diff --git a/src/assets/javascript/main.js b/src/assets/javascript/main.js
index a53e87a..ab5d4d7 100644
--- a/src/assets/javascript/main.js
+++ b/src/assets/javascript/main.js
@@ -1,5 +1,3 @@
-'use strict';
-
function domReady(cb) {
document.addEventListener("DOMContentLoaded", cb, false);
}
diff --git a/src/assets/javascript/tempura.js b/src/assets/javascript/tempura.js
new file mode 100644
index 0000000..93dcd79
--- /dev/null
+++ b/src/assets/javascript/tempura.js
@@ -0,0 +1,138 @@
+const ESCAPE = /[&"<]/g, CHARS = {
+ '"': '"',
+ '&': '&',
+ '<': '<',
+};
+
+const ENDLINES = /[\r\n]+$/g;
+const CURLY = /{{{?\s*([\s\S]*?)\s*}}}?/g;
+const ARGS = /([a-zA-Z$_][^\s=]*)\s*=\s*((["`'])(?:(?=(\\?))\4.)*?\3|{[^}]*}|\[[^\]]*]|\S+)/g;
+
+// $$1 = escape()
+// $$2 = extra blocks
+// $$3 = template values
+function gen(input, options) {
+ options = options || {};
+
+ let char, num, action, tmp;
+ let last = CURLY.lastIndex = 0;
+ let wip='', txt='', match, inner;
+
+ let extra=options.blocks||{}, stack=[];
+ let initials = new Set(options.props||[]);
+
+ function close() {
+ if (wip.length > 0) {
+ txt += (txt ? 'x+=' : '=') + '`' + wip + '`;';
+ } else if (txt.length === 0) {
+ txt = '="";'
+ }
+ wip = '';
+ }
+
+ while (match = CURLY.exec(input)) {
+ wip += input.substring(last, match.index).replace(ENDLINES, '');
+ last = match.index + match[0].length;
+
+ inner = match[1].trim();
+ char = inner.charAt(0);
+
+ if (char === '!') {
+ // comment, continue
+ } else if (char === '#') {
+ close();
+ [, action, inner] = /^#\s*(\w[\w\d]+)\s*([^]*)/.exec(inner);
+
+ if (action === 'expect') {
+ inner.split(/[\n\r\s\t]*,[\n\r\s\t]*/g).forEach(key => {
+ initials.add(key);
+ });
+ } else if (action === 'var') {
+ num = inner.indexOf('=');
+ tmp = inner.substring(0, num++).trim();
+ inner = inner.substring(num).trim().replace(/[;]$/, '');
+ txt += `var ${tmp}=${inner};`;
+ } else if (action === 'each') {
+ num = inner.indexOf(' as ');
+ stack.push(action);
+ if (!~num) {
+ txt += `for(var i=0,$$a=${inner};i<$$a.length;i++){`;
+ } else {
+ tmp = inner.substring(0, num).trim();
+ inner = inner.substring(num + 4).trim();
+ let [item, idx='i'] = inner.replace(/[()\s]/g, '').split(','); // (item, idx?)
+ txt += `for(var ${idx}=0,${item},$$a=${tmp};${idx}<$$a.length;${idx}++){${item}=$$a[${idx}];`;
+ }
+ } else if (action === 'if') {
+ txt += `if(${inner}){`;
+ stack.push(action);
+ } else if (action === 'elif') {
+ txt += `}else if(${inner}){`;
+ } else if (action === 'else') {
+ txt += `}else{`;
+ } else if (action in extra) {
+ if (inner) {
+ tmp = [];
+ // parse arguments, `defer=true` -> `{ defer: true }`
+ while (match = ARGS.exec(inner)) tmp.push(match[1] + ':' + match[2]);
+ inner = tmp.length ? '{' + tmp.join() + '}' : '';
+ }
+ inner = inner || '{}';
+ tmp = options.async ? 'await ' : '';
+ wip += '${' + tmp + '$$2.' + action + '(' + inner + ',$$2)}';
+ } else {
+ throw new Error(`Unknown "${action}" block`);
+ }
+ } else if (char === '/') {
+ action = inner.substring(1);
+ inner = stack.pop();
+ close();
+ if (action === inner) txt += '}';
+ else throw new Error(`Expected to close "${inner}" block; closed "${action}" instead`);
+ } else if (match[0].charAt(2) === '{') {
+ wip += '${' + inner + '}'; // {{{ raw }}}
+ } else {
+ wip += '${$$1(' + inner + ')}';
+ }
+ }
+
+ if (stack.length > 0) {
+ throw new Error(`Unterminated "${stack.pop()}" block`);
+ }
+
+ if (last < input.length) {
+ wip += input.substring(last).replace(ENDLINES, '');
+ }
+
+ close();
+
+ tmp = initials.size ? `{${ [...initials].join() }}=$$3,x` : ' x';
+ return `var${tmp + txt}return x`;
+}
+
+export function esc(value) {
+ if (typeof value !== 'string') return value;
+ let last=ESCAPE.lastIndex=0, tmp=0, out='';
+ while (ESCAPE.test(value)) {
+ tmp = ESCAPE.lastIndex - 1;
+ out += value.substring(last, tmp) + CHARS[value[tmp]];
+ last = tmp + 1;
+ }
+ return out + value.substring(last);
+}
+
+export function compile(input, options={}) {
+ return new (options.async ? (async()=>{}).constructor : Function)(
+ '$$1', '$$2', '$$3', gen(input, options)
+ ).bind(0, options.escape || esc, options.blocks);
+}
+
+export function transform(input, options={}) {
+ return (
+ options.format === 'cjs'
+ ? 'var $$1=require("tempura").esc;module.exports='
+ : 'import{esc as $$1}from"tempura";export default '
+ ) + (
+ options.async ? 'async ' : ''
+ ) + 'function($$3,$$2){'+gen(input, options)+'}';
+}
diff --git a/src/index.md b/src/index.md
index dd0ef9f..1632a95 100644
--- a/src/index.md
+++ b/src/index.md
@@ -28,18 +28,20 @@ support: true
{% include card.html %}
-