alchemymvc
Version:
MVC framework for Node.js
401 lines (316 loc) • 8.18 kB
JavaScript
/**
* The Alchemy helper
*
* @author Jelle De Loecker <jelle@elevenways.be>
* @since 0.0.1
* @version 1.0.0
*
* @param {ViewRender} view
*/
var Alchemy = Function.inherits('Alchemy.Helper', function Alchemy(view) {
Alchemy.super.call(this, view);
});
/**
* Function to execute on the client side, when the scene is made
*
* @author Jelle De Loecker <jelle@elevenways.be>
* @since 0.2.0
* @version 1.0.7
*
* @param {Scene} scene
* @param {Object} options
*/
Alchemy.setStatic(function onScene(scene, options) {
window.alchemy.initScene(scene, options);
});
/**
* Perform a resource request
*
* @author Jelle De Loecker <jelle@elevenways.be>
* @since 0.0.1
* @version 1.4.0
*
* @param {string|Object} options
* @param {Object} data
* @param {Function} callback
*
* @return {Pledge}
*/
Alchemy.setMethod(function getResource(options, data, callback) {
const that = this;
if (typeof options == 'string') {
if (options.indexOf('/') > -1) {
options = {
href : options
};
} else {
options = {
name : options
};
}
} else if (options instanceof Classes.RURL) {
options = {
href : options
};
}
if (typeof data == 'function') {
callback = data;
data = null;
} else {
if (data && !options.params) {
options.params = data;
}
}
let pledge;
if (!options.name && !options.href) {
pledge = Classes.Pledge.reject(new Error('Unable to get alchemy resource, a `name` or `href` option is required'));
pledge.done(callback);
return pledge;
}
function resolvePledge(err, result) {
if (err) {
return pledge.reject(err);
}
pledge.resolve(result);
}
if (Blast.isNode) {
const conduit = this.view.server_var('conduit');
if (!conduit) {
pledge = Classes.Pledge.reject(new Error('Could not find conduit, alchemy resource will not be fetched'));
pledge.done(callback);
return pledge;
}
pledge = new Classes.Pledge();
pledge.done(callback);
conduit.loopback(options, resolvePledge);
return pledge;
}
let method = 'get',
url;
if (options.href) {
url = options.href;
} else {
// See if this is a socket route
let config = this.view.helpers.Router.routeConfig(options.name, true);
if (config && config.socket_route) {
pledge = new Classes.Pledge();
pledge.done(callback);
// In this case the "alchemy" variable points to the window.alchemy object
alchemy.submit(config.name, data, resolvePledge);
return pledge;
}
config = this.view.helpers.Router.routeConfig(options.name);
if (config) {
if (config.methods && config.methods.indexOf('get') == -1) {
method = config.methods[0];
}
if (config.cache != null && options.cache == null) {
options.cache = config.cache;
}
}
// Get the url to the resource
url = hawkejs.scene.helpers.Router.routeUrl(options.name, options.params);
if (!url && typeof options.name == 'string') {
url = hawkejs.scene.helpers.Router.routeUrl('APIResource', {name: options.name});
}
if (!url) {
pledge = Classes.Pledge.reject(new Error('No URL could be found for route "' + options.name + '"'));
pledge.done(callback);
return pledge;
}
}
options.href = url;
if (data) {
options[method] = data;
} else if (method != 'get') {
options[method] = true;
}
pledge = this.view.fetch(options);
pledge.done(callback);
return pledge;
});
/**
* Register & recompile a callback
*
* @author Jelle De Loecker <jelle@elevenways.be>
* @since 0.2.0
* @version 0.2.0
*
* @param {Function} callback
*
* @return {Function}
*/
Alchemy.setMethod(function registerCallback(callback) {
if (!this.callbacks) {
this.callbacks = {};
}
// The callback will need to be recompiled
if (!this.callbacks[callback.name]) {
this.callbacks[callback.name] = this.view.hawkejs.compile({
// The function that will be re-compiled
compiled: callback,
// Template name
template_name: 'callback_' + callback.name,
// It's a single function that needs to be called with the 'group_arg' var
call: 'group_arg',
// Make sure these variables are available in the scope
scope: this.view.functionScopes.slice(0)
});
}
return this.callbacks[callback.name];
});
/**
* Register a group and possible id
*
* @author Jelle De Loecker <jelle@elevenways.be>
* @since 0.2.0
* @version 0.2.0
*/
Alchemy.setMethod(function registerGroup(name, id, callback) {
// Make sure the groups exist
if (!this.groups) {
this.groups = {};
}
if (!this.groups[name]) {
this.groups[name] = {
state: 'new',
hinder: null,
ids: []
};
}
if (id && !this.groups[name].ids[id]) {
this.groups[name].ids[id] = [];
}
if (callback) {
this.registerCallback(callback);
this.groups[name].ids[id].push(callback);
}
});
/**
* Get the group data
*
* @author Jelle De Loecker <jelle@elevenways.be>
* @since 0.2.0
* @version 0.2.0
*/
Alchemy.setMethod(function getGroupData(name, callback) {
var that = this,
group;
group = this.groups[name];
if (!group.hinder) {
group.hinder = Function.hinder(function getData(done) {
that.getResource(name, {ids: Object.keys(group.ids)}, done);
});
}
if (callback) group.hinder.push(callback);
});
/**
* Aggregate data.
* The callback needs to be named and be unique per scope structure
*
* @author Jelle De Loecker <jelle@elevenways.be>
* @since 0.2.0
* @version 0.2.0
*/
Alchemy.setMethod(function group(name, id, callback) {
var that = this,
scope;
// Keep reference to the current scope
scope = this.view.functionScopes.slice(0);
// Register the group and callback
this.registerGroup(name, id, callback);
// Return an async placeholder
this.view.async(function doAsync(next) {
that.getGroupData(name, function gotData(err, data) {
var id_data;
if (err) {
return next(err);
}
if (data) {
id_data = data[id];
}
that.view.hawkejs.render(that.registerCallback(callback), {__scope: scope, group_arg: id_data}, next);
});
});
});
/**
* Print a segment
*
* @author Jelle De Loecker <jelle@elevenways.be>
* @since 0.5.0
* @version 1.3.17
*
* @param {string|Object} options
* @param {Object} data
*
* @return {HePlaceholder}
*/
Alchemy.setMethod(function segment(options, data) {
const that = this;
if (typeof options == 'string') {
options = {
name : options
};
}
let print_placeholder = options.print;
if (print_placeholder == null) {
print_placeholder = true;
}
// Prints a placeholder
return this.view.async(function doAsync(next) {
var conduit,
route;
if (Blast.isNode) {
conduit = that.view.serverVar('conduit');
if (!conduit) {
return next(null, '<!-- Failed to render segment ' + options.name + ': no conduit found -->');
}
options.params = data;
conduit.loopback(options, function doneLoopback(err, res) {
// Response can be an HTML string or a Hawkejs.BlockBuffer instance
next(err, res);
});
return;
}
// Create a dummy root element so the renderer
// does not place it anywhere else
let root = document.createElement('div');
// Get the route configuration
route = that.view.helpers.Router.routeUrl(options.name, data, {extra_get_parameters: false});
hawkejs.scene.openUrl(route, {get: data, history: false, root: root}, function done(err, result) {
if (err) {
return next(err);
}
if (!result) {
return next();
}
let block = result.blocks.get(result.main_block_name);
next(null, block);
});
}, {print: print_placeholder});
});
/**
* Get the current locale
*
* @author Jelle De Loecker <jelle@elevenways.be>
* @since 1.1.0
* @version 1.1.0
*
* @return {string}
*/
Alchemy.setMethod(function getLocale() {
var locales = this.view_render.expose('locales');
if (locales && locales[0]) {
return locales[0];
}
return 'en';
});
if (!Blast.isBrowser) {
return;
}
// Send a message to the server when we unload the page
window.addEventListener('unload', function(event) {
if (console) {
console.log('Unloading the page ...');
}
});