19bdace85a
Extracts the parser to `@handlebars/parser`, where it can be shared between different implementations. This means that e.g. Glimmer/Ember will be able to iterate on new features without forcing Handlebars to adopt them immediately, and vice versa. All implementors will be able to absorb changes as it makes sense for them.
83 lines
2.1 KiB
JavaScript
83 lines
2.1 KiB
JavaScript
import { Exception } from '@handlebars/parser';
|
|
import { createFrame, isArray, isFunction } from '../utils';
|
|
|
|
export default function(instance) {
|
|
instance.registerHelper('each', function(context, options) {
|
|
if (!options) {
|
|
throw new Exception('Must pass iterator to #each');
|
|
}
|
|
|
|
let fn = options.fn,
|
|
inverse = options.inverse,
|
|
i = 0,
|
|
ret = '',
|
|
data;
|
|
|
|
if (isFunction(context)) {
|
|
context = context.call(this);
|
|
}
|
|
|
|
if (options.data) {
|
|
data = createFrame(options.data);
|
|
}
|
|
|
|
function execIteration(field, index, last) {
|
|
if (data) {
|
|
data.key = field;
|
|
data.index = index;
|
|
data.first = index === 0;
|
|
data.last = !!last;
|
|
}
|
|
|
|
ret =
|
|
ret +
|
|
fn(context[field], {
|
|
data: data,
|
|
blockParams: [context[field], field]
|
|
});
|
|
}
|
|
|
|
if (context && typeof context === 'object') {
|
|
if (isArray(context)) {
|
|
for (let j = context.length; i < j; i++) {
|
|
if (i in context) {
|
|
execIteration(i, i, i === context.length - 1);
|
|
}
|
|
}
|
|
} else if (global.Symbol && context[global.Symbol.iterator]) {
|
|
const newContext = [];
|
|
const iterator = context[global.Symbol.iterator]();
|
|
for (let it = iterator.next(); !it.done; it = iterator.next()) {
|
|
newContext.push(it.value);
|
|
}
|
|
context = newContext;
|
|
for (let j = context.length; i < j; i++) {
|
|
execIteration(i, i, i === context.length - 1);
|
|
}
|
|
} else {
|
|
let priorKey;
|
|
|
|
Object.keys(context).forEach(key => {
|
|
// We're running the iterations one step out of sync so we can detect
|
|
// the last iteration without have to scan the object twice and create
|
|
// an itermediate keys array.
|
|
if (priorKey !== undefined) {
|
|
execIteration(priorKey, i - 1);
|
|
}
|
|
priorKey = key;
|
|
i++;
|
|
});
|
|
if (priorKey !== undefined) {
|
|
execIteration(priorKey, i - 1, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i === 0) {
|
|
ret = inverse(this);
|
|
}
|
|
|
|
return ret;
|
|
});
|
|
}
|