Skip to content

Instantly share code, notes, and snippets.

@createvibe
Created February 2, 2016 00:38
Show Gist options
  • Save createvibe/73948b67f8ef60f4d09c to your computer and use it in GitHub Desktop.
Save createvibe/73948b67f8ef60f4d09c to your computer and use it in GitHub Desktop.
Twig.extend(function(Twig) {
'use strict';
var placeholders = {};
Twig.Templates.registerLoader('async', async_loader);
// overloaded to handle logic output promises
Twig.parse = function (tokens, context) {
try {
var output = [],
// Track logic chains
chain = true,
that = this,
promises = [];
Twig.forEach(tokens, function parseToken(token) {
Twig.log.debug("Twig.parse: ", "Parsing token: ", token);
switch (token.type) {
case Twig.token.type.raw:
output.push(Twig.filters.raw(token.value));
break;
case Twig.token.type.logic:
var logic_token = token.token,
logic = Twig.logic.parse.apply(that, [logic_token, context, chain]);
if (logic.chain !== undefined) {
chain = logic.chain;
}
if (logic.context !== undefined) {
context = logic.context;
}
if (logic.output !== undefined) {
if (logic.output instanceof Promise) {
(function(idx) {
output.push(null);
logic.output.then(function(data) {
output[idx] = data;
});
promises.push(logic.output);
}(output.length));
} else {
output.push(logic.output);
}
}
break;
case Twig.token.type.comment:
// Do nothing, comments should be ignored
break;
//Fall through whitespace to output
case Twig.token.type.output_whitespace_pre:
case Twig.token.type.output_whitespace_post:
case Twig.token.type.output_whitespace_both:
case Twig.token.type.output:
Twig.log.debug("Twig.parse: ", "Output token: ", token.stack);
// Parse the given expression in the given context
output.push(Twig.expression.parse.apply(that, [token.stack, context]));
break;
}
});
if (promises.length !== 0) {
var self = this;
return new Promise(function(resolve, reject) {
Promise.all(promises).then(function(results) {
resolve(Twig.output.apply(self, [output]));
});
});
}
return Twig.output.apply(this, [output]);
} catch (ex) {
Twig.log.error("Error parsing twig template " + this.id + ": ");
if (ex.stack) {
Twig.log.error(ex.stack);
} else {
Twig.log.error(ex.toString());
}
if (this.options.rethrow) throw ex;
if (Twig.debug) {
return ex.toString();
}
}
};
function async_loader(location, params, callback, error_callback) {
var p,
resolve,
reject,
template,
pid = Object.keys(placeholders).length;
p = placeholders[pid] = {
id: pid,
parentId: null,
loaded: false,
data: null,
promise: new Promise(function(res, rej) {
resolve = res;
reject = rej;
})
};
p.promise.placeholderId = pid;
p.xhr = load_file(location, params, function(data) {
p.loaded = true;
p.data = data;
resolve(data);
}, reject);
params.url = location;
template = new AsyncTemplate({
id: location,
pid: pid,
async: false,
method: 'async',
href: location
});
if (typeof callback === 'function') {
callback(template);
}
return template;
}
function load_file(location, params, callback, error_callback) {
var xhr = new XMLHttpRequest;
xhr.onreadystatechange = function() {
var data;
if (xhr.readyState === 4) {
if (xhr.status === 200) {
if (params.precompiled) {
data = JSON.parse(xhr.responseText);
} else {
data = xhr.responseText;
}
if (typeof callback === 'function') {
callback(data);
}
} else {
if (typeof error_callback === 'function') {
error_callback(xhr);
}
}
}
};
// aways async!
xhr.open('GET', location, true);
xhr.send();
return xhr;
}
function AsyncTemplate(params) {
Twig.Template.apply(this, arguments);
if (!this.options) {
this.options = {};
}
this.placeholderId = params.pid;
this.options.placeholderId = this.placeholderId;
}
AsyncTemplate.prototype = Object.create(Twig.Template.prototype, {
placeholderId: {
value: null,
writable: true
},
render: {
value: function render(context, params) {
var self = this,
p = placeholders[this.placeholderId];
return new Promise(function(resolve, reject) {
p.promise.then(function(data) {
if (typeof data === 'string') {
self.tokens = Twig.prepare.call(self, data);
} else {
self.tokens = data;
}
var promise = Twig.Template.prototype.render.call(self, context, params);
if (!(promise instanceof Promise)) {
var deferred_output = promise;
promise = new Promise(function(resolve, reject) {
resolve(deferred_output);
});
}
promise.then(resolve).catch(reject);
}).catch(reject);
});
}
}
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment