mirror of
https://github.com/harttle/liquidjs.git
synced 2026-10-03 09:05:14 -07:00
tag: tablerow
This commit is contained in:
@@ -31,8 +31,8 @@ Documentation: <https://shopify.github.io/liquid/basics/introduction/#tags>
|
|||||||
- [x] for [Document: limit,offset,range,reversed)](https://shopify.github.io/liquid/tags/iteration/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/for.js) [Test][tt]
|
- [x] for [Document: limit,offset,range,reversed)](https://shopify.github.io/liquid/tags/iteration/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/for.js) [Test][tt]
|
||||||
- [x] cycle [Document](https://shopify.github.io/liquid/tags/iteration/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/cycle.js) [Test][tt]
|
- [x] cycle [Document](https://shopify.github.io/liquid/tags/iteration/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/cycle.js) [Test][tt]
|
||||||
- [x] cycle [Document: group)](https://shopify.github.io/liquid/tags/iteration/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/cycle.js) [Test][tt]
|
- [x] cycle [Document: group)](https://shopify.github.io/liquid/tags/iteration/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/cycle.js) [Test][tt]
|
||||||
- [ ] tablerow [Document](https://shopify.github.io/liquid/tags/iteration/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/.js) [Test][tt]
|
- [x] tablerow [Document](https://shopify.github.io/liquid/tags/iteration/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/tablerow.js) [Test][tt]
|
||||||
- [ ] tablerow [Document: cols,limit,offset,range](https://shopify.github.io/liquid/tags/iteration/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/.js) [Test][tt]
|
- [x] tablerow [Document: cols,limit,offset,range](https://shopify.github.io/liquid/tags/iteration/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/tablerow.js) [Test][tt]
|
||||||
- [x] assign [Document](https://shopify.github.io/liquid/tags/variable/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/assign.js) [Test][tt]
|
- [x] assign [Document](https://shopify.github.io/liquid/tags/variable/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/assign.js) [Test][tt]
|
||||||
- [x] capture [Document](https://shopify.github.io/liquid/tags/variable/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/capture.js) [Test][tt]
|
- [x] capture [Document](https://shopify.github.io/liquid/tags/variable/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/capture.js) [Test][tt]
|
||||||
- [x] increment [Document](https://shopify.github.io/liquid/tags/variable/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/increment.js) [Test][tt]
|
- [x] increment [Document](https://shopify.github.io/liquid/tags/variable/) [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/increment.js) [Test][tt]
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
|
function TokenizationError(message, input, line, stack) {
|
||||||
function factory(msg, token){
|
this.name = "TokenizationError";
|
||||||
var err = new Error(msg || 'unkown error');
|
this.message = (message || "");
|
||||||
if(token){
|
this.input = input;
|
||||||
err.token = token.raw;
|
this.line = line;
|
||||||
err.line = token.line;
|
if(stack) this.stack = stack;
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
|
TokenizationError.prototype = Error.prototype;
|
||||||
|
|
||||||
module.exports = factory;
|
function ParseError(message, input, line, stack) {
|
||||||
|
this.name = "ParseError";
|
||||||
|
this.message = (message || "");
|
||||||
|
this.input = input;
|
||||||
|
this.line = line;
|
||||||
|
if(stack) this.stack = stack;
|
||||||
|
}
|
||||||
|
ParseError.prototype = Error.prototype;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
TokenizationError, ParseError
|
||||||
|
};
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ function stringify(val) {
|
|||||||
|
|
||||||
function factory(Filter, Tag) {
|
function factory(Filter, Tag) {
|
||||||
function renderTemplates(templates, scope) {
|
function renderTemplates(templates, scope) {
|
||||||
|
if (!scope) throw new Error('unable to evalTemplates: scope undefined');
|
||||||
var html = '';
|
var html = '';
|
||||||
templates.some(template => {
|
templates.some(template => {
|
||||||
if (scope.get('forloop.skip')) return true;
|
if (scope.get('forloop.skip')) return true;
|
||||||
@@ -45,7 +46,7 @@ function factory(Filter, Tag) {
|
|||||||
return {
|
return {
|
||||||
renderTemplates, evalFilter
|
renderTemplates, evalFilter
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
factory.stringify = stringify;
|
factory.stringify = stringify;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
const lexical = require('./lexical.js');
|
const lexical = require('./lexical.js');
|
||||||
const Exp = require('./expression.js');
|
const Exp = require('./expression.js');
|
||||||
|
const TokenizationError = require('./error.js').TokenizationError;
|
||||||
|
|
||||||
var _tagInstance = {
|
var _tagInstance = {
|
||||||
render: function(scope, register) {
|
render: function(scope, register) {
|
||||||
var reg = register[this.name];
|
var reg = register[this.name];
|
||||||
if(!reg) reg = register[this.name] = {}
|
if(!reg) reg = register[this.name] = {};
|
||||||
var obj = hash(this.token.args, scope);
|
var obj = hash(this.token.args, scope);
|
||||||
return this.tagImpl.render(scope, obj, reg) || '';
|
return this.tagImpl.render(scope, obj, reg) || '';
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ module.exports = function(liquid) {
|
|||||||
var offset = hash.offset || 0;
|
var offset = hash.offset || 0;
|
||||||
var limit = (hash.limit === undefined) ? collection.length : hash.limit;
|
var limit = (hash.limit === undefined) ? collection.length : hash.limit;
|
||||||
|
|
||||||
var collection = collection.slice(offset, offset + limit);
|
collection = collection.slice(offset, offset + limit);
|
||||||
if(this.reversed) collection.reverse();
|
if(this.reversed) collection.reverse();
|
||||||
collection.some((item, i) => {
|
collection.some((item, i) => {
|
||||||
ctx[this.variable] = item;
|
ctx[this.variable] = item;
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
var Liquid = require('..');
|
||||||
|
var lexical = Liquid.lexical;
|
||||||
|
var re = new RegExp(`^(${lexical.identifier.source})\\s+in\\s+` +
|
||||||
|
`(${lexical.variableOrRange.source})` +
|
||||||
|
`(?:\\s+${lexical.hash.source})*$`);
|
||||||
|
|
||||||
|
module.exports = function(liquid) {
|
||||||
|
liquid.registerTag('tablerow', {
|
||||||
|
|
||||||
|
parse: function(tagToken, remainTokens) {
|
||||||
|
var match = re.exec(tagToken.args);
|
||||||
|
if (!match) throw new Error(`illegal tag: ${tagToken.raw}`);
|
||||||
|
this.variable = match[1];
|
||||||
|
this.collection = match[2];
|
||||||
|
|
||||||
|
this.templates = [];
|
||||||
|
this.elseTemplates = [];
|
||||||
|
|
||||||
|
var p, stream = liquid.parseStream(remainTokens)
|
||||||
|
.onStart(x => p = this.templates)
|
||||||
|
.onTag('else', token => p = this.elseTemplates)
|
||||||
|
.onTag('endtablerow', token => stream.stop())
|
||||||
|
.onTemplate(tpl => p.push(tpl))
|
||||||
|
.onEnd(x => {
|
||||||
|
throw new Error(`tag ${tagToken.raw} not closed`);
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.start();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function(scope, hash) {
|
||||||
|
var collection = Liquid.evalExp(this.collection, scope);
|
||||||
|
if (Liquid.isFalsy(collection)) {
|
||||||
|
return liquid.renderTemplates(this.elseTemplates, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '<table>',
|
||||||
|
ctx = {},
|
||||||
|
length = collection.length;
|
||||||
|
var offset = hash.offset || 0;
|
||||||
|
var limit = (hash.limit === undefined) ? collection.length : hash.limit;
|
||||||
|
|
||||||
|
var cols = hash.cols;
|
||||||
|
if (!cols) throw new Error(`illegal cols: ${cols}`);
|
||||||
|
|
||||||
|
collection.slice(offset, offset + limit).some((item, i) => {
|
||||||
|
var row = Math.floor(i / cols) + 1,
|
||||||
|
col = (i % cols) + 1;
|
||||||
|
if(col === 1){
|
||||||
|
if(row !== 1){
|
||||||
|
html += '</tr>';
|
||||||
|
}
|
||||||
|
html += `<tr class="row${row}">`;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx[this.variable] = item;
|
||||||
|
scope.push(ctx);
|
||||||
|
html += `<td class="col${col}">`;
|
||||||
|
html += liquid.renderTemplates(this.templates, scope);
|
||||||
|
html += '</td>';
|
||||||
|
scope.pop(ctx);
|
||||||
|
});
|
||||||
|
html += '</tr></table>';
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
+7
-3
@@ -1,5 +1,6 @@
|
|||||||
const lexical = require('./lexical.js');
|
const lexical = require('./lexical.js');
|
||||||
const error = require('./error.js');
|
const error = require('./error.js');
|
||||||
|
const ParseError = require('./error.js').ParseError;
|
||||||
|
|
||||||
module.exports = function(Tag) {
|
module.exports = function(Tag) {
|
||||||
|
|
||||||
@@ -28,8 +29,7 @@ module.exports = function(Tag) {
|
|||||||
if (this.trigger(`tag:${token.name}`, token)) continue;
|
if (this.trigger(`tag:${token.name}`, token)) continue;
|
||||||
if (token.name === 'continue' || token.name === 'break') {
|
if (token.name === 'continue' || token.name === 'break') {
|
||||||
template = token;
|
template = token;
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
template = parseTag(token, this.tokens);
|
template = parseTag(token, this.tokens);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -71,9 +71,13 @@ module.exports = function(Tag) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parseTag(token, tokens) {
|
function parseTag(token, tokens) {
|
||||||
|
try {
|
||||||
return Tag.construct(token).parse(tokens);
|
return Tag.construct(token).parse(tokens);
|
||||||
|
} catch (e) {
|
||||||
|
throw new ParseError(e.message,
|
||||||
|
token.input, token.line, e.stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function parseStream(tokens) {
|
function parseStream(tokens) {
|
||||||
var s = Object.create(stream);
|
var s = Object.create(stream);
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
var chai = require("chai");
|
||||||
|
var sinonChai = require("sinon-chai");
|
||||||
|
var sinon = require("sinon");
|
||||||
|
var expect = chai.expect;
|
||||||
|
chai.use(sinonChai);
|
||||||
|
|
||||||
|
var liquid = require('..')(), ctx;
|
||||||
|
|
||||||
|
function test(func, cb){
|
||||||
|
try{
|
||||||
|
func();
|
||||||
|
cb({});
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
cb(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('error', function() {
|
||||||
|
|
||||||
|
it('should throw TokenizationError when tag illegal', function() {
|
||||||
|
test(function(){
|
||||||
|
liquid.render('{% -a %}', {});
|
||||||
|
}, function(err){
|
||||||
|
expect(err.name).to.equal('TokenizationError');
|
||||||
|
expect(err.message).to.equal('illegal tag: {% -a %}');
|
||||||
|
expect(err.input).to.equal('{% -a %}');
|
||||||
|
expect(err.line).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw correct error info', function() {
|
||||||
|
test(function(){
|
||||||
|
liquid.render('{%if true%}\naaa{%endif%}\n{% -a %}\n3', {});
|
||||||
|
}, function(err){
|
||||||
|
expect(err.input).to.equal('{% -a %}');
|
||||||
|
expect(err.line).to.equal(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw ParseError when tag not exist', function() {
|
||||||
|
test(function(){
|
||||||
|
liquid.render('{% a %}', {});
|
||||||
|
}, function(err){
|
||||||
|
expect(err.name).to.equal('ParseError');
|
||||||
|
expect(err.message).to.equal('tag a not found');
|
||||||
|
expect(err.input).to.equal('{% a %}');
|
||||||
|
expect(err.line).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw ParseError when tag not closed', function() {
|
||||||
|
test(function(){
|
||||||
|
liquid.render('{% if %}', {});
|
||||||
|
}, function(err){
|
||||||
|
expect(err.name).to.equal('ParseError');
|
||||||
|
expect(err.message).to.equal('tag {% if %} not closed');
|
||||||
|
expect(err.input).to.equal('{% if %}');
|
||||||
|
expect(err.line).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -136,4 +136,47 @@ describe('tags', function() {
|
|||||||
test('{% decrement foo %}{%decrement foo%}{{foo}}', '-2');
|
test('{% decrement foo %}{%decrement foo%}{{foo}}', '-2');
|
||||||
test('{% decrement one %}{{one}}', '0');
|
test('{% decrement one %}{{one}}', '0');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support tablerow', function() {
|
||||||
|
src = '{% tablerow i in alpha cols:2 %}{{ i }}{% endtablerow %}';
|
||||||
|
dst = '<table>' +
|
||||||
|
'<tr class="row1"><td class="col1">a</td><td class="col2">b</td></tr>' +
|
||||||
|
'<tr class="row2"><td class="col1">c</td></tr>' +
|
||||||
|
'</table>';
|
||||||
|
test(src, dst);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support tablerow with range', function() {
|
||||||
|
src = '{% tablerow i in (1..5) cols:2 %}{{ i }}{% endtablerow %}';
|
||||||
|
dst = '<table>' +
|
||||||
|
'<tr class="row1"><td class="col1">1</td><td class="col2">2</td></tr>' +
|
||||||
|
'<tr class="row2"><td class="col1">3</td><td class="col2">4</td></tr>' +
|
||||||
|
'<tr class="row3"><td class="col1">5</td></tr>' +
|
||||||
|
'</table>';
|
||||||
|
test(src, dst);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('tablerow should throw on illegal cols', function() {
|
||||||
|
testThrow('{% tablerow i in (1..5) cols:0 %}{{ i }}{% endtablerow %}',
|
||||||
|
/illegal cols: 0/);
|
||||||
|
testThrow('{% tablerow i in (1..5) %}{{ i }}{% endtablerow %}',
|
||||||
|
/illegal cols: undefined/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support tablerow with limit', function() {
|
||||||
|
src = '{% tablerow i in (1..5) cols:2 limit:3 %}{{ i }}{% endtablerow %}';
|
||||||
|
dst = '<table>' +
|
||||||
|
'<tr class="row1"><td class="col1">1</td><td class="col2">2</td></tr>' +
|
||||||
|
'<tr class="row2"><td class="col1">3</td></tr>' +
|
||||||
|
'</table>';
|
||||||
|
test(src, dst);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support tablerow with offset', function() {
|
||||||
|
src = '{% tablerow i in (1..5) cols:2 offset:3 %}{{ i }}{% endtablerow %}';
|
||||||
|
dst = '<table>' +
|
||||||
|
'<tr class="row1"><td class="col1">4</td><td class="col2">5</td></tr>' +
|
||||||
|
'</table>';
|
||||||
|
test(src, dst);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+20
-13
@@ -1,5 +1,5 @@
|
|||||||
const lexical = require('./lexical.js');
|
const lexical = require('./lexical.js');
|
||||||
const error = require('./error.js');
|
const TokenizationError = require('./error.js').TokenizationError;
|
||||||
|
|
||||||
function parse(html) {
|
function parse(html) {
|
||||||
var tokens = [];
|
var tokens = [];
|
||||||
@@ -8,6 +8,8 @@ function parse(html){
|
|||||||
var syntax = /({%(.*?)%})|({{(.*?)}})/g;
|
var syntax = /({%(.*?)%})|({{(.*?)}})/g;
|
||||||
var result, htmlFragment, token;
|
var result, htmlFragment, token;
|
||||||
var idx = 0;
|
var idx = 0;
|
||||||
|
var _idx = -1;
|
||||||
|
var _count = 0;
|
||||||
|
|
||||||
while ((result = syntax.exec(html)) !== null) {
|
while ((result = syntax.exec(html)) !== null) {
|
||||||
if (result.index > idx) {
|
if (result.index > idx) {
|
||||||
@@ -21,13 +23,15 @@ function parse(html){
|
|||||||
if (result[1]) {
|
if (result[1]) {
|
||||||
token = factory('tag', 1, result);
|
token = factory('tag', 1, result);
|
||||||
var match = token.value.match(lexical.tagLine);
|
var match = token.value.match(lexical.tagLine);
|
||||||
if (!match) error('illegal tag', token);
|
if (!match) {
|
||||||
|
throw new TokenizationError(`illegal tag: ${token.raw}`,
|
||||||
|
token.input, token.line);
|
||||||
|
}
|
||||||
|
|
||||||
token.name = match[1];
|
token.name = match[1];
|
||||||
token.args = match[2];
|
token.args = match[2];
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
token = factory('output', 3, result);
|
token = factory('output', 3, result);
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
}
|
}
|
||||||
@@ -44,22 +48,25 @@ function parse(html){
|
|||||||
return tokens;
|
return tokens;
|
||||||
|
|
||||||
function factory(type, offset, match) {
|
function factory(type, offset, match) {
|
||||||
|
|
||||||
|
var lines = match.input.slice(_idx + 1, match.index).split('\n');
|
||||||
|
var idx1 = match.input.lastIndexOf('\n', match.index);
|
||||||
|
var idx2 = match.input.indexOf('\n', match.index);
|
||||||
|
if(idx2 === -1) idx2 = match.input.length;
|
||||||
|
var input = match.input.slice(idx1 + 1, idx2);
|
||||||
|
_count += lines.length - 1;
|
||||||
|
_idx = match.index;
|
||||||
|
|
||||||
var token = {
|
var token = {
|
||||||
type,
|
type: type,
|
||||||
raw: match[offset],
|
raw: match[offset],
|
||||||
value: match[offset + 1].trim(),
|
value: match[offset + 1].trim(),
|
||||||
line: crCount(match.index) + 1
|
line: _count + 1,
|
||||||
|
input: input
|
||||||
};
|
};
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastIdx = -1;
|
|
||||||
var lastCount = 0;
|
|
||||||
function crCount(idx){
|
|
||||||
lastCount += html.slice(lastIdx+1, idx);
|
|
||||||
lastIdx = idx;
|
|
||||||
return lastCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.parse = parse;
|
exports.parse = parse;
|
||||||
|
|||||||
Reference in New Issue
Block a user