This commit is contained in:
harttle
2017-04-24 21:31:30 +08:00
parent 82f84d96c1
commit 2632e3e97e
50 changed files with 3941 additions and 3903 deletions
+56 -58
View File
@@ -1,68 +1,66 @@
const lexical = require('./lexical.js');
const Syntax = require('./syntax.js');
const assert = require('./util/assert.js');
const _ = require('./util/underscore.js');
const lexical = require('./lexical.js')
const Syntax = require('./syntax.js')
const assert = require('./util/assert.js')
const _ = require('./util/underscore.js')
var valueRE = new RegExp(`${lexical.value.source}`, 'g');
var valueRE = new RegExp(`${lexical.value.source}`, 'g')
module.exports = function(options) {
options = _.assign({}, options);
var filters = {};
module.exports = function (options) {
options = _.assign({}, options)
var filters = {}
var _filterInstance = {
render: function(output, scope) {
var args = this.args.map(arg => Syntax.evalValue(arg, scope));
args.unshift(output);
return this.filter.apply(null, args);
},
parse: function(str) {
var match = lexical.filterLine.exec(str);
assert(match, 'illegal filter: ' + str);
var _filterInstance = {
render: function (output, scope) {
var args = this.args.map(arg => Syntax.evalValue(arg, scope))
args.unshift(output)
return this.filter.apply(null, args)
},
parse: function (str) {
var match = lexical.filterLine.exec(str)
assert(match, 'illegal filter: ' + str)
var name = match[1], argList = match[2] || '', filter = filters[name];
if (typeof filter !== 'function'){
if(options.strict_filters){
throw new TypeError(`undefined filter: ${name}`);
}
this.name= name;
this.filter= x => x;
this.args= [];
return this;
//return {
//name: name,
//error: new TypeError(`undefined filter: ${name}`)
//};
}
var args = [];
while(match = valueRE.exec(argList.trim())){
var v = match[0];
var re = new RegExp(`${v}\\s*:`, 'g');
re.test(match.input) ? args.push(`'${v}'`) : args.push(v);
}
this.name = name;
this.filter = filter;
this.args = args;
return this;
var name = match[1]
var argList = match[2] || ''
var filter = filters[name]
if (typeof filter !== 'function') {
if (options.strict_filters) {
throw new TypeError(`undefined filter: ${name}`)
}
};
this.name = name
this.filter = x => x
this.args = []
return this
}
function construct(str) {
var instance = Object.create(_filterInstance);
return instance.parse(str);
var args = []
while ((match = valueRE.exec(argList.trim()))) {
var v = match[0]
var re = new RegExp(`${v}\\s*:`, 'g')
re.test(match.input) ? args.push(`'${v}'`) : args.push(v)
}
this.name = name
this.filter = filter
this.args = args
return this
}
}
function register(name, filter) {
filters[name] = filter;
}
function construct (str) {
var instance = Object.create(_filterInstance)
return instance.parse(str)
}
function clear() {
filters = {};
}
function register (name, filter) {
filters[name] = filter
}
return {
construct, register, clear
};
};
function clear () {
filters = {}
}
return {
construct, register, clear
}
}
+84 -61
View File
@@ -1,92 +1,115 @@
// quote related
var singleQuoted = /'[^']*'/;
var doubleQuoted = /"[^"]*"/;
var quoted = new RegExp(`${singleQuoted.source}|${doubleQuoted.source}`);
var quoteBalanced = new RegExp(`(?:${quoted.source}|[^'"])*`);
var singleQuoted = /'[^']*'/
var doubleQuoted = /"[^"]*"/
var quoted = new RegExp(`${singleQuoted.source}|${doubleQuoted.source}`)
var quoteBalanced = new RegExp(`(?:${quoted.source}|[^'"])*`)
// basic types
var integer = /-?\d+/;
var number = /-?\d+\.?\d*|\.?\d+/;
var bool = /true|false/;
var integer = /-?\d+/
var number = /-?\d+\.?\d*|\.?\d+/
var bool = /true|false/
// peoperty access
var identifier = /[\w-]+/;
var subscript = new RegExp(`\\[(?:${quoted.source}|[\\w-\\.]+)\\]`);
var literal = new RegExp(`(?:${quoted.source}|${bool.source}|${number.source})`);
var variable = new RegExp(`${identifier.source}(?:\\.${identifier.source}|${subscript.source})*`);
var identifier = /[\w-]+/
var subscript = new RegExp(`\\[(?:${quoted.source}|[\\w-\\.]+)\\]`)
var literal = new RegExp(`(?:${quoted.source}|${bool.source}|${number.source})`)
var variable = new RegExp(`${identifier.source}(?:\\.${identifier.source}|${subscript.source})*`)
// range related
var rangeLimit = new RegExp(`(?:${variable.source}|${number.source})`);
var range = new RegExp(`\\(${rangeLimit.source}\\.\\.${rangeLimit.source}\\)`);
var rangeCapture = new RegExp(`\\((${rangeLimit.source})\\.\\.(${rangeLimit.source})\\)`);
var rangeLimit = new RegExp(`(?:${variable.source}|${number.source})`)
var range = new RegExp(`\\(${rangeLimit.source}\\.\\.${rangeLimit.source}\\)`)
var rangeCapture = new RegExp(`\\((${rangeLimit.source})\\.\\.(${rangeLimit.source})\\)`)
var value = new RegExp(`(?:${variable.source}|${literal.source}|${range.source})`);
var value = new RegExp(`(?:${variable.source}|${literal.source}|${range.source})`)
// hash related
var hash = new RegExp(`(?:${identifier.source})\\s*:\\s*(?:${value.source})`);
var hashCapture = new RegExp(`(${identifier.source})\\s*:\\s*(${value.source})`, 'g');
var hash = new RegExp(`(?:${identifier.source})\\s*:\\s*(?:${value.source})`)
var hashCapture = new RegExp(`(${identifier.source})\\s*:\\s*(${value.source})`, 'g')
// full match
var tagLine = new RegExp(`^\\s*(${identifier.source})\\s*([\\s\\S]*)\\s*$`);
var literalLine = new RegExp(`^${literal.source}$`, 'i');
var variableLine = new RegExp(`^${variable.source}$`);
var numberLine = new RegExp(`^${number.source}$`);
var boolLine = new RegExp(`^${bool.source}$`, 'i');
var quotedLine = new RegExp(`^${quoted.source}$`);
var rangeLine = new RegExp(`^${rangeCapture.source}$`);
var integerLine = new RegExp(`^${integer.source}$`);
var tagLine = new RegExp(`^\\s*(${identifier.source})\\s*([\\s\\S]*)\\s*$`)
var literalLine = new RegExp(`^${literal.source}$`, 'i')
var variableLine = new RegExp(`^${variable.source}$`)
var numberLine = new RegExp(`^${number.source}$`)
var boolLine = new RegExp(`^${bool.source}$`, 'i')
var quotedLine = new RegExp(`^${quoted.source}$`)
var rangeLine = new RegExp(`^${rangeCapture.source}$`)
var integerLine = new RegExp(`^${integer.source}$`)
// filter related
var valueDeclaration = new RegExp(`(?:${identifier.source}\\s*:\\s*)?${value.source}`)
var valueList = new RegExp(`${valueDeclaration.source}(\\s*,\\s*${valueDeclaration.source})*`);
var filter = new RegExp(`${identifier.source}(?:\\s*:\\s*${valueList.source})?`, 'g');
var filterCapture = new RegExp(`(${identifier.source})(?:\\s*:\\s*(${valueList.source}))?`);
var filterLine = new RegExp(`^${filterCapture.source}$`);
var valueList = new RegExp(`${valueDeclaration.source}(\\s*,\\s*${valueDeclaration.source})*`)
var filter = new RegExp(`${identifier.source}(?:\\s*:\\s*${valueList.source})?`, 'g')
var filterCapture = new RegExp(`(${identifier.source})(?:\\s*:\\s*(${valueList.source}))?`)
var filterLine = new RegExp(`^${filterCapture.source}$`)
var operators = [
/\s+or\s+/,
/\s+and\s+/,
/==|!=|<=|>=|<|>|\s+contains\s+/
];
/\s+or\s+/,
/\s+and\s+/,
/==|!=|<=|>=|<|>|\s+contains\s+/
]
function isInteger(str){
return integerLine.test(str);
function isInteger (str) {
return integerLine.test(str)
}
function isLiteral(str) {
return literalLine.test(str);
function isLiteral (str) {
return literalLine.test(str)
}
function isRange(str) {
return rangeLine.test(str);
function isRange (str) {
return rangeLine.test(str)
}
function isVariable(str) {
return variableLine.test(str);
function isVariable (str) {
return variableLine.test(str)
}
function matchValue(str) {
return value.exec(str);
function matchValue (str) {
return value.exec(str)
}
function parseLiteral(str) {
var res;
if (res = str.match(numberLine)) {
return Number(str);
}
if (res = str.match(boolLine)) {
return str.toLowerCase() === 'true';
}
if (res = str.match(quotedLine)) {
return str.slice(1, -1);
}
function parseLiteral (str) {
var res = str.match(numberLine)
if (res) {
return Number(str)
}
res = str.match(boolLine)
if (res) {
return str.toLowerCase() === 'true'
}
res = str.match(quotedLine)
if (res) {
return str.slice(1, -1)
}
}
module.exports = {
quoted, number, bool, literal, filter, integer,
hash, hashCapture,
range, rangeCapture,
identifier, value, quoteBalanced, operators,
quotedLine, numberLine, boolLine, rangeLine, literalLine, filterLine, tagLine,
isLiteral, isVariable, parseLiteral, isRange, matchValue, isInteger
};
quoted,
number,
bool,
literal,
filter,
integer,
hash,
hashCapture,
range,
rangeCapture,
identifier,
value,
quoteBalanced,
operators,
quotedLine,
numberLine,
boolLine,
rangeLine,
literalLine,
filterLine,
tagLine,
isLiteral,
isVariable,
parseLiteral,
isRange,
matchValue,
isInteger
}
+15 -15
View File
@@ -1,17 +1,17 @@
var operators = {
'==': (l, r) => l == r,
'!=': (l, r) => l != r,
'>': (l, r) => l !== null && r !== null && l > r,
'<': (l, r) => l !== null && r !== null && l < r,
'>=': (l, r) => l !== null && r !== null && l >= r,
'<=': (l, r) => l !== null && r !== null && l <= r,
'contains': (l, r) => {
if (!l) return false;
if (typeof l.indexOf !== 'function') return false;
return l.indexOf(r) > -1;
},
'and': (l, r) => l && r,
'or': (l, r) => l || r
};
'==': (l, r) => l === r,
'!=': (l, r) => l !== r,
'>': (l, r) => l !== null && r !== null && l > r,
'<': (l, r) => l !== null && r !== null && l < r,
'>=': (l, r) => l !== null && r !== null && l >= r,
'<=': (l, r) => l !== null && r !== null && l <= r,
'contains': (l, r) => {
if (!l) return false
if (typeof l.indexOf !== 'function') return false
return l.indexOf(r) > -1
},
'and': (l, r) => l && r,
'or': (l, r) => l || r
}
module.exports = operators;
module.exports = operators
+91 -91
View File
@@ -1,105 +1,105 @@
const lexical = require('./lexical.js');
const ParseError = require('./util/error.js').ParseError;
const assert = require('./util/assert.js');
const lexical = require('./lexical.js')
const ParseError = require('./util/error.js').ParseError
const assert = require('./util/assert.js')
module.exports = function(Tag, Filter) {
var stream = {
init: function(tokens) {
this.tokens = tokens;
this.handlers = {};
return this;
},
on: function(name, cb) {
this.handlers[name] = cb;
return this;
},
trigger: function(event, arg) {
var h = this.handlers[event];
if (typeof h === 'function') {
h(arg);
return true;
}
},
start: function() {
this.trigger('start');
var token;
while (!this.stopRequested && (token = this.tokens.shift())) {
if (this.trigger('token', token)) continue;
if (token.type == 'tag' &&
this.trigger(`tag:${token.name}`, token)) {
continue;
}
var template = parseToken(token, this.tokens);
this.trigger('template', template);
}
if (!this.stopRequested) this.trigger('end');
return this;
},
stop: function() {
this.stopRequested = true;
return this;
module.exports = function (Tag, Filter) {
var stream = {
init: function (tokens) {
this.tokens = tokens
this.handlers = {}
return this
},
on: function (name, cb) {
this.handlers[name] = cb
return this
},
trigger: function (event, arg) {
var h = this.handlers[event]
if (typeof h === 'function') {
h(arg)
return true
}
},
start: function () {
this.trigger('start')
var token
while (!this.stopRequested && (token = this.tokens.shift())) {
if (this.trigger('token', token)) continue
if (token.type === 'tag' &&
this.trigger(`tag:${token.name}`, token)) {
continue
}
};
function parse(tokens) {
var token, templates = [];
while (token = tokens.shift()) {
templates.push(parseToken(token, tokens));
}
return templates;
var template = parseToken(token, this.tokens)
this.trigger('template', template)
}
if (!this.stopRequested) this.trigger('end')
return this
},
stop: function () {
this.stopRequested = true
return this
}
}
function parseToken(token, tokens) {
try {
var tpl = null;
if (token.type === 'tag') {
tpl = parseTag(token, tokens);
} else if (token.type === 'output') {
tpl = parseOutput(token.value);
} else { // token.type === 'html'
tpl = token;
}
tpl.token = token;
return tpl;
} catch (e) {
throw new ParseError(e, token);
}
function parse (tokens) {
var token
var templates = []
while ((token = tokens.shift())) {
templates.push(parseToken(token, tokens))
}
return templates
}
function parseTag(token, tokens) {
if (token.name === 'continue' || token.name === 'break') return token;
return Tag.construct(token, tokens);
function parseToken (token, tokens) {
try {
var tpl = null
if (token.type === 'tag') {
tpl = parseTag(token, tokens)
} else if (token.type === 'output') {
tpl = parseOutput(token.value)
} else { // token.type === 'html'
tpl = token
}
tpl.token = token
return tpl
} catch (e) {
throw new ParseError(e, token)
}
}
function parseOutput(str) {
var match = lexical.matchValue(str);
assert(match, `illegal output string: ${str}`);
function parseTag (token, tokens) {
if (token.name === 'continue' || token.name === 'break') return token
return Tag.construct(token, tokens)
}
var initial = match[0];
str = str.substr(match.index + match[0].length);
function parseOutput (str) {
var match = lexical.matchValue(str)
assert(match, `illegal output string: ${str}`)
var filters = [];
while (match = lexical.filter.exec(str)) {
filters.push([match[0].trim()]);
}
var initial = match[0]
str = str.substr(match.index + match[0].length)
return {
type: 'output',
initial: initial,
filters: filters.map(str => Filter.construct(str))
};
}
function parseStream(tokens) {
var s = Object.create(stream);
return s.init(tokens);
var filters = []
while ((match = lexical.filter.exec(str))) {
filters.push([match[0].trim()])
}
return {
parse,
parseTag,
parseStream,
parseOutput
};
};
type: 'output',
initial: initial,
filters: filters.map(str => Filter.construct(str))
}
}
function parseStream (tokens) {
var s = Object.create(stream)
return s.init(tokens)
}
return {
parse,
parseTag,
parseStream,
parseOutput
}
}
+56 -56
View File
@@ -1,68 +1,68 @@
const Syntax = require('./syntax.js');
const Promise = require('any-promise');
const mapSeries = require('./util/promise.js').mapSeries;
const RenderBreakError = require('./util/error.js').RenderBreakError;
const RenderError = require('./util/error.js').RenderError;
const assert = require('./util/assert.js');
const Syntax = require('./syntax.js')
const Promise = require('any-promise')
const mapSeries = require('./util/promise.js').mapSeries
const RenderBreakError = require('./util/error.js').RenderBreakError
const RenderError = require('./util/error.js').RenderError
const assert = require('./util/assert.js')
var render = {
renderTemplates: function(templates, scope) {
assert(scope, 'unable to evalTemplates: scope undefined');
renderTemplates: function (templates, scope) {
assert(scope, 'unable to evalTemplates: scope undefined')
var html = '';
return mapSeries(templates, (tpl) => {
return renderTemplate.call(this, tpl)
.then(partial => html += partial)
.catch(e => {
if (e instanceof RenderBreakError) {
e.resolvedHTML = html;
throw e;
}
throw new RenderError(e, tpl);
});
}).then(() => html);
var html = ''
return mapSeries(templates, (tpl) => {
return renderTemplate.call(this, tpl)
.then(partial => (html += partial))
.catch(e => {
if (e instanceof RenderBreakError) {
e.resolvedHTML = html
throw e
}
throw new RenderError(e, tpl)
})
}).then(() => html)
function renderTemplate(template) {
if (template.type === 'tag') {
return this.renderTag(template, scope)
.then(partial => partial === undefined ? '' : partial);
} else if (template.type === 'output') {
return Promise.resolve()
function renderTemplate (template) {
if (template.type === 'tag') {
return this.renderTag(template, scope)
.then(partial => partial === undefined ? '' : partial)
} else if (template.type === 'output') {
return Promise.resolve()
.then(() => this.evalOutput(template, scope))
.then(partial => partial === undefined ? '' : stringify(partial));
} else { // template.type === 'html'
return Promise.resolve(template.value);
}
}
},
renderTag: function(template, scope) {
if (template.name === 'continue') {
return Promise.reject(new RenderBreakError('continue'));
}
if (template.name === 'break') {
return Promise.reject(new RenderBreakError('break'));
}
return template.render(scope);
},
evalOutput: function(template, scope) {
assert(scope, 'unable to evalOutput: scope undefined');
return template.filters.reduce(
(prev, filter) => filter.render(prev, scope),
Syntax.evalExp(template.initial, scope));
.then(partial => partial === undefined ? '' : stringify(partial))
} else { // template.type === 'html'
return Promise.resolve(template.value)
}
}
};
},
function factory() {
var instance = Object.create(render);
return instance;
renderTag: function (template, scope) {
if (template.name === 'continue') {
return Promise.reject(new RenderBreakError('continue'))
}
if (template.name === 'break') {
return Promise.reject(new RenderBreakError('break'))
}
return template.render(scope)
},
evalOutput: function (template, scope) {
assert(scope, 'unable to evalOutput: scope undefined')
return template.filters.reduce(
(prev, filter) => filter.render(prev, scope),
Syntax.evalExp(template.initial, scope))
}
}
function stringify(val) {
if (typeof val === 'string') return val;
return JSON.stringify(val);
function factory () {
var instance = Object.create(render)
return instance
}
module.exports = factory;
function stringify (val) {
if (typeof val === 'string') return val
return JSON.stringify(val)
}
module.exports = factory
+165 -169
View File
@@ -1,182 +1,178 @@
const _ = require('./util/underscore.js');
const lexical = require('./lexical.js');
const assert = require('./util/assert.js');
const toStr = Object.prototype.toString;
const _ = require('./util/underscore.js')
const lexical = require('./lexical.js')
const assert = require('./util/assert.js')
const toStr = Object.prototype.toString
var Scope = {
getAll: function() {
var ctx = {};
for (var i = this.scopes.length - 1; i >= 0; i--) {
_.assign(ctx, this.scopes[i]);
getAll: function () {
var ctx = {}
for (var i = this.scopes.length - 1; i >= 0; i--) {
_.assign(ctx, this.scopes[i])
}
return ctx
},
get: function (str) {
for (var i = this.scopes.length - 1; i >= 0; i--) {
try {
return this.getPropertyByPath(this.scopes[i], str)
} catch (e) {
if (/undefined variable/.test(e.message)) {
continue
}
return ctx;
},
get: function(str) {
for (var i = this.scopes.length - 1; i >= 0; i--) {
try {
return this.getPropertyByPath(this.scopes[i], str);
} catch (e) {
if (/undefined variable/.test(e.message)) {
continue;
}
if (/Cannot read property/.test(e.message)) {
if (this.opts.strict_variables) {
e.message += ': ' + str;
throw e;
} else {
continue;
}
} else {
e.message += ': ' + str;
throw e;
}
}
if (/Cannot read property/.test(e.message)) {
if (this.opts.strict_variables) {
e.message += ': ' + str
throw e
} else {
continue
}
} else {
e.message += ': ' + str
throw e
}
if (this.opts.strict_variables) {
throw new TypeError('undefined variable: ' + str);
}
}
if (this.opts.strict_variables) {
throw new TypeError('undefined variable: ' + str)
}
},
set: function (k, v) {
this.setPropertyByPath(this.scopes[this.scopes.length - 1], k, v)
return this
},
push: function (ctx) {
assert(ctx, `trying to push ${ctx} into scopes`)
return this.scopes.push(ctx)
},
pop: function () {
return this.scopes.pop()
},
unshift: function (ctx) {
assert(ctx, `trying to push ${ctx} into scopes`)
return this.scopes.unshift(ctx)
},
shift: function () {
return this.scopes.shift()
},
setPropertyByPath: function (obj, path, val) {
if (_.isString(path)) {
var paths = path.replace(/\[/g, '.').replace(/\]/g, '').split('.')
for (var i = 0; i < paths.length; i++) {
var key = paths[i]
if (i === paths.length - 1) {
return (obj[key] = val)
}
},
set: function(k, v) {
this.setPropertyByPath(this.scopes[this.scopes.length - 1], k, v);
return this;
},
push: function(ctx) {
assert(ctx, `trying to push ${ctx} into scopes`);
return this.scopes.push(ctx);
},
pop: function() {
return this.scopes.pop();
},
unshift: function(ctx) {
assert(ctx, `trying to push ${ctx} into scopes`);
return this.scopes.unshift(ctx);
},
shift: function() {
return this.scopes.shift();
},
setPropertyByPath: function(obj, path, val) {
if (_.isString(path)) {
var paths = path.replace(/\[/g, '.').replace(/\]/g, '').split('.');
for (var i = 0; i < paths.length; i++) {
var key = paths[i];
if (i === paths.length - 1) {
return obj[key] = val;
}
if (undefined === obj[key]) obj[key] = {};
if (undefined === obj[key]) obj[key] = {}
// case for readonly objects
obj = obj[key] || {};
}
}
},
getPropertyByPath: function(obj, path) {
var paths = this.propertyAccessSeq(path + '');
var varName = paths.shift();
if (!obj.hasOwnProperty(varName)) {
throw new TypeError('undefined variable');
}
var variable = obj[varName];
var lastName = paths.pop();
paths.forEach(p => variable = variable[p]);
if (undefined !== lastName) {
if (lastName === 'size' &&
(toStr.call(variable) === '[object Array]'
|| toStr.call(variable) === '[object String]')) {
return variable.length;
}
variable = variable[lastName]
}
return variable;
},
/*
* Parse property access sequence from access string
* @example
* accessSeq("foo.bar") // ['foo', 'bar']
* accessSeq("foo['bar']") // ['foo', 'bar']
* accessSeq("foo['b]r']") // ['foo', 'b]r']
* accessSeq("foo[bar.coo]") // ['foo', 'bar'], for bar.coo == 'bar'
*/
propertyAccessSeq: function(str) {
var seq = [],
name = '';
for (var i = 0; i < str.length; i++) {
if (str[i] === '[') {
seq.push(name);
name = '';
var delemiter = str[i + 1];
// foo[bar.coo]
if (delemiter !== "'" && delemiter !== '"') {
var j = matchRightBracket(str, i + 1);
assert(j !== -1, `unbalanced []: ${str}`);
name = str.slice(i + 1, j);
// foo[1]
if (lexical.isInteger(name)) {
seq.push(name);
}
// foo["bar"]
else {
seq.push(this.get(name));
}
name = '';
i = j;
}
// foo["bar"]
else {
j = str.indexOf(delemiter, i + 2);
assert(j !== -1, `unbalanced ${delemiter}: ${str}`);
name = str.slice(i + 2, j);
seq.push(name);
name = '';
i = j + 2;
}
}
// foo.bar
else if (str[i] === ".") {
seq.push(name);
name = '';
}
//foo.bar
else {
name += str[i];
}
}
if (name.length) seq.push(name);
return seq;
obj = obj[key] || {}
}
}
};
},
function matchRightBracket(str, begin) {
var stack = 1; // count of '[' - count of ']'
for (var i = begin; i < str.length; i++) {
if (str[i] === '[') {
stack++;
}
if (str[i] === ']') {
stack--;
if (stack === 0) {
return i;
}
}
getPropertyByPath: function (obj, path) {
var paths = this.propertyAccessSeq(path + '')
var varName = paths.shift()
if (!obj.hasOwnProperty(varName)) {
throw new TypeError('undefined variable')
}
return -1;
var variable = obj[varName]
var lastName = paths.pop()
paths.forEach(p => (variable = variable[p]))
if (undefined !== lastName) {
if (lastName === 'size' &&
(toStr.call(variable) === '[object Array]' ||
toStr.call(variable) === '[object String]')) {
return variable.length
}
variable = variable[lastName]
}
return variable
},
/*
* Parse property access sequence from access string
* @example
* accessSeq("foo.bar") // ['foo', 'bar']
* accessSeq("foo['bar']") // ['foo', 'bar']
* accessSeq("foo['b]r']") // ['foo', 'b]r']
* accessSeq("foo[bar.coo]") // ['foo', 'bar'], for bar.coo == 'bar'
*/
propertyAccessSeq: function (str) {
var seq = []
var name = ''
for (var i = 0; i < str.length; i++) {
if (str[i] === '[') {
seq.push(name)
name = ''
var delemiter = str[i + 1]
if (delemiter !== "'" && delemiter !== '"') {
// foo[bar.coo]
var j = matchRightBracket(str, i + 1)
assert(j !== -1, `unbalanced []: ${str}`)
name = str.slice(i + 1, j)
if (lexical.isInteger(name)) {
// foo[1]
seq.push(name)
} else {
// foo["bar"]
seq.push(this.get(name))
}
name = ''
i = j
} else {
// foo["bar"]
j = str.indexOf(delemiter, i + 2)
assert(j !== -1, `unbalanced ${delemiter}: ${str}`)
name = str.slice(i + 2, j)
seq.push(name)
name = ''
i = j + 2
}
} else if (str[i] === '.') {
// foo.bar
seq.push(name)
name = ''
} else {
// foo.bar
name += str[i]
}
}
if (name.length) seq.push(name)
return seq
}
}
exports.factory = function(ctx, opts) {
opts = _.assign({
strict_variables: false,
strict_filters: false,
blocks: {},
root: []
}, opts);
function matchRightBracket (str, begin) {
var stack = 1 // count of '[' - count of ']'
for (var i = begin; i < str.length; i++) {
if (str[i] === '[') {
stack++
}
if (str[i] === ']') {
stack--
if (stack === 0) {
return i
}
}
}
return -1
}
ctx = _.assign(ctx, {
liquid: opts
});
exports.factory = function (ctx, opts) {
opts = _.assign({
strict_variables: false,
strict_filters: false,
blocks: {},
root: []
}, opts)
var scope = Object.create(Scope);
scope.opts = opts;
scope.scopes = [ctx];
return scope;
};
ctx = _.assign(ctx, {
liquid: opts
})
var scope = Object.create(Scope)
scope.opts = opts
scope.scopes = [ctx]
return scope
}
+40 -40
View File
@@ -1,55 +1,55 @@
const operators = require('./operators.js');
const lexical = require('./lexical.js');
const assert = require('../src/util/assert.js');
const operators = require('./operators.js')
const lexical = require('./lexical.js')
const assert = require('../src/util/assert.js')
function evalExp(exp, scope) {
assert(scope, 'unable to evalExp: scope undefined');
var operatorREs = lexical.operators,
match;
for (var i = 0; i < operatorREs.length; i++) {
var operatorRE = operatorREs[i];
var expRE = new RegExp(`^(${lexical.quoteBalanced.source})(${operatorRE.source})(${lexical.quoteBalanced.source})$`);
if (match = exp.match(expRE)) {
var l = evalExp(match[1], scope);
var op = operators[match[2].trim()];
var r = evalExp(match[3], scope);
return op(l, r);
}
function evalExp (exp, scope) {
assert(scope, 'unable to evalExp: scope undefined')
var operatorREs = lexical.operators
var match
for (var i = 0; i < operatorREs.length; i++) {
var operatorRE = operatorREs[i]
var expRE = new RegExp(`^(${lexical.quoteBalanced.source})(${operatorRE.source})(${lexical.quoteBalanced.source})$`)
if ((match = exp.match(expRE))) {
var l = evalExp(match[1], scope)
var op = operators[match[2].trim()]
var r = evalExp(match[3], scope)
return op(l, r)
}
}
if (match = exp.match(lexical.rangeLine)) {
var low = evalValue(match[1], scope),
high = evalValue(match[2], scope);
var range = [];
for (var j = low; j <= high; j++) {
range.push(j);
}
return range;
if ((match = exp.match(lexical.rangeLine))) {
var low = evalValue(match[1], scope)
var high = evalValue(match[2], scope)
var range = []
for (var j = low; j <= high; j++) {
range.push(j)
}
return range
}
return evalValue(exp, scope);
return evalValue(exp, scope)
}
function evalValue(str, scope) {
str = str && str.trim();
if (!str) return undefined;
function evalValue (str, scope) {
str = str && str.trim()
if (!str) return undefined
if (lexical.isLiteral(str)) {
return lexical.parseLiteral(str);
}
if (lexical.isVariable(str)) {
return scope.get(str);
}
if (lexical.isLiteral(str)) {
return lexical.parseLiteral(str)
}
if (lexical.isVariable(str)) {
return scope.get(str)
}
}
function isTruthy(val) {
return !isFalsy(val);
function isTruthy (val) {
return !isFalsy(val)
}
function isFalsy(val) {
return false === val || undefined === val || null === val;;
function isFalsy (val) {
return val === false || undefined === val || val === null
}
module.exports = {
evalExp, evalValue, isTruthy, isFalsy
};
evalExp, evalValue, isTruthy, isFalsy
}
+64 -64
View File
@@ -1,73 +1,73 @@
const lexical = require('./lexical.js');
const _ = require('./util/underscore.js');
const Promise = require('any-promise');
const Syntax = require('./syntax.js');
const assert = require('./util/assert.js');
const lexical = require('./lexical.js')
const _ = require('./util/underscore.js')
const Promise = require('any-promise')
const Syntax = require('./syntax.js')
const assert = require('./util/assert.js')
function hash(markup, scope) {
var obj = {},
match;
lexical.hashCapture.lastIndex = 0;
while (match = lexical.hashCapture.exec(markup)) {
var k = match[1],
v = match[2];
obj[k] = Syntax.evalValue(v, scope);
}
return obj;
function hash (markup, scope) {
var obj = {}
var match
lexical.hashCapture.lastIndex = 0
while ((match = lexical.hashCapture.exec(markup))) {
var k = match[1]
var v = match[2]
obj[k] = Syntax.evalValue(v, scope)
}
return obj
}
module.exports = function() {
var tagImpls = {};
module.exports = function () {
var tagImpls = {}
var _tagInstance = {
render: function(scope) {
var obj = hash(this.token.args, scope);
var impl = this.tagImpl;
if (typeof impl.render !== 'function') {
return Promise.resolve('');
}
return Promise.resolve()
.then(() => typeof impl.render === 'function' ?
impl.render(scope, obj) : '')
.catch(function(e) {
if (_.isError(e)) {
throw e;
}
var msg = `Please reject with an Error in ${impl.render}, got ${e}`;
throw new Error(msg);
});
},
parse: function(token, tokens) {
this.type = 'tag';
this.token = token;
this.name = token.name;
var _tagInstance = {
render: function (scope) {
var obj = hash(this.token.args, scope)
var impl = this.tagImpl
if (typeof impl.render !== 'function') {
return Promise.resolve('')
}
return Promise.resolve()
.then(() => typeof impl.render === 'function'
? impl.render(scope, obj) : '')
.catch(function (e) {
if (_.isError(e)) {
throw e
}
var msg = `Please reject with an Error in ${impl.render}, got ${e}`
throw new Error(msg)
})
},
parse: function (token, tokens) {
this.type = 'tag'
this.token = token
this.name = token.name
var tagImpl = tagImpls[this.name];
assert(tagImpl, `tag ${this.name} not found`);
this.tagImpl = Object.create(tagImpl);
if (this.tagImpl.parse) {
this.tagImpl.parse(token, tokens);
}
}
};
function register(name, tag) {
tagImpls[name] = tag;
var tagImpl = tagImpls[this.name]
assert(tagImpl, `tag ${this.name} not found`)
this.tagImpl = Object.create(tagImpl)
if (this.tagImpl.parse) {
this.tagImpl.parse(token, tokens)
}
}
}
function construct(token, tokens) {
var instance = Object.create(_tagInstance);
instance.parse(token, tokens);
return instance;
}
function register (name, tag) {
tagImpls[name] = tag
}
function clear() {
tagImpls = {};
}
function construct (token, tokens) {
var instance = Object.create(_tagInstance)
instance.parse(token, tokens)
return instance
}
return {
construct,
register,
clear
};
};
function clear () {
tagImpls = {}
}
return {
construct,
register,
clear
}
}
+77 -77
View File
@@ -1,92 +1,92 @@
const lexical = require('./lexical.js');
const TokenizationError = require('./util/error.js').TokenizationError;
const _ = require('./util/underscore.js');
const assert = require('../src/util/assert.js');
const lexical = require('./lexical.js')
const TokenizationError = require('./util/error.js').TokenizationError
const _ = require('./util/underscore.js')
const assert = require('../src/util/assert.js')
function parse(html, filepath, options) {
assert(_.isString(html), 'illegal input type');
function parse (html, filepath, options) {
assert(_.isString(html), 'illegal input type')
html = whiteSpaceCtrl(html, options);
html = whiteSpaceCtrl(html, options)
var tokens = [];
var syntax = /({%-?([\s\S]*?)-?%})|({{([\s\S]*?)}})/g;
var result, htmlFragment, token;
var lastMatchEnd = 0, lastMatchBegin = -1, parsedLinesCount = 0;
var tokens = []
var syntax = /({%-?([\s\S]*?)-?%})|({{([\s\S]*?)}})/g
var result, htmlFragment, token
var lastMatchEnd = 0
var lastMatchBegin = -1
var parsedLinesCount = 0
while ((result = syntax.exec(html)) !== null) {
while ((result = syntax.exec(html)) !== null) {
// passed html fragments
if (result.index > lastMatchEnd) {
htmlFragment = html.slice(lastMatchEnd, result.index);
tokens.push({
type: 'html',
raw: htmlFragment,
value: htmlFragment
});
}
// tag appeared
if (result[1]) {
token = factory('tag', 1, result);
var match = token.value.match(lexical.tagLine);
if (!match) {
throw new TokenizationError(`illegal tag syntax`, token);
}
token.name = match[1];
token.args = match[2];
tokens.push(token);
}
// output
else { token = factory('output', 3, result);
tokens.push(token);
}
lastMatchEnd = syntax.lastIndex;
if (result.index > lastMatchEnd) {
htmlFragment = html.slice(lastMatchEnd, result.index)
tokens.push({
type: 'html',
raw: htmlFragment,
value: htmlFragment
})
}
if (result[1]) {
// tag appeared
token = factory('tag', 1, result)
var match = token.value.match(lexical.tagLine)
if (!match) {
throw new TokenizationError(`illegal tag syntax`, token)
}
token.name = match[1]
token.args = match[2]
tokens.push(token)
} else {
// output
token = factory('output', 3, result)
tokens.push(token)
}
lastMatchEnd = syntax.lastIndex
}
// remaining html
if (html.length > lastMatchEnd) {
htmlFragment = html.slice(lastMatchEnd, html.length);
tokens.push({
type: 'html',
raw: htmlFragment,
value: htmlFragment
});
}
return tokens;
if (html.length > lastMatchEnd) {
htmlFragment = html.slice(lastMatchEnd, html.length)
tokens.push({
type: 'html',
raw: htmlFragment,
value: htmlFragment
})
}
return tokens
function factory(type, offset, match) {
return {
type: type,
raw: match[offset],
value: match[offset + 1].trim(),
line: getLineNum(match),
input: html,
file: filepath
};
function factory (type, offset, match) {
return {
type: type,
raw: match[offset],
value: match[offset + 1].trim(),
line: getLineNum(match),
input: html,
file: filepath
}
}
function getLineNum(match) {
var lines = match.input.slice(lastMatchBegin + 1, match.index).split('\n');
parsedLinesCount += lines.length - 1;
lastMatchBegin = match.index;
return parsedLinesCount + 1;
}
function getLineNum (match) {
var lines = match.input.slice(lastMatchBegin + 1, match.index).split('\n')
parsedLinesCount += lines.length - 1
lastMatchBegin = match.index
return parsedLinesCount + 1
}
}
function whiteSpaceCtrl(html, options){
options = options || {};
if(options.trim_left) {
html = html.replace(/{%-?/g, '{%-');
}
if(options.trim_right) {
html = html.replace(/-?%}/g, '-%}');
}
var rLeft = options.greedy ? /\s+({%-)/g : /[\t\r ]*({%-)/g;
var rRight = options.greedy ? /(-%})\s+/g : /(-%})[\t\r ]*\n?/g;
return html.replace(rLeft, '$1').replace(rRight, '$1');
function whiteSpaceCtrl (html, options) {
options = options || {}
if (options.trim_left) {
html = html.replace(/{%-?/g, '{%-')
}
if (options.trim_right) {
html = html.replace(/-?%}/g, '-%}')
}
var rLeft = options.greedy ? /\s+({%-)/g : /[\t\r ]*({%-)/g
var rRight = options.greedy ? /(-%})\s+/g : /(-%})[\t\r ]*\n?/g
return html.replace(rLeft, '$1').replace(rRight, '$1')
}
exports.parse = parse;
exports.whiteSpaceCtrl = whiteSpaceCtrl;
exports.parse = parse
exports.whiteSpaceCtrl = whiteSpaceCtrl
+9 -9
View File
@@ -1,13 +1,13 @@
const AssertionError = require('./error.js').AssertionError;
const AssertionError = require('./error.js').AssertionError
function assert(predicate, message) {
if (!predicate) {
if (message instanceof Error) {
throw message;
}
var message = message || `expect ${predicate} to be true`;
throw new AssertionError(message);
function assert (predicate, message) {
if (!predicate) {
if (message instanceof Error) {
throw message
}
message = message || `expect ${predicate} to be true`
throw new AssertionError(message)
}
}
module.exports = assert;
module.exports = assert
+88 -88
View File
@@ -1,118 +1,118 @@
const _ = require('./underscore.js');
const _ = require('./underscore.js')
function TokenizationError(message, token) {
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
this.name = this.constructor.name;
function TokenizationError (message, token) {
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor)
}
this.name = this.constructor.name
this.input = token.input;
this.line = token.line;
this.file = token.file;
this.input = token.input
this.line = token.line
this.file = token.file
var context = mkContext(token.input, token.line);
this.message = mkMessage(message, token);
this.stack = context + '\n' + (this.stack || '');
var context = mkContext(token.input, token.line)
this.message = mkMessage(message, token)
this.stack = context + '\n' + (this.stack || '')
}
TokenizationError.prototype = Object.create(Error.prototype);
TokenizationError.prototype.constructor = TokenizationError;
TokenizationError.prototype = Object.create(Error.prototype)
TokenizationError.prototype.constructor = TokenizationError
function ParseError(e, token) {
_.assign(this, e);
this.originalError = e;
this.name = this.constructor.name;
function ParseError (e, token) {
_.assign(this, e)
this.originalError = e
this.name = this.constructor.name
this.input = token.input;
this.line = token.line;
this.file = token.file;
this.input = token.input
this.line = token.line
this.file = token.file
var context = mkContext(token.input, token.line);
this.message = mkMessage(e.message || 'Unkown Error', token);
this.stack = context + '\n' + (e.stack || '');
var context = mkContext(token.input, token.line)
this.message = mkMessage(e.message || 'Unkown Error', token)
this.stack = context + '\n' + (e.stack || '')
}
ParseError.prototype = Object.create(Error.prototype);
ParseError.prototype.constructor = ParseError;
ParseError.prototype = Object.create(Error.prototype)
ParseError.prototype.constructor = ParseError
function RenderError(e, tpl) {
function RenderError (e, tpl) {
// return the original render error
if(e instanceof RenderError){
return e;
}
_.assign(this, e);
this.originalError = e;
this.name = this.constructor.name;
if (e instanceof RenderError) {
return e
}
_.assign(this, e)
this.originalError = e
this.name = this.constructor.name
this.input = tpl.token.input;
this.line = tpl.token.line;
this.file = tpl.token.file;
this.input = tpl.token.input
this.line = tpl.token.line
this.file = tpl.token.file
var context = mkContext(tpl.token.input, tpl.token.line);
this.message = mkMessage(e.message || 'Unkown Error', tpl.token);
this.stack = context + '\n' + (e.stack || '');
var context = mkContext(tpl.token.input, tpl.token.line)
this.message = mkMessage(e.message || 'Unkown Error', tpl.token)
this.stack = context + '\n' + (e.stack || '')
}
RenderError.prototype = Object.create(Error.prototype);
RenderError.prototype.constructor = RenderError;
RenderError.prototype = Object.create(Error.prototype)
RenderError.prototype.constructor = RenderError
function RenderBreakError(message) {
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
this.name = this.constructor.name;
this.message = message || '';
function RenderBreakError (message) {
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor)
}
this.name = this.constructor.name
this.message = message || ''
}
RenderBreakError.prototype = Object.create(Error.prototype);
RenderBreakError.prototype.constructor = RenderBreakError;
RenderBreakError.prototype = Object.create(Error.prototype)
RenderBreakError.prototype.constructor = RenderBreakError
function AssertionError(message) {
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
this.name = this.constructor.name;
this.message = message;
function AssertionError (message) {
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor)
}
this.name = this.constructor.name
this.message = message
}
AssertionError.prototype = Object.create(Error.prototype);
AssertionError.prototype.constructor = AssertionError;
AssertionError.prototype = Object.create(Error.prototype)
AssertionError.prototype.constructor = AssertionError
function mkContext(input, line) {
var lines = input.split('\n');
var begin = Math.max(line - 2, 1);
var end = Math.min(line + 3, lines.length);
function mkContext (input, line) {
var lines = input.split('\n')
var begin = Math.max(line - 2, 1)
var end = Math.min(line + 3, lines.length)
var context = _
var context = _
.range(begin, end + 1)
.map(l => [
(l === line) ? '>> ' : ' ',
align(l, end),
'| ',
lines[l - 1]
(l === line) ? '>> ' : ' ',
align(l, end),
'| ',
lines[l - 1]
].join(''))
.join('\n');
.join('\n')
return context;
return context
}
function align(n, max) {
var length = (max + '').length;
var str = n + '';
var blank = Array(length - str.length).join(' ');
return blank + str;
function align (n, max) {
var length = (max + '').length
var str = n + ''
var blank = Array(length - str.length).join(' ')
return blank + str
}
function mkMessage(msg, token){
msg = msg || '';
if(token.file){
msg += ', file:' + token.file;
}
if(token.line){
msg += ', line:' + token.line;
}
return msg;
function mkMessage (msg, token) {
msg = msg || ''
if (token.file) {
msg += ', file:' + token.file
}
if (token.line) {
msg += ', line:' + token.line
}
return msg
}
module.exports = {
TokenizationError,
ParseError,
RenderBreakError,
AssertionError,
RenderError
};
TokenizationError,
ParseError,
RenderBreakError,
AssertionError,
RenderError
}
+14 -14
View File
@@ -1,20 +1,20 @@
const fs = require('fs');
const fs = require('fs')
function readFileAsync(filepath) {
return new Promise(function(resolve, reject) {
fs.readFile(filepath, 'utf8', function(err, content) {
err ? reject(err) : resolve(content);
});
});
function readFileAsync (filepath) {
return new Promise(function (resolve, reject) {
fs.readFile(filepath, 'utf8', function (err, content) {
err ? reject(err) : resolve(content)
})
})
};
function statFileAsync(path) {
return new Promise(function(resolve, reject) {
fs.stat(path, (err, stat) => err ? reject(err) : resolve(stat))
});
function statFileAsync (path) {
return new Promise(function (resolve, reject) {
fs.stat(path, (err, stat) => err ? reject(err) : resolve(stat))
})
};
module.exports = {
readFileAsync,
statFileAsync
};
readFileAsync,
statFileAsync
}
+19 -19
View File
@@ -1,35 +1,35 @@
const Promise = require('any-promise');
const Promise = require('any-promise')
/*
* Call functions in serial until someone resolved.
* @param {Array} iterable the array to iterate with.
* @param {Array} iteratee returns a new promise.
* The iteratee is invoked with three arguments: (value, index, iterable).
* The iteratee is invoked with three arguments: (value, index, iterable).
*/
function anySeries(iterable, iteratee) {
var ret = Promise.reject(new Error('init'));
iterable.forEach(function(item, idx) {
ret = ret.catch(e => iteratee(item, idx, iterable));
});
return ret;
function anySeries (iterable, iteratee) {
var ret = Promise.reject(new Error('init'))
iterable.forEach(function (item, idx) {
ret = ret.catch(e => iteratee(item, idx, iterable))
})
return ret
}
/*
* Call functions in serial until someone rejected.
* @param {Array} iterable the array to iterate with.
* @param {Array} iteratee returns a new promise.
* The iteratee is invoked with three arguments: (value, index, iterable).
* The iteratee is invoked with three arguments: (value, index, iterable).
*/
function mapSeries(iterable, iteratee) {
var ret = Promise.resolve('init');
var result = [];
iterable.forEach(function(item, idx) {
ret = ret
function mapSeries (iterable, iteratee) {
var ret = Promise.resolve('init')
var result = []
iterable.forEach(function (item, idx) {
ret = ret
.then(() => iteratee(item, idx, iterable))
.then(x => result.push(x));
});
return ret.then(() => result);
.then(x => result.push(x))
})
return ret.then(() => result)
}
exports.anySeries = anySeries;
exports.mapSeries = mapSeries;
exports.anySeries = anySeries
exports.mapSeries = mapSeries
+180 -181
View File
@@ -1,200 +1,199 @@
var monthNames = [
"January", "February", "March", "April", "May", "June", "July", "August",
"September", "October", "November", "December"
];
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
'September', 'October', 'November', 'December'
]
var monthNamesShort = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
"Nov", "Dec"
];
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct',
'Nov', 'Dec'
]
var dayNames = [
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
];
var dayNamesShort = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
]
var dayNamesShort = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
var suffixes = {
1: 'st',
2: 'nd',
3: 'rd',
'default': 'th'
};
1: 'st',
2: 'nd',
3: 'rd',
'default': 'th'
}
// prototype extensions
var _date = {
daysInMonth: function(d) {
var feb = _date.isLeapYear(d) ? 29 : 28;
return [31, feb, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
},
daysInMonth: function (d) {
var feb = _date.isLeapYear(d) ? 29 : 28
return [31, feb, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
},
getDayOfYear: function(d) {
var num = 0;
for (var i = 0; i < d.getMonth(); ++i) {
num += _date.daysInMonth(d)[i];
}
return num + d.getDate();
},
// Startday is an integer of which day to start the week measuring from
// TODO: that comment was retarted. fix it.
getWeekOfYear: function(d, startDay) {
// Skip to startDay of this week
var now = this.getDayOfYear(d) + (startDay - d.getDay());
// Find the first startDay of the year
var jan1 = new Date(d.getFullYear(), 0, 1);
var then = (7 - jan1.getDay() + startDay);
return _number.pad(Math.floor((now - then) / 7) + 1, 2);
},
isLeapYear: function(d) {
var year = d.getFullYear();
return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
},
getSuffix: function(d) {
var str = d.getDate().toString();
var index = parseInt(str.slice(-1));
return suffixes[index] || suffixes['default'];
},
century: function(d) {
return parseInt(d.getFullYear().toString().substring(0, 2), 10);
getDayOfYear: function (d) {
var num = 0
for (var i = 0; i < d.getMonth(); ++i) {
num += _date.daysInMonth(d)[i]
}
};
return num + d.getDate()
},
// Startday is an integer of which day to start the week measuring from
// TODO: that comment was retarted. fix it.
getWeekOfYear: function (d, startDay) {
// Skip to startDay of this week
var now = this.getDayOfYear(d) + (startDay - d.getDay())
// Find the first startDay of the year
var jan1 = new Date(d.getFullYear(), 0, 1)
var then = (7 - jan1.getDay() + startDay)
return _number.pad(Math.floor((now - then) / 7) + 1, 2)
},
isLeapYear: function (d) {
var year = d.getFullYear()
return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)))
},
getSuffix: function (d) {
var str = d.getDate().toString()
var index = parseInt(str.slice(-1))
return suffixes[index] || suffixes['default']
},
century: function (d) {
return parseInt(d.getFullYear().toString().substring(0, 2), 10)
}
}
var _number = {
pad: function(value, size, ch) {
if (!ch) ch = '0';
var result = value.toString();
var pad = size - result.length;
pad: function (value, size, ch) {
if (!ch) ch = '0'
var result = value.toString()
var pad = size - result.length
while (pad-- > 0) {
result = ch + result;
}
return result;
while (pad-- > 0) {
result = ch + result
}
};
return result
}
}
var format_codes = {
a: function(d) {
return dayNamesShort[d.getDay()];
},
A: function(d) {
return dayNames[d.getDay()];
},
b: function(d) {
return monthNamesShort[d.getMonth()];
},
B: function(d) {
return monthNames[d.getMonth()];
},
c: function(d) {
return d.toLocaleString();
},
C: function(d) {
return _date.century(d);
},
d: function(d) {
return _number.pad(d.getDate(), 2);
},
e: function(d) {
return _number.pad(d.getDate(), 2, ' ');
},
H: function(d) {
return _number.pad(d.getHours(), 2);
},
I: function(d) {
return _number.pad(d.getHours() % 12 || 12, 2);
},
j: function(d) {
return _number.pad(_date.getDayOfYear(d), 3);
},
k: function(d) {
return _number.pad(d.getHours(), 2, ' ');
},
l: function(d) {
return _number.pad(d.getHours() % 12 || 12, 2, ' ');
},
L: function(d) {
return _number.pad(d.getMilliseconds(), 3);
},
m: function(d) {
return _number.pad(d.getMonth() + 1, 2);
},
M: function(d) {
return _number.pad(d.getMinutes(), 2);
},
p: function(d) {
return (d.getHours() < 12 ? 'AM' : 'PM');
},
P: function(d) {
return (d.getHours() < 12 ? 'am' : 'pm');
},
q: function(d) {
return _date.getSuffix(d);
},
s: function(d) {
return Math.round(d.valueOf() / 1000);
},
S: function(d) {
return _number.pad(d.getSeconds(), 2);
},
u: function(d) {
return d.getDay() || 7;
},
U: function(d) {
return _date.getWeekOfYear(d, 0);
},
w: function(d) {
return d.getDay();
},
W: function(d) {
return _date.getWeekOfYear(d, 1);
},
x: function(d) {
return d.toLocaleDateString();
},
X: function(d) {
return d.toLocaleTimeString();
},
y: function(d) {
return d.getFullYear().toString().substring(2, 4);
},
Y: function(d) {
return d.getFullYear();
},
z: function(d) {
var tz = d.getTimezoneOffset() / 60 * 100;
return (tz > 0 ? '-' : '+') + _number.pad(Math.abs(tz), 4);
},
"%": function() {
return '%';
var formatCodes = {
a: function (d) {
return dayNamesShort[d.getDay()]
},
A: function (d) {
return dayNames[d.getDay()]
},
b: function (d) {
return monthNamesShort[d.getMonth()]
},
B: function (d) {
return monthNames[d.getMonth()]
},
c: function (d) {
return d.toLocaleString()
},
C: function (d) {
return _date.century(d)
},
d: function (d) {
return _number.pad(d.getDate(), 2)
},
e: function (d) {
return _number.pad(d.getDate(), 2, ' ')
},
H: function (d) {
return _number.pad(d.getHours(), 2)
},
I: function (d) {
return _number.pad(d.getHours() % 12 || 12, 2)
},
j: function (d) {
return _number.pad(_date.getDayOfYear(d), 3)
},
k: function (d) {
return _number.pad(d.getHours(), 2, ' ')
},
l: function (d) {
return _number.pad(d.getHours() % 12 || 12, 2, ' ')
},
L: function (d) {
return _number.pad(d.getMilliseconds(), 3)
},
m: function (d) {
return _number.pad(d.getMonth() + 1, 2)
},
M: function (d) {
return _number.pad(d.getMinutes(), 2)
},
p: function (d) {
return (d.getHours() < 12 ? 'AM' : 'PM')
},
P: function (d) {
return (d.getHours() < 12 ? 'am' : 'pm')
},
q: function (d) {
return _date.getSuffix(d)
},
s: function (d) {
return Math.round(d.valueOf() / 1000)
},
S: function (d) {
return _number.pad(d.getSeconds(), 2)
},
u: function (d) {
return d.getDay() || 7
},
U: function (d) {
return _date.getWeekOfYear(d, 0)
},
w: function (d) {
return d.getDay()
},
W: function (d) {
return _date.getWeekOfYear(d, 1)
},
x: function (d) {
return d.toLocaleDateString()
},
X: function (d) {
return d.toLocaleTimeString()
},
y: function (d) {
return d.getFullYear().toString().substring(2, 4)
},
Y: function (d) {
return d.getFullYear()
},
z: function (d) {
var tz = d.getTimezoneOffset() / 60 * 100
return (tz > 0 ? '-' : '+') + _number.pad(Math.abs(tz), 4)
},
'%': function () {
return '%'
}
}
formatCodes.h = formatCodes.b
formatCodes.N = formatCodes.L
var strftime = function (d, format) {
var output = ''
var remaining = format
while (true) {
var r = /%./g
var results = r.exec(remaining)
// No more format codes. Add the remaining text and return
if (!results) {
return output + remaining
}
};
format_codes.h = format_codes.b;
format_codes.N = format_codes.L;
var strftime = function(d, format) {
var output = '';
var remaining = format;
// Add the preceding text
output += remaining.slice(0, r.lastIndex - 2)
remaining = remaining.slice(r.lastIndex)
while (true) {
var r = /%./g;
var results = r.exec(remaining);
// Add the format code
var ch = results[0].charAt(1)
var func = formatCodes[ch]
output += func ? func.call(this, d) : '%' + ch
}
}
// No more format codes. Add the remaining text and return
if (!results) {
return output + remaining;
}
// Add the preceding text
output += remaining.slice(0, r.lastIndex - 2);
remaining = remaining.slice(r.lastIndex);
// Add the format code
var ch = results[0].charAt(1);
var func = format_codes[ch];
output += func ? func.call(this, d) : '%' + ch;
}
};
module.exports = strftime;
module.exports = strftime
+76 -77
View File
@@ -3,39 +3,39 @@
* @param {any} value The value to check.
* @return {Boolean} Returns true if value is a string, else false.
*/
function isString(value) {
return value instanceof String || typeof value === 'string';
function isString (value) {
return value instanceof String || typeof value === 'string'
}
function isError(value) {
var signature = Object.prototype.toString.call(value);
function isError (value) {
var signature = Object.prototype.toString.call(value)
// [object XXXError]
return signature.substr(-6, 5) === 'Error' ||
(typeof value.message == 'string' && typeof value.name == 'string');
return signature.substr(-6, 5) === 'Error' ||
(typeof value.message === 'string' && typeof value.name === 'string')
}
/*
* Iterates over own enumerable string keyed properties of an object and invokes iteratee for each property.
* The iteratee is invoked with three arguments: (value, key, object).
* Iterates over own enumerable string keyed properties of an object and invokes iteratee for each property.
* The iteratee is invoked with three arguments: (value, key, object).
* Iteratee functions may exit iteration early by explicitly returning false.
* @param {Object} object The object to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @return {Object} Returns object.
*/
function forOwn(object, iteratee) {
object = object || {};
for (var k in object) {
if (object.hasOwnProperty(k)) {
if (iteratee(object[k], k, object) === false) break;
}
function forOwn (object, iteratee) {
object = object || {}
for (var k in object) {
if (object.hasOwnProperty(k)) {
if (iteratee(object[k], k, object) === false) break
}
return object;
}
return object
}
/*
* Assigns own enumerable string keyed properties of source objects to the destination object.
* Source objects are applied from left to right.
* Subsequent sources overwrite property assignments of previous sources.
* Assigns own enumerable string keyed properties of source objects to the destination object.
* Source objects are applied from left to right.
* Subsequent sources overwrite property assignments of previous sources.
*
* Note: This method mutates object and is loosely based on Object.assign.
*
@@ -43,89 +43,88 @@ function forOwn(object, iteratee) {
* @param {...Object} sources The source objects.
* @return {Object} Returns object.
*/
function assign(object) {
object = isObject(object) ? object : {};
var srcs = Array.prototype.slice.call(arguments, 1);
srcs.forEach(function(src) {
_assignBinary(object, src);
});
return object;
function assign (object) {
object = isObject(object) ? object : {}
var srcs = Array.prototype.slice.call(arguments, 1)
srcs.forEach(function (src) {
_assignBinary(object, src)
})
return object
}
function _assignBinary(dst, src) {
if (!dst) return dst;
forOwn(src, function(v, k) {
dst[k] = v;
});
return dst;
function _assignBinary (dst, src) {
if (!dst) return dst
forOwn(src, function (v, k) {
dst[k] = v
})
return dst
}
function isArray(value) {
return value instanceof Array;
function isArray (value) {
return value instanceof Array
}
function echo(prefix) {
return v => {
console.log('[' + prefix + ']', v);
return v;
};
function echo (prefix) {
return v => {
console.log('[' + prefix + ']', v)
return v
}
}
function uniq(arr) {
var u = {},
a = [];
for (var i = 0, l = arr.length; i < l; ++i) {
if (u.hasOwnProperty(arr[i])) {
continue;
}
a.push(arr[i]);
u[arr[i]] = 1;
function uniq (arr) {
var u = {}
var a = []
for (var i = 0, l = arr.length; i < l; ++i) {
if (u.hasOwnProperty(arr[i])) {
continue
}
return a;
a.push(arr[i])
u[arr[i]] = 1
}
return a
}
/*
* Checks if value is the language type of Object.
* Checks if value is the language type of Object.
* (e.g. arrays, functions, objects, regexes, new Number(0), and new String(''))
* @param {any} value The value to check.
* @return {Boolean} Returns true if value is an object, else false.
*/
function isObject(value) {
return value !== null && typeof value === 'object';
function isObject (value) {
return value !== null && typeof value === 'object'
}
/*
* A function to create flexibly-numbered lists of integers,
* handy for each and map loops. start, if omitted, defaults to 0; step defaults to 1.
* Returns a list of integers from start (inclusive) to stop (exclusive),
* incremented (or decremented) by step, exclusive.
* Note that ranges that stop before they start are considered to be zero-length instead of
* A function to create flexibly-numbered lists of integers,
* handy for each and map loops. start, if omitted, defaults to 0; step defaults to 1.
* Returns a list of integers from start (inclusive) to stop (exclusive),
* incremented (or decremented) by step, exclusive.
* Note that ranges that stop before they start are considered to be zero-length instead of
* negative if you'd like a negative range, use a negative step.
*/
function range(start, stop, step) {
if (arguments.length === 1) {
stop = start;
start = 0;
}
step = step || 1;
function range (start, stop, step) {
if (arguments.length === 1) {
stop = start
start = 0
}
step = step || 1
var arr = [];
for (var i = start; i < stop; i += step) {
arr.push(i);
}
return arr;
var arr = []
for (var i = start; i < stop; i += step) {
arr.push(i)
}
return arr
}
exports.isString = isString;
exports.isArray = isArray;
exports.isObject = isObject;
exports.isError = isError;
exports.isString = isString
exports.isArray = isArray
exports.isObject = isObject
exports.isError = isError
exports.range = range;
exports.range = range
exports.forOwn = forOwn;
exports.assign = assign;
exports.uniq = uniq;
exports.forOwn = forOwn
exports.assign = assign
exports.uniq = uniq
exports.echo = echo;
exports.echo = echo