lazo-next
Version:
A client-server web framework built on Node.js that allows front-end developers to easily create a 100% SEO compliant, component MVC structured web application with an optimized first page load.
209 lines (185 loc) • 10.6 kB
JavaScript
define(
[
'jquery', 'underscore', 'rehydrate/main', 'utils/prune',
'lib/client/state', 'utils/document', 'lib/client/prime',
'renderer', 'context', 'utils/cmpProcessor', 'requestFilters',
'resolver/file', 'l!viewManager', 'bundler', 'jquerycookie'
],
function ($, _, rehydrate, prune, state, doc, prime, renderer, Context, cmpProcessor, filter, file, viewManager, Bundler) {
'use strict';
var bundle = new Bundler();
var rootCtx = { dependencies: { modules: { css: [], js: [] } } };
function destroy(ctl) {
var children = ctl.children || {},
components = renderer.getList('component', ctl),
i = components.length;
while (i) {
i--;
try {
viewManager.cleanupView(components[i].currentView);
components[i].currentView.remove();
components[i]._getEl().remove();
for (var key in components[i]) {
if (key === 'currentView') {
components[i][key].el = null;
components[i][key].$el = null;
}
components[i][key] = null;
delete components[i][key];
}
} catch (e) {
LAZO.logger.warn('[client.post.destroy] Error while destroying component', ctl, e);
}
}
}
return function (eventData) {
function process(eventData, modules) {
var cmpDef = cmpProcessor.getDef(eventData.route);
var options = {
params: eventData.parameters,
_rootCtx: LAZO.ctl.ctx._rootCtx
};
var ctx = cmpProcessor.createCtx(options);
filter.apply(eventData.route, eventData.parameters, ctx, function (redirect) {
if (redirect) {
return LAZO.app.navigate(ctx, redirect);
}
cmpProcessor.process({
ctx: ctx,
def: cmpDef,
error: function (err) {
LAZO.logger.error('[client.execute]', 'Error executing component action: ' + err);
LAZO.error.render({
statusCode: '500',
error: 'Internal Server Error',
message: 'An internal server error occurred'
});
LAZO.app.trigger('navigate:application:error', eventData);
},
success: function (ctl, ctx, cmpDef) {
var hasLayoutChanged = !cmpDef.layout || (cmpDef.layout !== LAZO.layout);
var $target = $(hasLayoutChanged ? 'body' : '[lazo-cmp-container="lazo-layout-body"]');
var linksTypesLoaded = 0;
renderer.getTreeHtml(ctl, null, null, function (html) {
if (modules) {
delete ctx._rootCtx.dependencies;
} else {
var components = cmpProcessor.getComponents(hasLayoutChanged ? ctl : LAZO.ctl, []);
var links = {};
for (var i = 0; i < components.length; i++) {
links[components[i].name] = { css: null, imports: null };
links[components[i].name].css = file.getComponentFiles([components[i].name], function (fileName) {
return fileName.substr(-4, 4) === '.css' && fileName.indexOf('/imports/') === -1;
});
links[components[i].name].imports = file.getComponentFiles([components[i].name], function (fileName) {
return fileName.indexOf('/imports/') !== -1 && fileName.substr(-5, 5) === '.html';
});
}
var cssLinks = [];
var importLinks = [];
for (var k in links) {
for (var j = 0; j < links[k].css.length; j++) {
cssLinks.push({
'lazo-link-ctx': k,
href: '/' + links[k].css[j]
});
}
for (var l = 0; l < links[k].imports.length; l++) {
importLinks.push({
'lazo-link-ctx': k,
href: '/' + links[k].imports[l]
});
}
}
cssLinks = bundle._createCSSLinks(cssLinks);
// remove old component css
ctx._rootCtx.dependencies.css = bundle._createCSSLinks(ctx._rootCtx.dependencies.css);
ctx._rootCtx.dependencies.css = bundle.sortCss(state.cleanUpLinkDependencies(ctx._rootCtx.dependencies.css, cssLinks));
importLinks = bundle._createImportLinks(importLinks);
ctx._rootCtx.dependencies.imports = bundle._createImportLinks(ctx._rootCtx.dependencies.imports);
ctx._rootCtx.dependencies.imports = bundle.sortImports(state.cleanUpLinkDependencies(ctx._rootCtx.dependencies.imports, importLinks));
}
LAZO.layout = cmpDef.layout;
rootCtx = _.extend({}, rootCtx, ctx._rootCtx);
LAZO.app.trigger('navigate:application:response', eventData);
LAZO.layout = cmpDef.layout;
state.set(rootCtx); // push state object to history
var css = state.getAddRemoveLinks('css'); // get add, remove css
var imports = state.getAddRemoveLinks('imports');
prime(css.add, 'css', false); // prefetch css
if (hasLayoutChanged) {
destroy(LAZO.ctl); // clean up previous root controller
delete LAZO.ctl;
LAZO.ctl = ctl; // assign new controller
} else {
// layout body child changed; addChild pushes to children array;
// remove the old child, the first element in the children array
destroy(LAZO.ctl.children['lazo-layout-body'][0]);
LAZO.ctl.children['lazo-layout-body'].shift();
// update layout parameters
_.extend(LAZO.ctl.ctx.params, ctl.ctx.params);
}
prune(LAZO.ctl); // remove orphaned models
function onLinksDone(loaded) {
if (loaded === 2) {
if (hasLayoutChanged) {
$target.remove('[lazo-cmp-name]');
$target.html(html);
} else {
$target.html(html);
}
setTimeout(function () {
LAZO.error.clear();
$target.css({ 'visibility': 'visible'} );
viewManager.attachViews(ctl, function (err, success) {
if (err) {
LAZO.logger.error('[LAZO.navigate] Error attaching views', err);
}
doc.setTitle(LAZO.ctl.ctx._rootCtx.pageTitle);
LAZO.app.trigger('navigate:application:complete', eventData);
});
}, 0);
}
}
// keep dimensions, but hide content so that new css updating does not cause any display issues
$target.css({ 'visibility': 'hidden' });
doc.updateLinks(imports.add, imports.remove, 'import', function () {
linksTypesLoaded++;
onLinksDone(linksTypesLoaded);
});
doc.updateLinks(css.add, css.remove, 'css', function () {
linksTypesLoaded++;
onLinksDone(linksTypesLoaded);
});
});
}
});
});
}
// entry point
(function response() {
rootCtx = { dependencies: { modules: { css: [], js: [] } } };
if ($.cookie('development') !== '1') {
bundle.response(eventData.route, window.location.pathname, {
success: function (modules) {
if (modules) {
rootCtx.dependencies.modules = modules.js;
rootCtx.dependencies.css = bundle.sortCss(bundle._createCSSLinks(modules.css));
rootCtx.dependencies.imports = bundle.sortImports(bundle._createImportLinks(modules.imports));
LAZO.require(modules.js, function () {
process(eventData, true);
});
} else {
process(eventData);
}
},
error: function (err) {
throw err instanceof Error ? err : new Error(err);
}
});
} else {
process(eventData);
}
})();
};
});