');
17 |
18 | $('body > [lazo-cmp-name]').hide();
19 | $('body').append($lazoErr);
20 | $lazoErr.html(compiledTemplate(errContext));
21 | });
22 | }
23 |
24 | };
25 |
26 | });
--------------------------------------------------------------------------------
/lib/common/requestFilters.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |
3 | 'use strict';
4 |
5 | var filters = {};
6 |
7 | function iterate(path, params, ctx, fns, i, callback) {
8 | if (fns[i]) {
9 | fns[i](path, params, ctx, {
10 | success: function (redirect) {
11 | if (redirect) {
12 | return callback(redirect);
13 | } else {
14 | i += 1;
15 | iterate(path, params, ctx, fns, i, callback);
16 | }
17 | },
18 | error: function (err) {
19 | throw err instanceof Error ? err : new Error(err);
20 | }
21 | });
22 | } else {
23 | return callback();
24 | }
25 | }
26 |
27 | return {
28 |
29 | apply: function (path, params, ctx, callback) {
30 | var fns = [];
31 | var match;
32 | for (var key in filters) {
33 | if (path.match(new RegExp(key))) {
34 | fns = fns.concat(filters[key]);
35 | }
36 | }
37 |
38 | if (!fns.length) {
39 | return callback();
40 | }
41 |
42 | iterate(path, params, ctx, fns, 0, callback);
43 | },
44 |
45 | add: function (regex, func) {
46 | filters[regex] = filters[regex] || [];
47 | filters[regex].push(func);
48 | return this;
49 | }
50 |
51 | };
52 |
53 | });
--------------------------------------------------------------------------------
/lib/common/resolver/assets.js:
--------------------------------------------------------------------------------
1 | define(['underscore'], function (_) {
2 |
3 | 'use strict';
4 |
5 | return {
6 |
7 | keyRegex: /^(?:[a-z]{2}(?:-[A-Z]{2})?[\/\\])(.+)/,
8 |
9 | getLocales: function (ctx) {
10 | var languages = [];
11 |
12 | if (LAZO.isClient) {
13 | return languages.concat(navigator.languages || [navigator.language]).concat(['defaults']);
14 | } else {
15 | return languages.concat(this.resolveAcceptLanguge(ctx._request.raw.req.headers['accept-language'])).concat(['defaults']);
16 | }
17 | },
18 |
19 | resolveAssets: function (map, ctx) {
20 | var locales = this.getLocales(ctx);
21 | var i = locales.length;
22 | var assets = {};
23 | var localeAssets;
24 |
25 | while (i--) {
26 | localeAssets = map[locales[i]];
27 | if (!_.isUndefined(localeAssets)) {
28 | _.extend(assets, localeAssets);
29 | }
30 | }
31 |
32 | return assets;
33 | },
34 |
35 | resolveAssetKey: function (key, ctx) {
36 | return key.replace(this.keyRegex, '$1');
37 | },
38 |
39 | resolveAssetPath: function (path, component, ctx) {
40 | var prefix = component === 'app' ? '/' : '/components/';
41 | return prefix + component + '/assets/' + path;
42 | },
43 |
44 | resolveAcceptLanguge: function (languages, ctx) {
45 | languages = _.map(languages.split(','), function (language) {
46 | var langQuality = language.split(';');
47 | var languageParts = langQuality[0].split('-');
48 | return {
49 | language: languageParts[0],
50 | langLocale: langQuality[0],
51 | quality: langQuality[1] ? parseFloat(langQuality[1].split('=')[1]) : 1
52 | };
53 | });
54 |
55 | languages.sort(function (a, b) {
56 | if (a.language === b.language) {
57 | if (a.quality < b.quality) {
58 | return 1;
59 | }
60 | if (a.quality > b.quality) {
61 | return -1;
62 | }
63 | }
64 |
65 | return 0;
66 | });
67 |
68 | return _.map(languages, function (lang) {
69 | return lang.langLocale;
70 | });
71 | }
72 |
73 | };
74 |
75 | });
--------------------------------------------------------------------------------
/lib/common/resolver/component.js:
--------------------------------------------------------------------------------
1 | define(['underscore', 'resolver/file'], function (_, file) {
2 |
3 | 'use strict';
4 |
5 | return {
6 |
7 | getDef: function (route) {
8 | var defaultLayout = LAZO.app.defaultLayout;
9 | var def = LAZO.routes[route];
10 | var isObj;
11 | var name = (isObj = _.isObject(def)) ? def.component : def;
12 | var compParts = name.split('#');
13 | var action;
14 | var layout;
15 |
16 | name = compParts.length ? compParts[0] : name;
17 | action = compParts.length && compParts[1] ? compParts[1] : 'index';
18 | // possible scenarios
19 | // 1. no layout: def is a string, e.g. foo#baz, foo
20 | // 2. layout: def is an object with a layout
21 | // 3. default layout and layout: def is an object, app has a default layout
22 | // 4. default layout and layout === false: def is an object, app has a default layout, but route def def.layout = false
23 | layout = (isObj && def.layout) ? def.layout : (defaultLayout && !isObj || isObj && !_.isBoolean(def.layout)) ?
24 | defaultLayout : null;
25 |
26 | return _.extend({}, isObj ? def : {}, {
27 | name: name,
28 | action: action,
29 | layout: layout
30 | });
31 | },
32 |
33 | getLinks: function (cmpName, type) {
34 | return file.getComponentFiles([cmpName], function (link) {
35 | if (type === 'css') {
36 | return link.substr(-4, 4) === '.css' && link.indexOf('/imports/') === -1;
37 | } else {
38 | return link.indexOf('/imports/') !== -1 && link.substr(-5, 5) === '.html';
39 | }
40 | });
41 | }
42 |
43 | };
44 |
45 | });
46 |
--------------------------------------------------------------------------------
/lib/common/resolver/file.js:
--------------------------------------------------------------------------------
1 | define(['l!serverFileResolver', 'underscore', 'utils/template'], function (serverFileResolver, _, template) {
2 |
3 | 'use strict';
4 |
5 | return _.extend({
6 |
7 | getPath: function (moduleName, cmpName, moduleType) {
8 | var pathPrefix = moduleName.indexOf('a:') === 0 ? 'app' : '';
9 |
10 | moduleName = pathPrefix ? moduleName.split(':')[1] : moduleName;
11 | pathPrefix = pathPrefix && (moduleType === 'view' || moduleType === 'template') ?
12 | pathPrefix + '/views' : pathPrefix;
13 |
14 | return (pathPrefix ? pathPrefix : ('components/' + cmpName)) +
15 | (!pathPrefix && (moduleType === 'view' || moduleType === 'template') ? '/views' : '') +
16 | '/' + moduleName;
17 | },
18 |
19 | getBasePath: function (moduleName, cmpName, moduleType) {
20 | var modulePath = this.getPath(moduleName, cmpName, moduleType);
21 | return modulePath.substr(0, modulePath.lastIndexOf('/'));
22 | },
23 |
24 | getTemplateName: function (view) {
25 | return _.result(view, 'templateName') || view.name;
26 | },
27 |
28 | getTemplatePath: function (view) {
29 | return this.getPath(this.getTemplateName(view) + '.' + template.getTemplateExt(view.templateEngine), view.ctl.name, 'template');
30 | },
31 |
32 | // TODO: deprecated, can be removed once load_view is merged to dev
33 | convertTemplatePath: function (templatePath) {
34 | return 'tmp/' + templatePath.substr(0, templatePath.lastIndexOf('.')) + '.js';
35 | },
36 |
37 | getComponentFiles: function (components, filter) {
38 | var list = LAZO.files.components;
39 | var files = [];
40 |
41 | filter = filter || function () { return true; };
42 |
43 | for (var i = 0; i < components.length; i++) {
44 | for (var k in list) {
45 | if (k.indexOf('components/' + components[i] + '/') === 0 && filter(k)) {
46 | files.push(k);
47 | }
48 | }
49 | }
50 |
51 | return files;
52 | }
53 |
54 | }, serverFileResolver);
55 |
56 | });
--------------------------------------------------------------------------------
/lib/common/resolver/main.js:
--------------------------------------------------------------------------------
1 | define(['resolver/requireConfigure', 'resolver/route', 'resolver/file', 'resolver/component'],
2 | function (requireConfigure, routeRes, file, component) {
3 |
4 | 'use strict';
5 |
6 | return {
7 |
8 | // requirejs config generator
9 | getReqConfig: function (env, options, callback) {
10 | requireConfigure.get(env, options, callback);
11 | },
12 |
13 | // route transformer
14 | transformRoute: function (route) {
15 | return routeRes.transform(route);
16 | },
17 |
18 | // file helpers
19 | isBase: function (modulePath, moduleType, callback, options) {
20 | var isExtendedClass = (LAZO.files.components[modulePath + '.js'] ||
21 | LAZO.files.appViews[modulePath + '.js']) ||
22 | LAZO.files.models[modulePath + '.js'];
23 |
24 | return callback(isExtendedClass ? false : true);
25 | },
26 |
27 | getPath: function (moduleName, cmpName, moduleType) {
28 | return file.getPath(moduleName, cmpName, moduleType);
29 | },
30 |
31 | getBasePath: function (moduleName, cmpName, moduleType) {
32 | return file.getBasePath(moduleName, cmpName, moduleType);
33 | },
34 |
35 | getTemplateName: function (view) {
36 | return file.getTemplateName(view);
37 | },
38 |
39 | getTemplatePath: function (view) {
40 | return file.getTemplatePath(view);
41 | },
42 |
43 | convertTemplatePath: function (templatePath) {
44 | return file.convertTemplatePath(templatePath);
45 | },
46 |
47 | getComponentCss: function (cmpName) {
48 | return component.getLinks(cmpName, 'css');
49 | },
50 |
51 | getComponentImports: function (cmpName) {
52 | return component.getLinks(cmpName, 'import');
53 | },
54 |
55 | resolvePath: function (from, to) {
56 | return file.resolvePath(from, to);
57 | }
58 |
59 | };
60 |
61 | });
--------------------------------------------------------------------------------
/lib/common/resolver/model.js:
--------------------------------------------------------------------------------
1 | define(['underscore'], function (_) {
2 |
3 | 'use strict';
4 |
5 | var subRegEx = /\{\{\s*([^|}]+?)\s*(?:\|([^}]*))?\s*\}\}/g;
6 |
7 | return {
8 |
9 | methodMap: {
10 | 'create': 'POST',
11 | 'update': 'PUT',
12 | 'patch': 'PATCH',
13 | 'delete': 'DELETE',
14 | 'read': 'GET'
15 | },
16 |
17 | substitute: function (s, o) {
18 | return s.replace ? s.replace(subRegEx, function (match, key) {
19 | return _.isUndefined(o[key]) ? match : o[key];
20 | }) : s;
21 | }
22 |
23 |
24 | };
25 |
26 | });
--------------------------------------------------------------------------------
/lib/common/resolver/requireConfigure.js:
--------------------------------------------------------------------------------
1 | define(['json!resolver/paths.json'], function (paths) {
2 |
3 | var configs,
4 | loader;
5 |
6 | function getLoader(basePath) {
7 | if (loader) {
8 | return loader;
9 | }
10 |
11 | loader = requirejs.config({
12 | baseUrl: basePath,
13 | context: 'configloader',
14 | paths: { // set paths for bootstrapping
15 | 'json': 'lib/vendor/json',
16 | 'text': 'lib/vendor/text',
17 | 'common': 'lib/common'
18 | }});
19 |
20 | return loader;
21 | }
22 |
23 | function resolvePath(path, env, options) {
24 | var basePath;
25 |
26 | if (env === 'server') {
27 | path = (options.basePath ? options.basePath + '/' : '') + path;
28 | }
29 |
30 | return path;
31 | }
32 |
33 | function getPaths(env, options) {
34 | var _paths = JSON.parse(JSON.stringify(paths)),
35 | needle = '/{env}/',
36 | replace = '/' + env + '/';
37 |
38 | for (var k in _paths.common) { // update env specific implementation paths
39 | _paths.common[k] = resolvePath(_paths.common[k].replace(needle, replace), env, options);
40 | }
41 | for (k in _paths[env]) { // merge env specific paths
42 | _paths.common[k] = resolvePath(_paths[env][k], env, options);
43 | }
44 |
45 | return _paths.common;
46 | }
47 |
48 | function augment(receiver, giver) {
49 | for (var key in giver) {
50 | receiver[key] = giver[key];
51 | }
52 |
53 | return receiver;
54 | }
55 |
56 | configs = {
57 |
58 | client: function (options, callback) {
59 |
60 | function augmentConf(options) {
61 | return augment(options, {
62 | baseUrl: '/',
63 | map: {
64 | '*': {
65 | 'l': '/lib/client/loader.js'
66 | }
67 | },
68 | paths: augment(getPaths('client', options), options.paths),
69 | shim: options.shim
70 | });
71 | }
72 |
73 | try { // client
74 | window; // attempt to access window object; if server catch err & use requirejs loader
75 | // use globals set by server render to simplePage.hbs, lazoShim, lazoPaths
76 | callback(null, augmentConf(LAZO.initConf.config));
77 | } catch (e) { // server
78 | callback(null, augmentConf(options));
79 | }
80 | },
81 |
82 | server: function (options, callback) {
83 |
84 | function augmentConf(options) {
85 | return augment(options, {
86 | shim: options.shim || {},
87 | baseUrl: options.baseUrl,
88 | context: 'application',
89 | map: {
90 | '*': {
91 | 's': options.basePath + '/lib/server/loader.js'
92 | }
93 | },
94 | paths: augment(getPaths('server', options), options.paths)
95 | });
96 | }
97 |
98 | callback(null, augmentConf(options));
99 | }
100 |
101 | };
102 |
103 | return {
104 | get: function (env, options, callback) {
105 | if (arguments.length === 2) {
106 | callback = arguments[1];
107 | }
108 | return configs[env](options, callback);
109 | }
110 | };
111 |
112 | });
--------------------------------------------------------------------------------
/lib/common/resolver/route.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |
3 | 'use strict';
4 |
5 | var REGEX_QUERY_STRING = /(\(?\?.*)$/;
6 | var REGEX_PATH_PARAM = /:(\w+)/g;
7 | var REGEX_OPT_PATH_PARAM = /\(\/\{(\w+)\}(\/?)\)$/;
8 | var REGEX_OPT_TRAILING_SLASH = /(.*)\(\/\)$/;
9 | var REGEX_DYNAMIC_PATH = /(\*)(\w+)/g;
10 |
11 | return {
12 |
13 | transform: function (route) {
14 | var routeTrailingSlash = null;
15 |
16 | route = '/' + route;
17 | route = route.replace(REGEX_QUERY_STRING, '');
18 | route = route.replace(REGEX_PATH_PARAM, '{$1}');
19 | route = route.replace(REGEX_OPT_PATH_PARAM, '/{$1?}$2');
20 |
21 | if (REGEX_OPT_TRAILING_SLASH.test(route)) {
22 | route = route.replace(REGEX_OPT_TRAILING_SLASH, '$1');
23 | // handle case of developer adding a trailing slash to a dynamic path:
24 | // test-dynamic-routes/*nodes(/)
25 | if (!REGEX_DYNAMIC_PATH.test(route)) {
26 | routeTrailingSlash = route + '/';
27 | }
28 | }
29 |
30 | // transform backbone style splats to hapi splats:
31 | // test-dynamic-routes/*nodes -> test-dynamic-routes/{nodes*}
32 | if (LAZO.app.isServer) {
33 | route = route.replace(REGEX_DYNAMIC_PATH, '{$2$1}');
34 | }
35 |
36 | return {
37 | routeTrailingSlash: routeTrailingSlash,
38 | route: route
39 | };
40 | }
41 |
42 | };
43 |
44 | });
--------------------------------------------------------------------------------
/lib/common/templates/403.hbs:
--------------------------------------------------------------------------------
1 |
You shall not pass!
2 |
3 | - Code: {{statusCode}}
4 | - Error: {{error}}
5 | - Message: {{message}}
6 |
--------------------------------------------------------------------------------
/lib/common/templates/404.hbs:
--------------------------------------------------------------------------------
1 |
These are not the droids you are looking for.
2 |
3 | - Code: {{statusCode}}
4 | - Error: {{error}}
5 | - Message: {{message}}
6 |
--------------------------------------------------------------------------------
/lib/common/templates/500.hbs:
--------------------------------------------------------------------------------
1 |
Damn it Jim!
2 |
3 | - Code: {{statusCode}}
4 | - Error: {{error}}
5 | - Message: {{message}}
6 |
--------------------------------------------------------------------------------
/lib/common/templates/page.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{!--
3 | htmlTag is generated by LAZO.app.setHtmlTag;
4 | removing this if, else block will cause LAZO.app.setHtmlTag
5 | to not have any effect on the application
6 | --}}
7 | {{#if htmlTag}}
8 | {{{htmlTag}}}
9 | {{else}}
10 |
11 | {{/if}}
12 |
13 |
14 | {{!--
15 | START: DO NOT REMOVE
16 | If any of the code between "START" and "END" is removed it will break lazo.
17 | --}}
18 | {{#each tags}}
19 | <{{name}} {{#each attributes}}{{@key}}="{{this}}" {{/each}}>{{content}}{{name}}>
20 | {{/each}}
21 | {{#each dependencies.imports}}
22 |
23 | {{/each}}
24 | {{#each dependencies.css}}
25 |
26 | {{/each}}
27 |
28 |
42 |
43 | {{#if lib}}
44 |
59 | {{else}}
60 |
75 | {{/if}}
76 | {{!-- END: DO NOT REMOVE --}}
77 |
78 | {{!--
79 | bodyClass is generated by LAZO.app.setBodyClass;
80 | removing this if, else block will cause LAZO.app.setBodyClass
81 | to not have any effect on the application
82 | --}}
83 |
84 | {{!-- DO NOT REMOVE --}}
85 | {{{body}}}
86 |
87 |
--------------------------------------------------------------------------------
/lib/common/utils/assetsMixin.js:
--------------------------------------------------------------------------------
1 | define(['assets'], function (LazoAssets) {
2 |
3 | 'use strict';
4 |
5 | var assets = new LazoAssets();
6 |
7 | return {
8 |
9 | getAssets: function (cmpName, ctx, options) {
10 | if (!LAZO.app.getAssets) {
11 | return options.success({});
12 | }
13 |
14 | assets.get([cmpName], ctx, {
15 | success: function (assets) {
16 | options.success(assets[cmpName]);
17 | },
18 | error: function (err) {
19 | options.error(err);
20 | }
21 | });
22 | }
23 |
24 | };
25 |
26 | });
--------------------------------------------------------------------------------
/lib/common/utils/cmpProcessor.js:
--------------------------------------------------------------------------------
1 | define(['underscore', 'utils/loader', 'context', 'resolver/component', 'utils/assetsMixin'],
2 | function (_, loader, Context, cmpResolver, assetsMixin) {
3 |
4 | 'use strict';
5 |
6 | return _.extend({
7 |
8 | getRootCtxForReply: function (ctx, ctl) {
9 | var components = this.getComponents(ctl, []);
10 | var assets = {};
11 | var i = components.length;
12 |
13 | while (i--) {
14 | assets[components[i].name] = components[i].ctx.assets;
15 | }
16 |
17 | return _.extend(_.omit(ctx._rootCtx, 'modelInstances'), {
18 | dependencies: ctx._rootCtx.dependencies,
19 | assets: assets,
20 | modelInstances: {}
21 | });
22 | },
23 |
24 | getDef: function (route) {
25 | return cmpResolver.getDef(route);
26 | },
27 |
28 | createCtx: function (options) {
29 | return new Context(options);
30 | },
31 |
32 | getComponents: function (ctl, list) {
33 | list.push(ctl);
34 |
35 | if (_.size(ctl.children)) {
36 | for (var k in ctl.children) {
37 | for (var i = 0; i < ctl.children[k].length; i++) {
38 | this.getComponents(ctl.children[k][i], list);
39 | }
40 | }
41 | }
42 |
43 | return list;
44 | },
45 |
46 | getComponentNames: function (ctl) {
47 | return _.map(this.getComponents(ctl, []), function (ctl) {
48 | return ctl.name;
49 | });
50 | },
51 |
52 | process: function (options) {
53 | var self = this;
54 | var cmpDef = options.def;
55 | var ctx = options.ctx;
56 | var hasLayoutChanged = !cmpDef.layout || (cmpDef.layout !== LAZO.layout) ? true : false;
57 | var cmpToLoad = (hasLayoutChanged && cmpDef.layout) ? cmpDef.layout : cmpDef.name;
58 |
59 | function onError(err) {
60 | return options.error(err);
61 | }
62 |
63 | if (LAZO.isClient && !hasLayoutChanged && cmpDef.layout) {
64 | return LAZO.ctl.addChild('lazo-layout-body', cmpDef.name, {
65 | params: ctx.params,
66 | action: cmpDef.action,
67 | success: function (ctl) {
68 | options.success(ctl, ctx, cmpDef, options);
69 | },
70 | error: onError
71 | });
72 | }
73 |
74 | this.getAssets(cmpDef.name, ctx, {
75 | success: function (assets) {
76 | ctx.assets = assets;
77 | loader(cmpToLoad, {
78 | ctx: ctx,
79 | action: hasLayoutChanged && cmpDef.layout ? 'index' : cmpDef.action,
80 | error: onError,
81 | success: function (ctl) {
82 | if (cmpDef.layout) {
83 | ctl.addChild('lazo-layout-body', cmpDef.name, {
84 | params: ctx.params,
85 | action: cmpDef.action,
86 | success: function () {
87 | options.success(ctl, ctx, cmpDef, options);
88 | },
89 | error: onError
90 | });
91 | } else {
92 | options.success(ctl, ctx, cmpDef, options);
93 | }
94 | }
95 | });
96 | },
97 | error: onError
98 | });
99 | }
100 |
101 | }, assetsMixin);
102 |
103 | });
--------------------------------------------------------------------------------
/lib/common/utils/ctlSerializor.js:
--------------------------------------------------------------------------------
1 | define(['underscore', 'lazoModel'], function (_, Model) {
2 |
3 | 'use strict';
4 |
5 | return {
6 |
7 | serialize: function (ctl, rootCtx) {
8 | var serObj = {},
9 | viewRef,
10 | omit = rootCtx ? ['cookies', '_request', '_reply', 'models', 'collections', 'location', 'userAgent', 'headers', 'response'] :
11 | ['cookies', '_request', '_reply', 'models', 'collections', '_rootCtx', 'location', 'userAgent', 'headers', 'response'];
12 |
13 | serObj.cid = ctl.cid;
14 | serObj.ctx = _.omit(ctl.ctx, omit);
15 | _.each(serObj.ctx.params, function (param, k) {
16 | delete serObj.ctx.params[k];
17 | // encode to prevent xss of serialized context
18 | serObj.ctx.params[encodeURIComponent(k)] = encodeURIComponent(param);
19 | });
20 |
21 | serObj.ctx.models = {};
22 | serObj.ctx.collections = {};
23 | serObj.isBase = ctl.isBase;
24 | serObj.name = ctl.name;
25 | if (ctl.currentView && ctl.currentView) {
26 | viewRef = ctl.currentView.ref;
27 |
28 | serObj.currentView = {
29 | cid: ctl.currentView.cid,
30 | name: ctl.currentView.name,
31 | ref: ctl.currentView.ref,
32 | templatePath: ctl.currentView.templatePath,
33 | compiledTemplatePath: ctl.currentView.compiledTemplatePath,
34 | basePath: ctl.currentView.basePath,
35 | isBase: ctl.currentView.isBase,
36 | hasTemplate: ctl.currentView.hasTemplate
37 | };
38 | }
39 |
40 | //children
41 | var serializedKids = {};
42 | _.each(ctl.children, function (regionChildren, region) {
43 | var comps = [];
44 | _.each(regionChildren, function (child) {
45 | comps.push(child.toJSON());
46 | });
47 | serializedKids[region] = comps;
48 | });
49 | serObj.children = serializedKids;
50 |
51 | //models
52 | Model._serialize(ctl.ctx.models, serObj.ctx.models);
53 | Model._serialize(ctl.ctx.collections, serObj.ctx.collections);
54 |
55 | _.each(ctl.ctx.collections, function (value, key, list) {
56 | serObj.ctx.collections[key] = value._getGlobalId();
57 | });
58 |
59 | return serObj;
60 | },
61 |
62 | deserialize: function (ctl, options) {
63 |
64 | }
65 |
66 | };
67 |
68 | });
--------------------------------------------------------------------------------
/lib/common/utils/handlebarsEngine.js:
--------------------------------------------------------------------------------
1 | define(['handlebars'], function (Handlebars) {
2 |
3 | return {
4 | compile: function(template) {
5 | return Handlebars.default.compile(template);
6 | },
7 | precompile: function (template) {
8 | return Handlebars.default.precompile(template);
9 | },
10 | execute: function(template, context, templateName) {
11 | return template(context);
12 | },
13 | engine: Handlebars.default
14 | };
15 | });
16 |
--------------------------------------------------------------------------------
/lib/common/utils/loader.js:
--------------------------------------------------------------------------------
1 | define(['underscore'], function (_) {
2 |
3 | 'use strict';
4 |
5 | return function (cmpName, options) {
6 | if (typeof cmpName !== 'string') { // TODO: better error handling??? just prints the stack to the screen
7 | return onError(new Error('The parameter "cmpName" must be a string'));
8 | }
9 |
10 | function onError(error) {
11 | _.delay(options.error, 0, error);
12 | }
13 |
14 | function onCtlLoad(controller) {
15 | try {
16 | controller._execute(options.action, {
17 | success: function (controller) {
18 | _.delay(options.success, 0, controller);
19 | },
20 | error: function (err) {
21 | return onError(err);
22 | }
23 | });
24 | } catch (error) {
25 | return onError(error);
26 | }
27 | }
28 |
29 | options = _.defaults(options || {}, {
30 | action: 'index',
31 | name: cmpName,
32 | error: function () {
33 | return;
34 | },
35 | success: function () {
36 | return;
37 | }});
38 |
39 | // loader is used by ctl.addChild; requiring at run time prevents the circular dependency
40 | LAZO.require(['lazoCtl'], function (Ctl) {
41 | Ctl.create(cmpName, _.pick(options, 'ctx', 'name'), {
42 | success: onCtlLoad,
43 | error: onError
44 | });
45 | });
46 | };
47 |
48 | });
--------------------------------------------------------------------------------
/lib/common/utils/modelLoader.js:
--------------------------------------------------------------------------------
1 | define(['lazoModel', 'lazoCollection', 'resolver/main'], function (LazoModel, LazoCollection, resolver) {
2 | 'use strict';
3 |
4 | var modelLoader = function (name, type, cb) {
5 | resolver.isBase(('models/' + name + '/' + type), 'model', function (isBase) {
6 | if (isBase) {
7 | cb(type === 'model' ? LazoModel : LazoCollection, true);
8 | } else {
9 | LAZO.require(['models/' + name + '/' + type],
10 | function (model) {
11 | // https://github.com/jrburke/requirejs/issues/922
12 | if (typeof model === 'function') {
13 | cb(model);
14 | }
15 | }
16 | );
17 | }
18 | });
19 | };
20 |
21 | return modelLoader;
22 | });
--------------------------------------------------------------------------------
/lib/common/utils/module.js:
--------------------------------------------------------------------------------
1 | define(['resolver/file', 'underscore', 'text', 'context', 'utils/loader', 'async', 'utils/assetsMixin'],
2 | function (file, _, text, Context, loader, async, assetsMixin) {
3 |
4 | 'use strict';
5 |
6 | return _.extend({
7 |
8 | addPath: function (path, ctx) {
9 | var modules;
10 | if (LAZO.app.isServer) {
11 | modules = ctx._rootCtx.modules = ctx._rootCtx.modules || [];
12 | modules.push(path);
13 | ctx.js = ctx.js || [];
14 | ctx.js.push(path);
15 | }
16 |
17 | return this;
18 | },
19 |
20 | getView: function (path, callback) {
21 | LAZO.require([path], function (View) {
22 | return callback(null, View);
23 | }, function (err) { // TODO: error handling
24 | return callback(new Error('module.getView failed for ' + path + ' : ' + err.message), null);
25 | });
26 | },
27 |
28 | getTemplate: function (templatePath, callback) {
29 | LAZO.require(['text!' + templatePath], function (template) {
30 | return callback(null, template);
31 | }, function (err) { // TODO: error handling
32 | return callback(new Error('Controller _getTemplate failed for ' + templatePath + ' : ' + err.message), null);
33 | });
34 | },
35 |
36 | addChild: function (container, cmpName, parent, options) {
37 | var childOptions,
38 | childContext,
39 | defaults = {
40 | action: 'index',
41 | render: false
42 | };
43 |
44 | function onError(error) {
45 | _.delay(options.error, 0, error);
46 | }
47 |
48 | function onComponentLoad(child) {
49 | if (!parent.children) {
50 | parent.children = {};
51 | }
52 |
53 | if (!parent.children[container]) {
54 | parent.children[container] = [];
55 | }
56 |
57 | if (_.isNumber(options.index)) {
58 | parent.children[container].splice(options.index, 0, child);
59 | } else {
60 | parent.children[container].push(child);
61 | }
62 |
63 | _.delay(options.success, 0, child);
64 | }
65 |
66 | options = _.defaults(options || {}, {
67 | error: function () {
68 | return;
69 | },
70 | success: function () {
71 | return;
72 | }
73 | });
74 |
75 | if (typeof container !== 'string' || !container) {
76 | return onError(new TypeError()); // TODO: error handling
77 | }
78 |
79 | if (typeof cmpName !== 'string' || !cmpName) {
80 | return onError(new TypeError()); // TODO: error handling
81 | }
82 |
83 | childOptions = _.defaults(_.pick(options, 'action', 'rootComponent'), defaults);
84 | childContext = new Context(_.extend({}, _.pick(parent.ctx, ['_rootCtx', '_request', '_reply', 'headers', 'meta', 'response']), {
85 | params : options.params }));
86 |
87 | if (parent.ctx && parent.ctx._rootCtx) {
88 | childContext._rootCtx = parent.ctx._rootCtx;
89 | }
90 |
91 | this.getAssets(cmpName, childContext, {
92 | success: function (assets) {
93 | childContext.assets = assets;
94 | childOptions.ctx = childContext;
95 | loader(cmpName, _.extend({}, childOptions, {
96 | success: onComponentLoad, error: onError
97 | }));
98 | },
99 | error: onError
100 | });
101 | }
102 |
103 | }, assetsMixin);
104 |
105 | });
--------------------------------------------------------------------------------
/lib/common/utils/prune.js:
--------------------------------------------------------------------------------
1 | define(['lazoModel'], function (LazoModel) {
2 |
3 | 'use strict';
4 |
5 | function getRefs(ctl, refs) {
6 | var ctx = ctl.ctx;
7 | var models = ctx.models;
8 | var collections = ctx.collections;
9 | var k;
10 | var gid;
11 | var i = 0;
12 | var child;
13 |
14 | for (k in models) {
15 | if (models[k] && models[k].name) {
16 | gid = LazoModel._getGlobalId(models[k].name, models[k].params);
17 | refs[gid] = true;
18 | }
19 | }
20 |
21 | for (k in collections) {
22 | if (collections[k] && collections[k].name) {
23 | gid = LazoModel._getGlobalId(collections[k].name, collections[k].params);
24 | refs[gid] = true;
25 | }
26 | }
27 |
28 | if (ctl.children) {
29 | for (k in ctl.children) {
30 | child = ctl.children[k];
31 | for (i = 0; i < child.length; i++) {
32 | getRefs(child[i], refs);
33 | }
34 | }
35 | }
36 |
37 | return refs;
38 | }
39 |
40 | return function (ctl) {
41 | var refs = getRefs(ctl, {});
42 | var modelInstances = ctl.ctx._rootCtx.modelInstances;
43 | var modelList = ctl.ctx._rootCtx.modelList;
44 | var k;
45 |
46 | for (k in modelInstances) {
47 | if (!refs[k]) {
48 | delete modelInstances[k];
49 | }
50 | }
51 | for (k in modelList) {
52 | if (!refs[k]) {
53 | delete modelList[k];
54 | }
55 | }
56 |
57 | };
58 |
59 | });
--------------------------------------------------------------------------------
/lib/common/utils/sanitizer.js:
--------------------------------------------------------------------------------
1 | define([], function () {
2 |
3 | 'use strict';
4 |
5 | return {
6 |
7 | /**
8 | * Encodes an entity for safe display in html element text or attributes
9 | */
10 | encode: function (value) {
11 |
12 | // TODO: This should be expanded to include other scenarios.
13 | // See: https://github.com/angular/angular.js/blob/v1.3.14/src/ngSanitize/sanitize.js#L435
14 |
15 | return String(value)
16 | .replace(/&/g, '&')
17 | .replace(/"/g, '"')
18 | .replace(/'/g, ''')
19 | .replace(//g, '>')
21 | .replace(/`/g, '`');
22 | }
23 |
24 | };
25 |
26 | });
27 |
--------------------------------------------------------------------------------
/lib/common/utils/template.js:
--------------------------------------------------------------------------------
1 | define(['utils/handlebarsEngine'], function (handlebarsEngine) {
2 |
3 | 'use strict';
4 |
5 | var defaultExt = {
6 |
7 | handlebars: 'hbs',
8 | micro: 'mt'
9 |
10 | };
11 |
12 | var defaultTemplateEngineName = 'handlebars',
13 | _engines = {
14 | handlebars: {
15 | extension: 'hbs',
16 | handler: handlebarsEngine,
17 | exp: 'Handlebars'
18 | }
19 | };
20 |
21 | function resolveEnginePath(engineDef) {
22 | if (engineDef.path) {
23 | return engineDef.path;
24 | }
25 |
26 | switch (engineDef.name) {
27 | case 'micro':
28 | return 'underscore';
29 | }
30 | }
31 |
32 | return {
33 |
34 | getDefaultExt: function (engineName) {
35 | return this.getTemplateExt() || defaultExt[engineName];
36 | },
37 |
38 | setTemplateExt: function (engineName, ext) {
39 | var engineDef = this.getTemplateEngineDef(engineName);
40 | if (engineDef) {
41 | engineDef.extension = ext;
42 | }
43 | },
44 |
45 | registerTemplateEngine: function (name, extension, handler, path, exp) {
46 | return (_engines[name] = { extension: extension, handler: handler, path: path, exp: exp });
47 | },
48 |
49 | getTemplateEngine: function (engineName) {
50 | var engine = _engines[engineName];
51 | return engine ? engine.handler : undefined;
52 | },
53 |
54 | getTemplateExt: function (engineName) {
55 | var engineDef = this.getTemplateEngineDef(engineName);
56 | return engineDef ? engineDef.extension : undefined;
57 | },
58 |
59 | getTemplateEngineDef: function (engineName) {
60 | return _engines[engineName];
61 | },
62 |
63 | getDefaultTemplateEngine: function () {;
64 | return this.getTemplateEngine(defaultTemplateEngineName);
65 | },
66 |
67 | getDefaultTemplateEngineName: function () {
68 | return defaultTemplateEngineName;
69 | },
70 |
71 | setDefaultTemplateEngine: function (engineName) {
72 | var engine = _engines[engineName];
73 | if (!engine) {
74 | throw new Error('Invalid template engine name, ' + engineName);
75 | }
76 |
77 | defaultTemplateEngineName = engineName;
78 | },
79 |
80 | loadTemplateEngine: function (engineDef, options) {
81 | var engine,
82 | self = this;
83 |
84 | options.error = options.error || function (err) { throw err; };
85 |
86 | if ((engine = this.getTemplateEngine(engineDef.name))) {
87 | options.success(engine);
88 | } else {
89 | LAZO.require([resolveEnginePath(engineDef)], function (engine) {
90 | self.registerTemplateEngine(engineDef.name, engineDef.extension, engineDef.handler(engine), engineDef.exp);
91 | options.success(self.getTemplateEngine(engineDef.name));
92 | }, function (err) {
93 | options.error(err);
94 | });
95 | }
96 | },
97 |
98 | engHandlerMaker: function (engineName) {
99 | switch (engineName) {
100 | case 'micro':
101 | return function (engine) {
102 | return {
103 | compile: function (template) {
104 | return _.template(template);
105 | },
106 | execute: function (compiledTemplate, data) {
107 | return compiledTemplate(data);
108 | },
109 | engine: _.template
110 | };
111 | };
112 | }
113 | }
114 |
115 | };
116 |
117 | });
--------------------------------------------------------------------------------
/lib/common/utils/treeMixin.js:
--------------------------------------------------------------------------------
1 | define(['underscore'], function (_, $) {
2 |
3 | 'use strict';
4 |
5 | return {
6 |
7 | getList: function (nodeType, rootNode) {
8 | var self = this;
9 |
10 | return (function flatten(nodeType, node, nodes) {
11 | var children;
12 |
13 | if (nodeType === 'view' && node.currentView) {
14 | nodes.push(node.currentView);
15 | } else if (nodeType === 'component') {
16 | nodes.push(node);
17 | }
18 |
19 | if ((children = self.getNodeChildren(node)).length) {
20 | _.each(children, function (child) {
21 | nodes = flatten(nodeType, child, nodes);
22 | });
23 | }
24 |
25 | return nodes;
26 | })(nodeType, rootNode, []);
27 | },
28 |
29 | getNodeType: function (node) {
30 | var ret;
31 |
32 | if (!node) {
33 | ret = null;
34 | } else if (node.currentView) {
35 | ret = 'component';
36 | } else if (node.setElement) {
37 | ret = 'view';
38 | }
39 |
40 | return ret;
41 | },
42 |
43 | getNodeChildren: function (node) {
44 | var nodeType = this.getNodeType(node);
45 | if (nodeType !== 'component') {
46 | return [];
47 | }
48 |
49 | var children = node.children;
50 | var child;
51 | var nodes = [];
52 | node.currentView.parent = node;
53 | // nodes.push(node.currentView);
54 | for (var k in children) {
55 | for (var i = 0; i < children[k].length; i++) {
56 | child = children[k][i];
57 | child.parent = node;
58 | nodes.push(child);
59 | }
60 | }
61 |
62 | return nodes;
63 | },
64 |
65 | _attrs: {
66 | cmpName: 'lazo-cmp-name',
67 | cmpId: 'lazo-cmp-id',
68 | compContainerName: 'lazo-cmp-container',
69 | viewId: 'lazo-view-id'
70 | }
71 |
72 | };
73 |
74 | });
--------------------------------------------------------------------------------
/lib/common/utils/uniqueId.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |
3 | 'use strict';
4 |
5 | var idCounter = LAZO && LAZO.initConf ? LAZO.initConf.rootCtx.idCounter : 0;
6 |
7 | return function (prefix) {
8 | var id = ++idCounter + '';
9 | return prefix ? prefix + id : id;
10 | };
11 |
12 | });
--------------------------------------------------------------------------------
/lib/public/assets.js:
--------------------------------------------------------------------------------
1 | define(['assetsProvider', 'base'], function (assetsProvider, Base) {
2 |
3 | 'use strict';
4 |
5 | return Base.extend(assetsProvider);
6 |
7 | });
--------------------------------------------------------------------------------
/lib/public/bundle.js:
--------------------------------------------------------------------------------
1 | define(['underscore', 'base', 'resolver/component', 'jquery'], function (_, Base, cmpResolver, $) {
2 |
3 | 'use strict';
4 |
5 | var supportsImports = (function () {
6 | return LAZO.isClient && 'import' in document.createElement('link');
7 | })();
8 |
9 | return Base.extend({
10 |
11 | response: function (route, uri, options) {
12 | options.success(null);
13 | },
14 |
15 | getLibPath: function () {
16 | return LAZO.conf.libPath;
17 | },
18 |
19 | resolveBundleUrl: function (bundle) {
20 | return bundle;
21 | },
22 |
23 | getComponentDef: function (route) {
24 | return cmpResolver.getDef(route);
25 | },
26 |
27 | getComponentCss: function (name) {
28 | return cmpResolver.getLinks(name, 'css');
29 | },
30 |
31 | // TODO: deprecate next major
32 | getComponentCSS: function (name) {
33 | return cmpResolver.getLinks(name, 'css');
34 | },
35 |
36 | getComponentImports: function (name) {
37 | return cmpResolver.getLinks(name, 'import');
38 | },
39 |
40 | sortCss: function (links) {
41 | return links;
42 | },
43 |
44 | sortImports: function (links) {
45 | return links;
46 | },
47 |
48 | resolveImport: function (relativePath, namespace) {
49 | if (LAZO.app.isServer) {
50 | LAZO.logger.warn('bundle.resolveImport', 'Cannot call on server.');
51 | return null;
52 | }
53 |
54 | var path = namespace === 'application' ? relativePath :
55 | 'components/' + namespace + '/imports/' + relativePath;
56 | var $import = $('link[href*="' + path + '"][lazo-link-ctx="' + namespace + '"]');
57 | var linkNode = $import[0];
58 |
59 | return linkNode ? (supportsImports ? linkNode.import : linkNode) : null;
60 | },
61 |
62 | _createLink: function (link, defaults) {
63 | if (_.isString(link)) {
64 | return _.extend({ href: link }, defaults);
65 | } else {
66 | return _.extend(defaults, link);
67 | }
68 | },
69 |
70 | _createCSSLink: function (link) {
71 | return this._createLink(link, {
72 | rel: 'stylesheet',
73 | type: 'text/css',
74 | 'lazo-link': 'css',
75 | 'lazo-link-ctx': 'application'
76 | });
77 | },
78 |
79 | _createCSSLinks: function (links) {
80 | return _.map(links, _.bind(this._createCSSLink, this));
81 | },
82 |
83 | _createImportLink: function (link) {
84 | return this._createLink(link, {
85 | rel: 'import',
86 | 'lazo-link': 'import',
87 | 'lazo-link-ctx': 'application'
88 | });
89 | },
90 |
91 | _createImportLinks: function (links) {
92 | return _.map(links, _.bind(this._createImportLink, this));
93 | }
94 |
95 | });
96 |
97 | });
--------------------------------------------------------------------------------
/lib/public/collection.js:
--------------------------------------------------------------------------------
1 | define(['underscore', 'backbone', 'proxy', 'lazoModel'], function (_, Backbone, Proxy, LazoModel) {
2 | 'use strict';
3 |
4 | /**
5 | * A base class for collections
6 | *
7 | * @class LazoCollection
8 | * @constructor
9 | * @param {Array Models} models An array of models
10 | * @param {Object} options
11 | * @param {String} options.name The name of the collection in the repo
12 | * @param {Object} options.params A hash of name-value pairs used in url substitution
13 | * @param {Context} options.ctx The current context for the request
14 | * @param {String} [options.modelName] Specify the LazoModel class that the collection contains. This
15 | * should be the name of the model in the repo. This *MUST* be used with the Backbone.Collection model property.
16 | * @extends Backbone.Collection
17 | */
18 | var Collection = Backbone.Collection.extend({
19 |
20 | constructor: function(attributes, options) {
21 | this._initialize(attributes, options);
22 | this.cid = _.uniqueId('c');
23 | if (options.modelName) {
24 | this.modelName = options.modelName;
25 | }
26 | Backbone.Collection.apply(this, arguments);
27 | },
28 |
29 | sync: function(method, model, options) {
30 | var self = this,
31 | success = options.success;
32 |
33 | options.success = function (resp) {
34 | self._resp = resp;
35 |
36 | if (success) {
37 | success(resp);
38 | }
39 | };
40 |
41 | Proxy.prototype.sync.call(this, method, options);
42 | },
43 |
44 | call: function(fname, args, options) {
45 | Proxy.prototype.callSyncher.call(this, fname, args, options);
46 | },
47 |
48 | _initialize: function(attributes, options) {
49 | LazoModel.prototype._initialize.apply(this, arguments);
50 | },
51 |
52 | _getGlobalId: function() {
53 | return LazoModel.prototype._getGlobalId.apply(this);
54 | },
55 |
56 | // taken directly from Backbone.Collection._prepareModel, except where noted by comments
57 | _prepareModel: function(attrs, options) {
58 |
59 | // begin lazo specific overrides
60 | var modelName = null;
61 | if (this.modelName) {
62 | if (_.isFunction(this.modelName)) {
63 | modelName = this.modelName(attrs, options);
64 | }
65 | else {
66 | modelName = this.modelName;
67 | }
68 | }
69 | // end lazo specific overrides
70 |
71 | if (attrs instanceof Backbone.Model) {
72 | if (!attrs.collection) attrs.collection = this;
73 | if (!attrs.name && modelName) attrs.name = modelName; // lazo specific
74 | return attrs;
75 | }
76 | options || (options = {});
77 | options.collection = this;
78 |
79 | // begin lazo specific overrides
80 | if (modelName) {
81 | options.name = modelName;
82 | }
83 |
84 | options.ctx = this.ctx;
85 | // end lazo specific overrides
86 |
87 | var model = new this.model(attrs, options);
88 | if (!model._validate(attrs, options)) {
89 | this.trigger('invalid', this, attrs, options);
90 | return false;
91 | }
92 | return model;
93 | }
94 |
95 | });
96 |
97 | return Collection;
98 | });
99 |
--------------------------------------------------------------------------------
/lib/public/server/server.js:
--------------------------------------------------------------------------------
1 | define(['base'], function (Base) {
2 |
3 | 'use strict';
4 |
5 | return Base.extend({
6 |
7 | setup: function (hapi, pack, servers, options) {
8 | options.success();
9 | },
10 |
11 | afterInitialize: function (hapi, pack, servers, options) {
12 | options.success();
13 | }
14 |
15 | });
16 |
17 | });
--------------------------------------------------------------------------------
/lib/public/views/base.js:
--------------------------------------------------------------------------------
1 | define(['flexo', 'lazoViewMixin', 'underscore', 'lazoWidgetMixin'], function (flexo, lazoViewMixin, _, lazoWidgetMixin) {
2 |
3 | 'use strict';
4 |
5 | return flexo.View.extend(_.extend({
6 |
7 | getInnerHtml: function (options) {
8 | var self = this;
9 | flexo.View.prototype.getInnerHtml.call(this, {
10 | success: function (html) {
11 | self.getWidgetsHtml(html, {
12 | success: options.success,
13 | error: options.error
14 | });
15 | },
16 | error: options.error
17 | });
18 | },
19 |
20 | attach: function (el, options) {
21 | flexo.View.prototype.attach.call(this, el, options);
22 | this.$el.removeClass(this._getStateClass('detached')).addClass(this._getStateClass('attached'));
23 | this._uiStates = this._getStates();
24 | },
25 |
26 | _onRemove: function () {
27 | var self = this;
28 |
29 | this._removeWidgets();
30 | this._removeChildViews();
31 | },
32 |
33 | _removeChildViews: function () {
34 | if (!this.children) {
35 | return;
36 | }
37 |
38 | for (var k in this.children) {
39 | this.children[k].remove();
40 | }
41 | }
42 |
43 | }, lazoViewMixin, lazoWidgetMixin));
44 |
45 | });
--------------------------------------------------------------------------------
/lib/public/views/state.js:
--------------------------------------------------------------------------------
1 | define(['jquery', 'underscore'], function ($, _) {
2 |
3 | var prefix = 'lazo';
4 | var states = {
5 | focus: true,
6 | disabled: true,
7 | visible: true,
8 | hidden: true
9 | };
10 |
11 | return {
12 |
13 | stateClassPrefix: 'lazo',
14 |
15 | _getStateClass: function (state) {
16 | return this.stateClassPrefix + '-' + state;
17 | },
18 |
19 | _getValidStates: function () {
20 | return states;
21 | },
22 |
23 | _getStates: function () {
24 | if (LAZO.app.isServer) {
25 | LAZO.logger.warn('[getStates] Client only method');
26 | }
27 |
28 | var self = this;
29 | var retVal = {};
30 | var state;
31 | var classNames = this.el ? this.el.className.split(' ') : [];
32 | var validStates = this._getValidStates();
33 | var states = _.filter(classNames, function (className) {
34 | var classNameParts = className.split('-');
35 | return classNameParts[0] === self.stateClassPrefix && validStates[classNameParts[1]] &&
36 | classNameParts.length === 2;
37 | });
38 |
39 | for (var i = 0; i < states.length; i++) {
40 | state = states[i].split('-')[1];
41 | retVal[state] = state;
42 | }
43 |
44 | return retVal;
45 | },
46 |
47 | setState: function (state, on) {
48 | if (!states[state]) {
49 | LAZO.logger.warn('[setState] Invalid state, ' + state);
50 | return;
51 | }
52 |
53 | if (LAZO.app.isClient) {
54 | $(this.el)[on ? 'addClass' : 'removeClass'](this._getStateClass(state));
55 | }
56 |
57 | this._uiStates = this._uiStates || {};
58 | if (on) {
59 | this._uiStates[state] = state;
60 | } else {
61 | delete this._uiStates[state];
62 | }
63 | }
64 |
65 | };
66 |
67 | });
--------------------------------------------------------------------------------
/lib/server/backbone.js:
--------------------------------------------------------------------------------
1 | define(['backboneServer'], function (Backbone) {
2 |
3 | function noop() {
4 | return this;
5 | }
6 |
7 | // ignore client events on server
8 | Backbone.Events.on = noop;
9 | Backbone.Events.off = noop;
10 | Backbone.Events.trigger = noop;
11 | Backbone.Events.once = noop;
12 | Backbone.Events.listenTo = noop;
13 | Backbone.Events.stopListening = noop;
14 | Backbone.Events.listenToOnce = noop;
15 |
16 | Backbone.Model.prototype.on = noop;
17 | Backbone.Model.prototype.off = noop;
18 | Backbone.Model.prototype.trigger = noop;
19 | Backbone.Model.prototype.once = noop;
20 | Backbone.Model.prototype.listenTo = noop;
21 | Backbone.Model.prototype.stopListening = noop;
22 | Backbone.Model.prototype.listenToOnce = noop;
23 |
24 | Backbone.Collection.prototype.on = noop;
25 | Backbone.Collection.prototype.off = noop;
26 | Backbone.Collection.prototype.trigger = noop;
27 | Backbone.Collection.prototype.once = noop;
28 | Backbone.Collection.prototype.listenTo = noop;
29 | Backbone.Collection.prototype.stopListening = noop;
30 | Backbone.Collection.prototype.listenToOnce = noop;
31 |
32 | Backbone.View.prototype.on = noop;
33 | Backbone.View.prototype.off = noop;
34 | Backbone.View.prototype.trigger = noop;
35 | Backbone.View.prototype.once = noop;
36 | Backbone.View.prototype.listenTo = noop;
37 | Backbone.View.prototype.stopListening = noop;
38 | Backbone.View.prototype.listenToOnce = noop;
39 |
40 | // Do not use Backbone history on server
41 | Backbone.history.start = noop;
42 |
43 | // Disable view events on server
44 | Backbone.View.prototype._ensureElement = noop;
45 | Backbone.View.prototype.delegateEvents = noop;
46 | Backbone.View.prototype.undelegateEvents = noop;
47 |
48 | return Backbone;
49 |
50 | });
51 |
--------------------------------------------------------------------------------
/lib/server/cls-facade.js:
--------------------------------------------------------------------------------
1 | define(['underscore'], function (_) {
2 |
3 | 'use strict';
4 |
5 | var namespaceObj = {
6 | store: {},
7 | get: function (key) {
8 | return this.store[key];
9 | },
10 | set: function (key, val) {
11 | return this.store[key] = val;
12 | },
13 | run: function (fn) {
14 | return fn.call(this);
15 | }
16 | };
17 |
18 | return {
19 | createNamespace: function (name) {
20 | var domain = require('domain');
21 | var active = domain.active;
22 | var namespace = _.extend({}, namespaceObj);
23 |
24 | if (active) {
25 | if (active[name]) {
26 | return active[name];
27 | }
28 |
29 | active[name] = namespace;
30 | }
31 |
32 | return namespace;
33 | },
34 |
35 | getNamespace: function (name) {
36 | var domain = require('domain');
37 | var active = domain.active;
38 |
39 | return (active && active[name]) || _.extend({}, namespaceObj);
40 | }
41 | };
42 | });
--------------------------------------------------------------------------------
/lib/server/forbidden.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |
3 | 'use strict';
4 |
5 | var regexes = [/\/server(\/)?/, /\/node_modules(\/)?/];
6 |
7 | return function (path) {
8 | var i = 0;
9 | var len = regexes.length;
10 |
11 | for (i; i < len; i++) {
12 | if (path.match(regexes[i])) {
13 | return true;
14 | }
15 | }
16 |
17 | return false;
18 | };
19 |
20 | });
--------------------------------------------------------------------------------
/lib/server/handlers/app/html.js:
--------------------------------------------------------------------------------
1 | define(['text!pageTemplate', 'handlebars'], function (pageTemplate, Handlebars) {
2 |
3 | var compiledPage = Handlebars.default.compile(pageTemplate);
4 |
5 | return function (options) {
6 | return compiledPage(options);
7 | };
8 |
9 | });
--------------------------------------------------------------------------------
/lib/server/handlers/app/main.js:
--------------------------------------------------------------------------------
1 | define(['handlers/utils', 'handlers/app/processor', 'underscore', 'httpResponse'], function (utils, processor, _, httpResponse) {
2 |
3 | return function (request, reply, route, svr) {
4 | var payload = request.payload;
5 | var options = utils.createCtxOptions(request, reply);
6 |
7 | LAZO.logger.debug('[server.handlers.app.main.processor.reply] Processing route request...', route, options.url.href);
8 |
9 | processor.reply(route, options, {
10 | error: function (err) {
11 | err = err instanceof Error ? err : new Error(err);
12 | LAZO.logger.warn('[server.handlers.app.main.processor.reply] Error processing request...', route, options.url.href, err);
13 | throw err; // hapi domain catches error
14 | },
15 | success: function (html, httpResponseData) {
16 | var response = reply(html);
17 | httpResponse.applyHttpResponseData(response, httpResponseData);
18 | }
19 | });
20 | };
21 |
22 | });
--------------------------------------------------------------------------------
/lib/server/handlers/assets.js:
--------------------------------------------------------------------------------
1 | define(['assets', 'context', 'handlers/utils'], function (LazoAssets, Context, utils) {
2 |
3 | var assets = new LazoAssets();
4 |
5 | return function (request, reply) {
6 | var ctx = new Context(utils.createCtxOptions(request, reply));
7 |
8 | function onError(err) {
9 | LAZO.logger.warn('[assets.get] an error occurred while querying the assets end point.', err);
10 | reply({});
11 | }
12 |
13 | function onSuccess(assets) {
14 | reply(assets);
15 | }
16 |
17 | if (!request.query) {
18 | return onError(new Error('Query parameters not defined.'));
19 | }
20 |
21 | assets.get(request.query.components.split(','), ctx, {
22 | success: onSuccess,
23 | error: onError
24 | });
25 | };
26 |
27 | });
--------------------------------------------------------------------------------
/lib/server/handlers/safe.js:
--------------------------------------------------------------------------------
1 | define(['continuation-local-storage', 'cluster', 'domain', 'hapi'], function (cls, cluster, domain, hapi) {
2 |
3 | var onServerStop = function () {
4 | LAZO.logger.error('[server.handlers.safe] Server stopped. Killing process...');
5 | process.exit(1);
6 | };
7 |
8 | return function (server, handler, context) {
9 | var lazoNs = cls.getNamespace('lazojs') || cls.createNamespace('lazojs');
10 |
11 | if (!server || server.constructor !== hapi.Server) {
12 | throw new TypeError();
13 | }
14 |
15 | if (typeof handler !== 'function') {
16 | throw new TypeError();
17 | }
18 |
19 | return function (request, reply) {
20 | var handlerArgs = arguments;
21 | var handlerDomain = domain.create();
22 | var uncaughtCount = 0;
23 |
24 | var onError = function (error) {
25 | lazoNs.run(function () {
26 | lazoNs.set('request', request);
27 |
28 | LAZO.logger.warn('[server.handlers.safe] Unhandled error: %s %j', error.message, error.stack);
29 |
30 | switch (uncaughtCount++) {
31 | case 0:
32 | reply(error);
33 | break;
34 | case 1:
35 | if (cluster.isWorker) {
36 | cluster.worker.disconnect();
37 | }
38 | LAZO.logger.error('[server.handlers.safe] Stopping server...');
39 | server.stop(onServerStop);
40 | break;
41 | default:
42 | // Ignore
43 | break;
44 | }
45 | });
46 | };
47 |
48 | handlerDomain.on('error', onError);
49 |
50 | handlerDomain.run(function () {
51 | handler.apply(context || this, handlerArgs);
52 | });
53 | };
54 | };
55 |
56 | });
57 |
--------------------------------------------------------------------------------
/lib/server/handlers/stream.js:
--------------------------------------------------------------------------------
1 | define(['hapi'], function (Hapi) {
2 |
3 | 'use strict';
4 |
5 | return function (req, reply) {
6 | var payload = req.payload;
7 | var componentName = req.params.compName;
8 | var callback = payload ? payload.callback : null;
9 | var fn_name = req.params.action || 'upload';
10 | var _path = (componentName ? 'components/' + componentName : 'app') + '/server/utilActions';
11 | LAZO.require([_path], function (util) {
12 | var fn = util[fn_name];
13 | if (fn) {
14 | fn(req, {
15 | success: function (ret) {
16 | var resp = [];
17 | if (callback) {
18 | resp.push("");
26 | }
27 | reply(resp.join(''));
28 | },
29 | error: function (ret, options) {
30 | var error = Hapi.error.internal(ret);
31 |
32 | if (options && options.statusCode) {
33 | error.response.code = options.statusCode;
34 | }
35 |
36 | reply(error);
37 | },
38 | done: function () {
39 | LAZO.logger.debug('[server.handlers.stream] Handled by application...', _path);
40 | }
41 | });
42 | }
43 |
44 | }, function (err) {
45 | LAZO.logger.error('[server.handlers.stream] Error loading "%s" module.', _path, err);
46 | reply(err);
47 | });
48 | };
49 |
50 | });
--------------------------------------------------------------------------------
/lib/server/handlers/utils.js:
--------------------------------------------------------------------------------
1 | define(['underscore'], function (_) {
2 |
3 | 'use strict';
4 |
5 | return {
6 |
7 | getParams: function (request) {
8 | var params = {},
9 | reqParams,
10 | qryParams,
11 | key;
12 |
13 | if ((reqParams = _.extend(request.params, request.payload ? request.payload : {}))) {
14 | for (key in reqParams) {
15 | params[key] = reqParams[key];
16 | }
17 | }
18 | if ((qryParams = request.query)) {
19 | for (key in qryParams) {
20 | params[key] = qryParams[key];
21 | }
22 | }
23 |
24 | return params;
25 | },
26 |
27 | getCookies: function (request) {
28 | var cookies = {},
29 | state;
30 | if (!(state = request.state)) {
31 | return cookies;
32 | }
33 |
34 | for (var key in state) {
35 | cookies[key] = state[key];
36 | }
37 |
38 | return cookies;
39 | },
40 |
41 | getHeaders: function (request) {
42 | return request.raw.req.headers;
43 | },
44 |
45 | getParsedUrl: function (request) {
46 | return request.url;
47 | },
48 |
49 | createCtxOptions: function (request, reply, options) {
50 | return _.extend({
51 | params: this.getParams(request),
52 | cookies: this.getCookies(request),
53 | _request: request,
54 | _reply: reply,
55 | headers: this.getHeaders(request),
56 | url: this.getParsedUrl(request),
57 | response: {
58 | statusCode: null,
59 | httpHeaders: [],
60 | varyParams: []
61 | }
62 | }, options);
63 | }
64 |
65 | };
66 |
67 | });
--------------------------------------------------------------------------------
/lib/server/httpResponse.js:
--------------------------------------------------------------------------------
1 | define(['underscore'], function (_) {
2 |
3 | 'use strict';
4 |
5 | var httpHeaders = [],
6 | varyHeader = [];
7 |
8 | return {
9 |
10 | addHttpHeader: function (name, value, options) {
11 | httpHeaders.push({ name: name, value: value, options: options || null });
12 | return this;
13 | },
14 |
15 | getHttpHeaders: function () {
16 | return httpHeaders;
17 | },
18 |
19 | addVaryParam: function (varyParam) {
20 | varyHeader.push(varyParam);
21 | return this;
22 | },
23 |
24 | getVaryParams: function () {
25 | return varyHeader;
26 | },
27 |
28 | mergeHttpResponseData: function (ctl) {
29 | return {
30 | statusCode: ctl.getHttpStatusCode() || null,
31 | httpHeaders: httpHeaders.concat(ctl.getHttpHeaders()),
32 | varyParams: _.union(varyHeader, ctl.getHttpVaryParams())
33 | };
34 | },
35 |
36 | applyHttpResponseData: function (response, httpResponseData) {
37 | if (httpResponseData.statusCode) {
38 | response.code(httpResponseData.statusCode);
39 | }
40 |
41 | _.each(httpResponseData.httpHeaders, function (header) {
42 | response.header(header.name, header.value, header.options || { override: true });
43 | });
44 |
45 | _.each(httpResponseData.varyParams, function (headerName) {
46 | response.vary(headerName);
47 | });
48 | }
49 |
50 | }
51 | });
--------------------------------------------------------------------------------
/lib/server/jquery.js:
--------------------------------------------------------------------------------
1 | define('jquery', [], function () { return function () {}; }); // noop on server
--------------------------------------------------------------------------------
/lib/server/logger/fileSink.js:
--------------------------------------------------------------------------------
1 | /*global __dirname:false*/
2 |
3 | define(['underscore', 'fs', 'moment', 'os', 'path', 'util'], function (_, fs, moment, os, path, util) {
4 |
5 | var DEFAULT_FILE_NAME = 'lazojs.log';
6 |
7 | var DEFAULT_FILE_PATH = path.join(process.cwd(), DEFAULT_FILE_NAME);
8 |
9 | var MB = 1024 * 1024;
10 |
11 | var DEFAULT_MAX_SIZE = 10 * MB;
12 |
13 | var DEFAULT = {
14 | filePath: DEFAULT_FILE_PATH,
15 | maxFiles: 10,
16 | maxSize: DEFAULT_MAX_SIZE,
17 | roll: true
18 | };
19 |
20 | var dateChanged = function (a, b) {
21 | return a === null || b === null ||
22 | a.getUTCFullYear() !== b.getUTCFullYear() ||
23 | a.getUTCMonth() !== b.getUTCMonth() ||
24 | a.getUTCDate() !== b.getUTCDate();
25 | };
26 |
27 | var getFilename = function (filePath) {
28 | return _.last(filePath.split(path.sep));
29 | };
30 |
31 | var getNextFilePath = function (filePath, maxFiles) {
32 | var dirPath = path.dirname(filePath);
33 | var files = fs.readdirSync(dirPath);
34 | var fileName = getFilename(filePath);
35 | var regExp = new RegExp('^' + fileName + '\\.(\\d+)$');
36 | var filtered = _.filter(files, function (file) {
37 | return regExp.test(file);
38 | });
39 | var last = _.max(filtered, function (fileName) {
40 | var filePath = path.join(dirPath, fileName);
41 | return fs.existsSync(filePath) ? fs.statSync(filePath).mtime : 0;
42 | });
43 | var match = last && regExp.exec(last);
44 | var pointer = match && match[1] ? parseInt(match[1]) : 0;
45 | var sequence = pointer >= 1 && pointer < maxFiles ? pointer + 1 : 1;
46 |
47 | return util.format('%s.%d', filePath, sequence);
48 | };
49 |
50 | var rollFileSync = function (filePath, maxFiles) {
51 | try {
52 | if (!fs.existsSync(filePath)) {
53 | return;
54 | }
55 |
56 | var nextFilePath = getNextFilePath(filePath, maxFiles);
57 | fs.renameSync(filePath, nextFilePath);
58 | } catch (error) {
59 | console.error(util.format('FileSink, error while rolling file "%s": %s', filePath, error.message));
60 | }
61 | };
62 |
63 | var FileSink = function (options) {
64 |
65 | var _options = _.defaults({}, options, DEFAULT);
66 |
67 | // Check if directory exists
68 |
69 | var dirPath = path.dirname(_options.filePath);
70 |
71 | if (!fs.existsSync(dirPath)) {
72 | throw new Error(util.format('FileSink, given path does not exist: %s', dirPath));
73 | }
74 |
75 | // Check if can write file
76 |
77 | fs.closeSync(fs.openSync(_options.filePath, 'a'));
78 |
79 | // Initial status
80 |
81 | var stats = fs.statSync(_options.filePath);
82 | var fileSize = stats.size;
83 | var lastModified = stats.mtime;
84 |
85 | // Sink function
86 |
87 | return function (message) {
88 | var now = new Date();
89 | var messageBuffer = new Buffer(message + os.EOL);
90 | var messageLength = messageBuffer.length;
91 |
92 | if ((fileSize + messageLength) > _options.maxSize || (_options.roll && dateChanged(lastModified, now))) {
93 | rollFileSync(_options.filePath, _options.maxFiles);
94 | fileSize = 0;
95 | }
96 |
97 | fs.appendFile(_options.filePath, messageBuffer, function (error) {
98 | if (error) {
99 | return console.error(util.format('FileSink, error while appending to "%s": %s', _options.filePath, error.message));
100 | }
101 |
102 | fileSize += messageLength;
103 | lastModified = now;
104 | });
105 | };
106 | };
107 |
108 | return FileSink;
109 |
110 | });
111 |
--------------------------------------------------------------------------------
/lib/server/logger/utils.js:
--------------------------------------------------------------------------------
1 | define(['continuation-local-storage', 'util'], function (cls, util) {
2 |
3 | var getRequest = function () {
4 | var lazoNs = cls.getNamespace('lazojs');
5 | var request = lazoNs && lazoNs.get('request');
6 | return request;
7 | };
8 |
9 | var serverStringify = function (object) {
10 | var options = {
11 | depth: 1
12 | };
13 |
14 | return util.inspect(object, options).replace(/\s+/g, ' ');
15 | };
16 |
17 | return {
18 | getRequest: getRequest,
19 | serverStringify: serverStringify
20 | };
21 |
22 | });
23 |
--------------------------------------------------------------------------------
/lib/server/proxy.js:
--------------------------------------------------------------------------------
1 | define(['base', 'underscore', 'serviceProxy', 'fs', 'path'], function (Base, _, ServiceProxy, fs, path) {
2 |
3 | var Proxy = Base.extend({
4 | sync: function (method, options) {
5 | var model = this;
6 | var validationResp = doValidate(model);
7 | if (validationResp) {
8 | if (options.error) {
9 | return options.error(validationResp);
10 | } else {
11 | throw new Error('Error back not defined');
12 | }
13 | }
14 |
15 | findSyncer(model, {
16 | success: function (Syncher) {
17 | var sync = new Syncher();
18 | sync.proxy.ctx = model.ctx;
19 |
20 | var opts = _.extend({ params: model.params || {} }, options, { model: model });
21 |
22 | LAZO.logger.debug('[server.proxy.sync] Calling CRUD syncher...', model.name);
23 |
24 | switch (method) {
25 | case 'read':
26 | sync.fetch(opts);
27 | break;
28 | case 'create':
29 | sync.add(model.attributes, opts);
30 | break;
31 | case 'update':
32 | sync.update(model.attributes, opts);
33 | break;
34 | case 'delete':
35 | sync.destroy(model.attributes, opts);
36 | break;
37 | }
38 | },
39 | error: function (err) {
40 | LAZO.logger.debug('[server.proxy.sync] Error calling CRUD syncher...', model.name, err);
41 |
42 | ServiceProxy.prototype.sync.call(this, method, model, options);
43 | }
44 | });
45 | },
46 |
47 | callSyncher: function (fname, args, options) {
48 | var model = this;
49 | var validationResp = doValidate(model);
50 | if (validationResp) {
51 | if (options.error) {
52 | return options.error(validationResp);
53 | } else {
54 | throw new Error('Error back not defined');
55 | }
56 | }
57 |
58 | findSyncer(model, {
59 | success: function (Syncher) {
60 | var sync = new Syncher();
61 | sync.proxy.ctx = model.ctx;
62 |
63 | var opts = _.extend(options, { ctx: model.ctx });
64 |
65 | LAZO.logger.debug('[server.proxy.callSyncher] Calling NON-CRUD syncher...', fname);
66 |
67 | if (typeof(sync[fname]) === 'function') {
68 | return sync[fname](args, opts);
69 | }
70 |
71 | return options.error({error: 'Method not found in syncher: ' + fname});
72 | },
73 | error: function (err) {
74 | return options.error({error: 'No syncher defined for model: ' + model.name});
75 | }
76 | });
77 | }
78 | });
79 |
80 | function doValidate(model) {
81 | if (!model.name) {
82 | return {error: 'model does not have a name'};
83 | }
84 | if (!model.ctx) {
85 | return {error: 'model does not have a context'};
86 | }
87 | return null;
88 | }
89 |
90 | function findSyncer(model, options) {
91 | var exists = fs.existsSync(path.join(LAZO.FILE_REPO_PATH, 'models', model.name, 'server', 'syncher.js'));
92 | if (!exists) {
93 | return options.error(Error("syncher.js not found for model " + model.name));
94 | }
95 | LAZO.require(['models/' + model.name + '/server/syncher'], options.success, options.error);
96 | }
97 |
98 | return Proxy;
99 | });
100 |
--------------------------------------------------------------------------------
/lib/server/resolver/file.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |
3 | 'use strict';
4 |
5 | var fs = require('fs'),
6 | path = require('path'),
7 | dir = require('node-dir');
8 |
9 | return {
10 |
11 | getErrorTemplatePaths: function (callback) {
12 | var templatePaths = {
13 | '404': {
14 | server: LAZO.BASE_PATH + '/lib/common/templates/404.hbs',
15 | client: 'lib/common/templates/404.hbs'
16 | },
17 | '500': {
18 | server: LAZO.BASE_PATH + '/lib/common/templates/500.hbs',
19 | client: 'lib/common/templates/500.hbs'
20 | }
21 | },
22 | basename,
23 | clientPath,
24 | serverPath,
25 | directory = path.normalize(LAZO.FILE_REPO_PATH + '/app/' + 'views');
26 |
27 | this.list(directory, { ext: '.hbs' }, function (err, files) {
28 | if (err) {
29 | throw err;
30 | }
31 |
32 | for (var i = 0; i < files.length; i++) {
33 |
34 | basename = path.basename(files[i], '.hbs');
35 | serverPath = path.normalize(files[i]);
36 | clientPath = path.normalize(serverPath.replace(LAZO.FILE_REPO_PATH, '/'));
37 | if (basename === '404' || basename === '500') {
38 | templatePaths[basename] = {
39 | server: serverPath,
40 | client: clientPath
41 | };
42 | }
43 | }
44 |
45 | callback(null, templatePaths);
46 | });
47 | },
48 |
49 | resolvePath: function (from, to) {
50 | if (LAZO.app.isClient) {
51 | LAZO.logger.warn('[server.resolver.resolvePath]', 'Should not be called on client.');
52 | return from;
53 | }
54 |
55 | return path.resolve(from, to);
56 | },
57 |
58 | list: function (directory, options, callback) {
59 | var filtered = [];
60 |
61 | if (LAZO.app.isClient) {
62 | LAZO.logger.warn('[server.resolver.resolvePath]', 'Should only be called on the server.');
63 | return callback(null, []);
64 | }
65 |
66 | directory = options.basePath ? path.resolve(options.basePath, directory) : directory;
67 | fs.exists(directory, function (exists) {
68 | if (!exists) {
69 | return callback(null, []);
70 | }
71 |
72 | dir.files(directory, function (err, files) {
73 | if (err) { // TODO: error handling
74 | throw err;
75 | }
76 |
77 | if (!options.basePath && !options.ext) {
78 | return files;
79 | }
80 |
81 | for (var i = 0; i < files.length; i++) {
82 | if (options.ext && path.extname(files[i]) !== options.ext) {
83 | continue;
84 | }
85 | files[i] = options.basePath ? files[i].replace(options.basePath + '/', '') : files[i];
86 | filtered.push(files[i]);
87 | }
88 |
89 | callback(err, filtered || []);
90 | });
91 | });
92 | }
93 |
94 | };
95 |
96 | });
--------------------------------------------------------------------------------
/lib/server/scanner.js:
--------------------------------------------------------------------------------
1 | // returns a hash used to determine if the base class should be loaded
2 | var fs = require('fs');
3 | var path = require('path');
4 | var dir = require('node-dir');
5 |
6 | function scan(appPath, callback) {
7 | var processed = 0;
8 | var retVal = {
9 | components: { subdir: '/components', filters: ['controller.js', path.sep + 'views' + path.sep, '.css', path.sep + 'imports' + path.sep] },
10 | models: { subdir: '/models', filters: ['model.js', 'collection.js'] },
11 | appViews: { subdir: '/app/views', filters: [] }
12 | };
13 |
14 | function getRelativePath(appPath, absolutePath) {
15 | return absolutePath.replace(appPath + path.sep, '').replace(/\\/g, '/');
16 | }
17 |
18 | console.log('Scanning application directory. Please wait...');
19 |
20 | for (var k in retVal) {
21 | (function (k, retVal) {
22 | var subdir = path.normalize(appPath + retVal[k].subdir);
23 | var filesHash = {};
24 | dir.files(subdir, function (err, files) {
25 | if (err) {
26 | console.log('INFO: ' + err.path + ' does not exist');
27 | } else {
28 | files.forEach(function (file) {
29 | if (retVal[k].filters.length) {
30 | retVal[k].filters.forEach(function (filter) {
31 | if (file.indexOf(filter) !== -1) {
32 | filesHash[getRelativePath(appPath, file)] = true;
33 | }
34 | });
35 | } else {
36 | filesHash[getRelativePath(appPath, file)] = true;
37 | }
38 | });
39 | }
40 |
41 | retVal[k] = filesHash;
42 | processed++;
43 | if (processed === 3) {
44 | callback(retVal);
45 | }
46 | });
47 | })(k, retVal);
48 | }
49 | }
50 |
51 | module.exports = scan;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lazo",
3 | "version": "4.0.0",
4 | "description": "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.",
5 | "homepage": "https://github.com/lazojs/lazo",
6 | "keywords": [
7 | "backbone",
8 | "requirejs",
9 | "hapi",
10 | "mvc",
11 | "client-server",
12 | "lazo",
13 | "lazojs",
14 | "isomorphic javascript",
15 | "SEO"
16 | ],
17 | "licenses": [
18 | {
19 | "type": "MIT",
20 | "url": "/blob/master/LICENSE-MIT"
21 | }
22 | ],
23 | "repository": {
24 | "type": "git",
25 | "url": "https://github.com/lazojs/lazo"
26 | },
27 | "bugs": {
28 | "url": "https://github.com/lazojs/lazo/issues"
29 | },
30 | "private": false,
31 | "scripts": {
32 | "start": "lazo",
33 | "prepublish": "node bin/postinstall; grunt requirejs",
34 | "test": "grunt test"
35 | },
36 | "main": "index.js",
37 | "bin": {
38 | "lazo": "lazo.js"
39 | },
40 | "dependencies": {
41 | "crumb": "3.1.0",
42 | "request": "2.40.0",
43 | "forever": "0.10.8",
44 | "fs-extra": "0.6.3",
45 | "handlebars": "2.0.0",
46 | "yargs": "3.6.0",
47 | "node-dir": "0.1.5",
48 | "lodash": "2.4.1",
49 | "hapi": "6.7.1",
50 | "requirejs": "2.1.11",
51 | "backbone": "1.1.1",
52 | "underscore": "1.6.0",
53 | "jquery": "1.11.1",
54 | "requirejs-text": "2.0.12",
55 | "hermes-conrad": "1.x.x",
56 | "jquery.cookie": "1.4.1",
57 | "requirejs-plugins": "1.0.2",
58 | "async": "0.6.2",
59 | "flexo.js": "0.2.x",
60 | "htmlparser": "1.7.7",
61 | "htmlparser-tostring": "0.0.2"
62 | },
63 | "devDependencies": {
64 | "grunt-contrib-requirejs": "0.4.x",
65 | "grunt": "0.4.5",
66 | "grunt-contrib-watch": "0.5.x",
67 | "intern": "2.1.1",
68 | "sinon-chai": "2.6.0",
69 | "sinon": "1.10.3",
70 | "phantomjs": "1.9.13",
71 | "selenium-server": "2.43.1",
72 | "grunt-exec": "0.4.6"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/run.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var path = require('path');
3 | var lazoPath = path.dirname(module.filename);
4 | var os = require('os');
5 | var lazo = require('./index.js')
6 | var args = parseArgs();
7 |
8 | // helper functions
9 | function getAppDir() {
10 | var fileRepo = args._[1] ? path.resolve(args._[1]) : undefined;
11 | return fileRepo && fs.existsSync(fileRepo) ? fileRepo : 0;
12 | }
13 |
14 | function parseArgs() {
15 | return require('yargs')
16 | .alias('c', 'cluster')
17 | .alias('v', 'version')
18 | .alias('d', 'daemon')
19 | .alias('p', 'port')
20 | .default('port', '8080')
21 | .alias('r', 'robust')
22 | .boolean(['d', 'r'])
23 | .string(['p', 'c'])
24 | .argv;
25 | }
26 |
27 | // get the options for a lazo command
28 | function getStartEnvOptions() {
29 | var options = {};
30 |
31 | for (var k in args) {
32 | switch (k) {
33 | case 'daemon':
34 | case 'robust':
35 | options[k] = args[k] === true ? '1' : '0';
36 | break;
37 | case 'cluster':
38 | options[k] = args[k] ? args[k] : os.cpus().length;
39 | break;
40 | case 'port':
41 | options[k] = args[k];
42 | break;
43 | }
44 | }
45 |
46 | return options;
47 | }
48 |
49 | // lazo --help [command]
50 | function help(command) {
51 | switch (command) {
52 | case 'start':
53 | console.log('\nUsage: lazo start app_dir -c [num] -d -p [num]\n');
54 | console.log('Options:\n');
55 | console.log('app_dir [required] application directory');
56 | console.log('-c [optional] cluster, value [optional]\n');
57 | console.log('-d [optional] daemonize process using forever\n');
58 | console.log('-p [optional] port, value [required]\n');
59 | break;
60 | case 'stop':
61 | console.log('\nUsage: lazo stop\n');
62 | break;
63 | default:
64 | console.log('\nUsage: lazo --help command\n');
65 | console.log('Available commands: start stop\n');
66 | console.log('Options:\n');
67 | console.log('command [optional]\n');
68 | break;
69 | }
70 | }
71 |
72 | // entry point
73 | function main() {
74 | if (args._.length) {
75 | switch (args._[0]) {
76 | case 'start':
77 | args.app_dir = getAppDir();
78 | lazo('start', args);
79 | break;
80 | case 'stop':
81 | lazo('stop', args);
82 | break;
83 | default:
84 | help();
85 | break;
86 | }
87 | } else {
88 | if (args.version) {
89 | console.log('v' + lazo('version'));
90 | } else if (args.help) {
91 | help(args.help);
92 | } else {
93 | help();
94 | }
95 | }
96 | }
97 |
98 | module.exports = main;
--------------------------------------------------------------------------------
/start.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var path = require('path');
3 | var lazoPath = path.dirname(module.filename);
4 | var os = require('os');
5 |
6 | function setEnv(options) {
7 | process.env['BASE_PATH'] = lazoPath;
8 | process.env['BASE_REPO_DIR'] = path.join(lazoPath, 'base');
9 | process.env['LAZO_VERSION'] = options.version;
10 | process.env['FILE_REPO_DIR'] = options.app_dir;
11 | process.env['PORT'] = options.port;
12 |
13 | for (var k in options) {
14 | process.env[k.toUpperCase()] = options[k];
15 | }
16 | }
17 |
18 | function daemon() {
19 | var fsx = require('fs-extra');
20 | var forever = require('forever');
21 |
22 | fsx.mkdirs(lazoPath + '/logs', function (err) {
23 | if (!err) {
24 | forever.load({ root: lazoPath + '/logs', pidPath: lazoPath });
25 | forever.startDaemon('./lib/server/app.js', {
26 | logFile: 'lazo.log',
27 | pidFile: 'lazo.pid',
28 | errFile: 'lazo.err',
29 | sourceDir: lazoPath,
30 | a: true
31 | });
32 | } else {
33 | console.error(err);
34 | }
35 | });
36 | }
37 |
38 | module.exports = function (options) {
39 | setEnv(options);
40 | if (options.daemon) {
41 | return daemon();
42 | }
43 |
44 | console.log('Starting Lazo! Please wait...');
45 | // starts application server
46 | var lazo = require('./lib/server/app.js');
47 | }
--------------------------------------------------------------------------------
/stop.js:
--------------------------------------------------------------------------------
1 | module.exports = function () {
2 | var forever = require('forever');
3 | var fsx = require('fs-extra');
4 |
5 | if (fsx.existsSync(lazoPath + '/lazo.pid')) {
6 | try {
7 | forever.stop('lib/server/app.js', true);
8 | forever.cleanUp();
9 | fsx.remove(lazoPath + '/lazo.pid');
10 | console.log('Lazo! stopped');
11 | } catch (err) {
12 | console.log('Error stopping Lazo!');
13 | process.exit(1);
14 | }
15 | }
16 | };
--------------------------------------------------------------------------------
/templates/README.md:
--------------------------------------------------------------------------------
1 | This will contain templates for the create and add commands.
--------------------------------------------------------------------------------
/test/application/app/assets/en-US/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lazojs/lazo/0b91b8b3b9f5246414b23458e8202fc4940ebd45/test/application/app/assets/en-US/img/logo.png
--------------------------------------------------------------------------------
/test/application/app/assets/en-US/strings.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Käthe Kollwitz"
3 | }
--------------------------------------------------------------------------------
/test/application/app/assets/info.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lazojs/lazo/0b91b8b3b9f5246414b23458e8202fc4940ebd45/test/application/app/assets/info.pdf
--------------------------------------------------------------------------------
/test/application/app/assets/strings.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Robert Crumb"
3 | }
--------------------------------------------------------------------------------
/test/application/app/imports/index.html:
--------------------------------------------------------------------------------
1 |
I am an application import.
--------------------------------------------------------------------------------
/test/application/components/bar/assets/en-US/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lazojs/lazo/0b91b8b3b9f5246414b23458e8202fc4940ebd45/test/application/components/bar/assets/en-US/img/logo.png
--------------------------------------------------------------------------------
/test/application/components/bar/assets/en-US/strings.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Käthe Kollwitz"
3 | }
--------------------------------------------------------------------------------
/test/application/components/bar/assets/info.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lazojs/lazo/0b91b8b3b9f5246414b23458e8202fc4940ebd45/test/application/components/bar/assets/info.pdf
--------------------------------------------------------------------------------
/test/application/components/bar/assets/strings.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Robert Crumb"
3 | }
--------------------------------------------------------------------------------
/test/application/components/bar/imports/index.html:
--------------------------------------------------------------------------------
1 |
I am an import for component bar.
--------------------------------------------------------------------------------
/test/application/components/foo/views/child.js:
--------------------------------------------------------------------------------
1 | define(['lazoView'], function (LazoView) {
2 |
3 | return LazoView.extend({
4 | child: true,
5 | getTemplate: function (options) {
6 | options.success('I am the child template.');
7 | }
8 | });
9 |
10 | });
--------------------------------------------------------------------------------
/test/application/components/foo/views/index.hbs:
--------------------------------------------------------------------------------
1 | I am the template.
--------------------------------------------------------------------------------
/test/mocks/css/a.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lazojs/lazo/0b91b8b3b9f5246414b23458e8202fc4940ebd45/test/mocks/css/a.css
--------------------------------------------------------------------------------
/test/mocks/css/b.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lazojs/lazo/0b91b8b3b9f5246414b23458e8202fc4940ebd45/test/mocks/css/b.css
--------------------------------------------------------------------------------
/test/mocks/css/c.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lazojs/lazo/0b91b8b3b9f5246414b23458e8202fc4940ebd45/test/mocks/css/c.css
--------------------------------------------------------------------------------
/test/mocks/css/d.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lazojs/lazo/0b91b8b3b9f5246414b23458e8202fc4940ebd45/test/mocks/css/d.css
--------------------------------------------------------------------------------
/test/mocks/lazo.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |
3 | var LAZO = {},
4 | isServer = true,
5 | isClient = true;
6 |
7 | try {
8 | window;
9 | isServer = false;
10 | } catch (err) {
11 | isClient = false;
12 | }
13 |
14 | LAZO.app = {
15 | isServer: isServer,
16 | isClient: isClient
17 | };
18 |
19 | LAZO.logger = {
20 | debug: function () {
21 | },
22 | error: function () {
23 | },
24 | log: function () {
25 | },
26 | warn: function () {
27 | },
28 | info: function () {
29 | }
30 | };
31 |
32 | LAZO.config = {
33 | get: function (key) {
34 | return this[key];
35 | },
36 | set: function (key, value) {
37 | this[key] = value;
38 | }
39 | };
40 |
41 | LAZO.files = {
42 | components: [],
43 | models: [],
44 | appViews: []
45 | };
46 |
47 | LAZO.FILE_REPO_PATH = './test';
48 |
49 | return LAZO;
50 |
51 | });
--------------------------------------------------------------------------------
/test/mocks/server/continuation-local-storage.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |
3 | return {
4 | getNamespace: function () {
5 |
6 | },
7 |
8 | createNamespace: function () {
9 |
10 | }
11 | };
12 |
13 | });
--------------------------------------------------------------------------------
/test/mocks/server/hapi.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |
3 | return function Hapi() {
4 |
5 | };
6 |
7 | });
--------------------------------------------------------------------------------
/test/mocks/server/request.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |
3 | function request (uri, options, callback) {
4 | requestStub(uri, options, callback);
5 | }
6 |
7 | return request;
8 |
9 | });
--------------------------------------------------------------------------------
/test/unit/client-server/common/config.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'underscore',
3 | 'intern!bdd',
4 | 'intern/chai!expect',
5 | 'test/unit/utils',
6 | 'sinon',
7 | 'intern/chai!',
8 | 'sinon-chai',
9 | 'lib/common/config'
10 | ], function (_, bdd, expect, utils, sinon, chai, sinonChai, config) {
11 | chai.use(sinonChai);
12 |
13 | with (bdd) {
14 |
15 | describe('config', function () {
16 |
17 | config.addPlugin(new function(){
18 | return {
19 | data: {"key": "value"},
20 |
21 | get: function (key, options) {
22 | var ret;
23 | if (options && options.context) {
24 | ret = options.context[key];
25 | } else if (this.data && key in this.data) {
26 | ret = this.data[key];
27 | }
28 |
29 | return (options && _.isFunction(options.success)) ? options.success(ret) : ret;
30 | }
31 | }
32 | });
33 |
34 |
35 | it('should return the value for a given key', function () {
36 | expect(config.get("key")).to.be.equal('value');
37 | });
38 |
39 | it('should return the value for a given key in the provided context', function () {
40 | expect(config.get("key", {context:{"key":"contextValue"}})).to.be.equal('contextValue');
41 | });
42 |
43 | it('should return the value for a given key via a success callback', function () {
44 | var dfd = this.async();
45 |
46 | config.get("key",{
47 | success: function(value){
48 | expect(value).to.be.equal('value');
49 | dfd.resolve();
50 | }
51 | });
52 | });
53 |
54 | it('should return the value for a given key in the provided context via a success callback', function () {
55 | var dfd = this.async();
56 |
57 | config.get("key",{
58 | context: {"key":"contextValue"},
59 | success: function(value){
60 | expect(value).to.be.equal('contextValue');
61 | dfd.resolve();
62 | }
63 | });
64 | });
65 |
66 | });
67 | }
68 | });
--------------------------------------------------------------------------------
/test/unit/client-server/common/logger.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!expect',
4 | 'test/unit/utils',
5 | 'sinon',
6 | 'intern/chai!',
7 | 'sinon-chai',
8 | 'lib/common/logger'
9 | ], function (bdd, expect, utils, sinon, chai, sinonChai, logger) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 |
14 | describe('logger', function () {
15 |
16 | var noop = function () {
17 | };
18 |
19 | it('should have error as default level', function () {
20 | expect(logger.getLevel()).to.be.equal('error');
21 | });
22 |
23 | it('should have console as default sink', function () {
24 | var sinks = logger.getSinks();
25 | expect(sinks.console).to.exist;
26 | expect(sinks.console).to.be.a.function;
27 | });
28 |
29 | it('should format messages correctly', function () {
30 | this.skip();
31 | var dfd = this.async();
32 | sinon.stub(console, 'log');
33 |
34 | var error = new Error('consectetur adipiscing elit');
35 | error.stack = 'nullam vel tempus massa';
36 |
37 | expect(logger.error('Lorem ipsum dolor sit amet')).to.be.equal('2012-01-27T12:30:00.000Z\tERROR\t-\tLorem ipsum dolor sit amet');
38 | expect(logger.error('Lorem ipsum dolor sit amet', {foo: 123, bar: 456})).to.be.equal('2012-01-27T12:30:00.000Z\tERROR\t-\tLorem ipsum dolor sit amet {"foo":123,"bar":456}');
39 | expect(logger.error('Lorem ipsum dolor sit amet', error)).to.be.equal('2012-01-27T12:30:00.000Z\tERROR\t-\tLorem ipsum dolor sit amet {"message":"consectetur adipiscing elit","stack":"nullam vel tempus massa"}');
40 | expect(logger.error('Lorem %s dolor %s amet', 'ipsum', 'sit')).to.be.equal('2012-01-27T12:30:00.000Z\tERROR\t-\tLorem ipsum dolor sit amet');
41 | expect(logger.error('Lorem %s dolor %s amet')).to.be.equal('2012-01-27T12:30:00.000Z\tERROR\t-\tLorem undefined dolor undefined amet');
42 | expect(logger.error('Lorem ipsum dolor sit amet %d %f', 3.14159, 3.14159)).to.be.equal('2012-01-27T12:30:00.000Z\tERROR\t-\tLorem ipsum dolor sit amet 3 3.14159');
43 |
44 | setTimeout(function () {
45 | expect(console.log.callCount).to.be.equal(6);
46 | console.log.restore();
47 | dfd.resolve();
48 | }, 0);
49 | });
50 |
51 | it('should change the log level', function () {
52 | logger.setLevel('warn');
53 | expect(logger.getLevel()).to.be.equal('warn');
54 | logger.setLevel('info');
55 | expect(logger.getLevel()).to.be.equal('info');
56 | logger.setLevel('debug');
57 | expect(logger.getLevel()).to.be.equal('debug');
58 | });
59 |
60 | it('should call new registered sink', function () {
61 | var dfd = this.async();
62 | var stubSink = sinon.stub();
63 |
64 | logger.addSink('stub', stubSink);
65 |
66 | expect(logger.getSinks()['stub']).to.be.a.function;
67 |
68 | logger.error('Lorem ipsum dolor sit amet');
69 |
70 | setTimeout(function () {
71 | expect(stubSink).to.be.called.once;
72 | dfd.resolve();
73 | }, 0);
74 | });
75 |
76 | it('should remove registered sink', function () {
77 | expect(logger.getSinks()['stub']).to.be.a.function;
78 | logger.removeSink('stub');
79 | expect(logger.getSinks()['stub']).to.not.exist;
80 | });
81 |
82 | });
83 | }
84 | });
--------------------------------------------------------------------------------
/test/unit/client-server/common/renderer.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!expect',
4 | 'test/unit/utils',
5 | 'sinon',
6 | 'intern/chai!',
7 | 'sinon-chai',
8 | 'renderer'
9 | ], function (bdd, expect, utils, sinon, chai, sinonChai, renderer) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 |
14 | describe('Renderer', function () {
15 |
16 | it('should get the index for an markup insertion in a string', function () {
17 | var dfd = this.async();
18 |
19 | utils.setUpApp(function () {
20 | var html = '
';
21 | var insertStr = '
hello
';
22 | var expectStr = '
';
23 | var match = renderer.getInsertIndex('lazo-cmp-container', 'foo', html);
24 | var open = html.substr(0, match.index + match[0].length);
25 | var close = html.substr(match.index + match[0].length);
26 | var testStr = open + insertStr + close;
27 |
28 | expect(testStr).to.be.equal(expectStr);
29 | dfd.resolve();
30 | });
31 | });
32 |
33 | it('should render a tree', function () {
34 | var dfd = this.async();
35 |
36 | utils.setUpApp(function () {
37 | utils.createCtlTree(function (ctl) {
38 | renderer.getTreeHtml(ctl, null, null, function (html) {
39 | var regex = /
I am a template!<\/div><\/div>
I am a template!<\/div><\/div><\/div><\/div><\/div>/;
40 | var match = html.match(regex);
41 |
42 | expect(match.length).to.be.equal(1);
43 | expect(match.index).to.be.equal(0);
44 | dfd.resolve();
45 | });
46 | });
47 | });
48 | });
49 | });
50 |
51 | }
52 | });
--------------------------------------------------------------------------------
/test/unit/client-server/common/resolver/file.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'resolver/file'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, file) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('file resolver', function () {
14 |
15 | it('should get a view\'s path', function () {
16 | var cmpViewPath = file.getPath('module_foo', 'cmp_bar', 'view'),
17 | appViewPath = file.getPath('a:module_bar', 'cmp_bar', 'view');
18 |
19 | expect(cmpViewPath).to.be.equal('components/cmp_bar/views/module_foo');
20 | expect(appViewPath).to.be.equal('app/views/module_bar');
21 |
22 | });
23 |
24 | it('should get a template\'s path', function () {
25 | var view1 = {
26 | templateName: 'view_tmp',
27 | ctl: {
28 | name: 'cmp_name'
29 | },
30 | templateEngine: 'handlebars'
31 | },
32 | view2 = {
33 | templateName: 'a:view_tmp',
34 | ctl: {
35 | name: 'cmp_name'
36 | },
37 | templateEngine: 'handlebars'
38 | },
39 | cmpTemplatePath = file.getTemplatePath(view1),
40 | appTemplatePath = file.getTemplatePath(view2);
41 |
42 | expect(cmpTemplatePath).to.be.equal('components/cmp_name/views/view_tmp.hbs');
43 | expect(appTemplatePath).to.be.equal('app/views/view_tmp.hbs');
44 |
45 | });
46 |
47 | it('should get a template\'s name', function () {
48 | var view1 = {
49 | templateName: 'view_tmp',
50 | ctl: {
51 | name: 'cmp_name'
52 | },
53 | templateEngine: 'handlebars'
54 | },
55 | view2 = {
56 | templateName: function () { return 'a:view_tmp' },
57 | ctl: {
58 | name: 'cmp_name'
59 | },
60 | templateEngine: 'handlebars'
61 | },
62 | strTemplateName = file.getTemplateName(view1),
63 | fnTemplateName = file.getTemplateName(view2);
64 |
65 | expect(strTemplateName).to.be.equal('view_tmp');
66 | expect(fnTemplateName).to.be.equal('a:view_tmp');
67 |
68 | });
69 |
70 | it('should get a view\'s base path', function () {
71 | var cmpViewPath = file.getBasePath('module_foo', 'cmp_bar', 'view'),
72 | appViewPath = file.getBasePath('a:module_bar', 'cmp_bar', 'view');
73 |
74 | expect(cmpViewPath).to.be.equal('components/cmp_bar/views');
75 | expect(appViewPath).to.be.equal('app/views');
76 |
77 | });
78 |
79 | });
80 | }
81 | });
--------------------------------------------------------------------------------
/test/unit/client-server/common/resolver/route.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'resolver/route'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, route) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('route resolver', function () {
14 | it('should transform routes', function () {
15 | var foo = route.transform('foo(/)');
16 | var bar = route.transform('bar');
17 | var baz = route.transform('');
18 | var dynamic = route.transform('foo/*bar');
19 |
20 | expect(foo.routeTrailingSlash).to.be.equal('/foo/');
21 | expect(foo.route).to.be.equal('/foo');
22 | expect(bar.routeTrailingSlash).to.be.null;
23 | expect(bar.route).to.be.equal('/bar');
24 | expect(baz.routeTrailingSlash).to.be.null;
25 | expect(baz.route).to.be.equal('/');
26 |
27 | if (LAZO.app.isServer) {
28 | // hapi style splat
29 | expect(dynamic.route).to.be.equal('/foo/{bar*}');
30 | } else {
31 | // backbone style splat
32 | expect(dynamic.route).to.be.equal('/foo/*bar');
33 | }
34 | });
35 | });
36 | }
37 | });
--------------------------------------------------------------------------------
/test/unit/client-server/common/utils/handlebarsEngine.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'handlebars',
9 | 'utils/handlebarsEngine'
10 | ], function (bdd, chai, expect, sinon, sinonChai, utils, handlebars, hbsEng) {
11 | chai.use(sinonChai);
12 |
13 | with (bdd) {
14 | describe('handlebarsEngine', function () {
15 | it('should compile a template', function () {
16 | var template = hbsEng.compile('I am {{fname}} {{lname}}.');
17 |
18 | expect(template).to.be.function;
19 | });
20 |
21 | it('should precompile a template', function () {
22 | var template = hbsEng.precompile('I am {{fname}} {{lname}}.');
23 |
24 | expect(template).to.be.function;
25 | });
26 |
27 | it('should execute a template', function () {
28 | var context = {
29 | fname: 'John',
30 | lname: 'Doe'
31 | };
32 | var template = hbsEng.compile('I am {{fname}} {{lname}}.');
33 |
34 | expect(hbsEng.execute(template, context)).to.be.equal('I am John Doe.');
35 | });
36 |
37 | it('should get handlebars', function () {
38 | expect(hbsEng.engine.VERSION).to.be.equal(handlebars.default.VERSION);
39 | });
40 |
41 | });
42 | }
43 | });
--------------------------------------------------------------------------------
/test/unit/client-server/public/collection.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'lazoModel',
9 | 'lazoCollection'
10 | ], function (bdd, chai, expect, sinon, sinonChai, utils, LazoModel, LazoCollection) {
11 | chai.use(sinonChai);
12 |
13 | with (bdd) {
14 | describe('publicCollection', function () {
15 |
16 | it('should assign the correct model name to collection items', function () {
17 |
18 | var ChildModel = LazoModel.extend({});
19 | var ParentCollection = LazoCollection.extend({
20 | model: ChildModel
21 | });
22 |
23 | var parent = new ParentCollection(null, { modelName: 'childModel' });
24 | parent.add(new ChildModel({ id: 1 }, {}));
25 |
26 | expect(parent.models[0].name).to.exist;
27 | });
28 |
29 | });
30 |
31 | }
32 | });
--------------------------------------------------------------------------------
/test/unit/client-server/public/views/state.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'uiStateMixin',
9 | 'jquery'
10 | ], function (bdd, chai, expect, sinon, sinonChai, utils, uiStateMixin, $) {
11 | chai.use(sinonChai);
12 |
13 | with (bdd) {
14 | describe('Lazo View, Widget State Mixin', function () {
15 |
16 | it('should set the state class for a widget or view', function () {
17 | if (LAZO.app.isClient) {
18 | uiStateMixin.el = $('
')[0];
19 | }
20 | uiStateMixin.setState('disabled', true);
21 | expect(uiStateMixin._uiStates.disabled).to.be.equal('disabled');
22 | if (LAZO.app.isClient) {
23 | expect($(uiStateMixin.el).hasClass('lazo-disbaled'));
24 | }
25 | });
26 |
27 | });
28 | }
29 | });
--------------------------------------------------------------------------------
/test/unit/client-server/public/widget.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'lazoWidget'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, LazoWidget) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('Lazo Widget Interface', function () {
14 | var MyWidget = LazoWidget.extend({});
15 | var spy = sinon.spy(MyWidget.prototype, 'initialize');
16 | var widget = new MyWidget({
17 | view: {
18 | ctl: {
19 | ctx: {
20 | foo: 1,
21 | bar: true,
22 | baz: 'baz'
23 | }
24 | }
25 | },
26 | obj: '{ foo: true }',
27 | arr: '[1, 2, 3]',
28 | 'data-foo': '$.foo',
29 | 'data-bar': '$.bar',
30 | 'data-baz': '$.baz',
31 | num1: '1.03',
32 | num2: '8',
33 | bool1: 'true',
34 | bool2: 'false'
35 | });
36 |
37 | it('should have empty implementation and default values', function () {
38 | expect(widget.initialize).to.be.Function;
39 | expect(widget.render).to.be.Function;
40 | expect(widget.bind).to.be.Function;
41 | expect(widget.unbind).to.be.Function;
42 | expect(widget.afterRender).to.be.Function;
43 | expect(widget.attrValCoercion).to.be.true;
44 | });
45 |
46 | it('should construct a LazoWidget instance', function () {
47 | expect(widget).to.be.instanceof(LazoWidget);
48 | expect(spy).to.have.been.calledOnce;
49 | });
50 |
51 | it('should coerce values', function () {
52 | expect(widget.attributes.obj).to.be.Object;
53 | expect(widget.attributes.arr).to.be.Array;
54 | expect(widget.attributes.num1).to.be.equal(1.03);
55 | expect(widget.attributes.num2).to.be.equal(8);
56 | expect(widget.attributes.bool1).to.be.true;
57 | expect(widget.attributes.bool2).to.be.false;
58 | });
59 |
60 | it('should resolve context values', function () {
61 | expect(widget.attributes['data-foo']).to.be.equal(1);
62 | expect(widget.attributes['data-bar']).to.be.true;
63 | expect(widget.attributes['data-baz']).to.be.equal('baz');
64 | });
65 |
66 | });
67 | }
68 | });
--------------------------------------------------------------------------------
/test/unit/client-server/utils/ctlSerializor.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'utils/ctlSerializor'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, ctlSerializor) {
10 | chai.use(sinonChai);
11 |
12 | var id = 0;
13 |
14 | function generateCtl(name) {
15 | return {
16 | name: name,
17 | cid: name + (++id),
18 | ctx: {
19 | models: {},
20 | collections: {},
21 | _rootCtx: {
22 | blah: 1
23 | },
24 | params: {
25 | '': '',
26 | good: 1,
27 | bad: ''
28 | }
29 | },
30 | children: {},
31 | currentView: {
32 | cid:'view' + (++id),
33 | name:'index',
34 | ref:'components/' + name + '/views/index',
35 | templatePath:'components/' + name + '/views/index.hbs',
36 | basePath:'components/' + name + '/views',
37 | isBase: false,
38 | hasTemplate: true
39 | },
40 | toJSON: function (rootCtx) {
41 | return ctlSerializor.serialize(this);
42 | }
43 | };
44 | }
45 |
46 | with (bdd) {
47 | describe('Controller Serializor', function () {
48 |
49 | it('should serialize a component controller for transport', function () {
50 | var serializedCtl;
51 | var ctl = generateCtl('foo');
52 |
53 | ctl.children.bar = [generateCtl('bar')];
54 | ctl.children.bar[0].children.baz = [generateCtl('baz')];
55 | serializedCtl = ctlSerializor.serialize(ctl);
56 |
57 | expect(serializedCtl).to.include.keys('cid', 'name', 'ctx', 'isBase', 'currentView', 'children');
58 | // check if children were serialized
59 | expect(serializedCtl.children.bar[0]).to.be.Object;
60 | expect(serializedCtl.children.bar[0].children.baz).to.be.Object;
61 | // check if views were serialized
62 | expect(serializedCtl.currentView).to.be.Object;
63 | expect(serializedCtl.children.bar[0].currentView).to.be.Object;
64 | expect(serializedCtl.currentView).to.include
65 | .keys('cid', 'name', 'ref', 'templatePath', 'compiledTemplatePath', 'basePath', 'isBase', 'hasTemplate');
66 | // check if params were encoded
67 | expect(serializedCtl.ctx.params['%3Cscript%3Ealert(%22hello%22)%3C%2Fscript%3E']).to.exist;
68 | expect(serializedCtl.ctx.params.bad).to.be.equal('%3Cscript%3Ealert(%22hello%22)%3C%2Fscript%3E');
69 | });
70 |
71 | });
72 | }
73 | });
--------------------------------------------------------------------------------
/test/unit/client-server/utils/treeMixin.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'utils/treeMixin'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, treeMixin) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('Tree Mixin', function () {
14 |
15 | it('should get a list of nodes', function () {
16 | var dfd = this.async();
17 |
18 | utils.setUpApp(function () {
19 | utils.createCtlTree(function (ctl) {
20 | var views = treeMixin.getList('view', ctl);
21 | var components = treeMixin.getList('component', ctl);
22 | var i;
23 |
24 | expect(views.length).to.be.equal(3);
25 | for (i = 0; i < 3; i++) {
26 | expect(views[i].setElement).to.be.function;
27 | }
28 |
29 | expect(components.length).to.be.equal(3);
30 | for (i = 0; i < 3; i++) {
31 | expect(components[i].currentView).to.be.defined;
32 | }
33 |
34 | dfd.resolve();
35 | });
36 | });
37 | });
38 |
39 | it('should get a node\'s type', function () {
40 | utils.createCtlTree(function (ctl) {
41 | expect(treeMixin.getNodeType(ctl.currentView)).to.be.equal('view');
42 | expect(treeMixin.getNodeType(ctl)).to.be.equal('component');
43 | });
44 | });
45 |
46 | it('should get a node\'s children', function () {
47 | utils.createCtlTree(function (ctl) {
48 | var children = treeMixin.getNodeChildren(ctl);
49 | expect(children.length).to.be.equal(2);
50 | });
51 | });
52 |
53 | });
54 | }
55 | });
--------------------------------------------------------------------------------
/test/unit/client/common/app.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'lazoApp'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, LazoApp) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('Lazo Application - client', function () {
14 |
15 | it('should no-op on add http vary params', function () {
16 |
17 | var lazoApp = new LazoApp({});
18 | lazoApp.isClient = true;
19 | expect(lazoApp.getHttpVaryParams().length).to.equal(0);
20 | var app = lazoApp.addHttpVaryParam('user-agent');
21 |
22 | var params = lazoApp.getHttpVaryParams();
23 | expect(params.length).to.equal(0);
24 | expect(app == lazoApp).to.be.true;
25 |
26 | });
27 |
28 | it('should no-op on add http headers', function () {
29 | var lazoApp = new LazoApp({});
30 | lazoApp.isClient = true;
31 | expect(lazoApp.getHttpHeaders().length).to.equal(0);
32 | var app = lazoApp.addHttpHeader('X-Frame-Options', 'deny');
33 |
34 | var headers = lazoApp.getHttpHeaders();
35 | expect(headers.length).to.equal(0);
36 | expect(app == lazoApp).to.be.true;
37 | });
38 | });
39 | }
40 | });
--------------------------------------------------------------------------------
/test/unit/client/common/resolver/requireConfigure.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'resolver/requireConfigure'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, conf) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('requirejs configure', function () {
14 |
15 | beforeEach(function () {
16 | LAZO.initConf = {}; // mock for test
17 | });
18 |
19 | afterEach(function () {
20 | delete LAZO.initConf; // clean up test mock
21 | });
22 |
23 | it('get client configuration', function () {
24 | this.skip();
25 | var dfd = this.async();
26 | var config;
27 | var options = {
28 | basePath: 'base/path',
29 | baseUrl: 'base/url'
30 | };
31 |
32 | conf.get('client', options, function (err, conf) {
33 | config = conf;
34 |
35 | expect(config.baseUrl).to.be.equal('base/url');
36 | expect(config.context).to.be.equal('application');
37 | expect(config.map['*'].s.indexOf('\/client\/')).to.not.be.equal(-1);
38 |
39 | dfd.resolve();
40 | });
41 | });
42 |
43 | });
44 | }
45 | });
--------------------------------------------------------------------------------
/test/unit/client/common/utils/document.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'utils/document',
9 | 'jquery'
10 | ], function (bdd, chai, expect, sinon, sinonChai, utils, doc, $) {
11 | chai.use(sinonChai);
12 |
13 | with (bdd) {
14 | describe('document utils', function () {
15 |
16 | it('set page title', function () {
17 | var $title = $('title');
18 | doc.setTitle('foobar');
19 | expect($title.text()).to.be.equal('foobar');
20 | });
21 |
22 | // link.onload is not being called when test is executed in phantomjs because it
23 | // is not supported by the version of webkit phantomjs is running
24 | it('should update css', function () {
25 | this.skip(); // test failing regardless of env because link.onload never executes
26 | // if (window.navigator.userAgent.indexOf('PhantomJS') !== -1 || window.lazoLocalTesting) {
27 | // this.skip();
28 | // }
29 |
30 | var add = [{ href: '../../test/mocks/css/b.css' }, { href: '../../test/mocks/css/c.css' }, { href: '../../test/mocks/css/d.css' }];
31 | var remove = [{ href: '../../test/mocks/css/a.css' }];
32 | var $head = $('head');
33 | var dfd = this.async();
34 |
35 | $head.append('
');
36 |
37 | doc.updateLinks(add, remove, 'css', function () {
38 | var $links = $('link[lazo-link="css"]');
39 | expect($links.length).to.be.equal(3);
40 | $links.each(function (i) {
41 | expect($(this).attr('href')).to.be.equal(add[i]);
42 | });
43 | dfd.resolve();
44 | });
45 | });
46 |
47 | it('add, get page tags', function () {
48 |
49 | var ctx = {
50 | _rootCtx: {},
51 | meta: {}
52 | };
53 |
54 | expect(doc.getPageTags(ctx, false).length).to.equal(0);
55 | doc.addPageTag(ctx, false, 'meta', { description: 'text' });
56 | expect(doc.getPageTags(ctx, false).length).to.equal(1);
57 | });
58 |
59 | it('updates page tags', function () {
60 | var dfd = this.async();
61 | var ctx = {
62 | _rootCtx: {
63 | pageTags: [
64 | { name: 'meta', attributes: { description: 'text' }, content: null },
65 | { name: 'meta', attributes: { keywords: 'keyword' }, content: null }
66 | ]
67 | },
68 | meta: {
69 | pageTags: []
70 | }
71 | };
72 |
73 | // simulate initial update on client
74 | doc.updatePageTags(ctx, function (err) {
75 | expect(err).to.not.exist;
76 | expect(ctx._rootCtx.pageTags.length).to.equal(0);
77 | expect(ctx.meta.pageTags.length).to.equal(0);
78 |
79 | doc.addPageTag(ctx, false, 'meta', { description: 'text' });
80 | doc.addPageTag(ctx, false, 'meta', { keywords: 'keyword' });
81 | expect(ctx.meta.pageTags.length).to.equal(2);
82 |
83 | // simulate secondary update on client
84 | doc.updatePageTags(ctx, function (err) {
85 | expect(err).to.not.exist;
86 | expect(ctx._rootCtx.pageTags.length).to.equal(2);
87 | expect(ctx.meta.pageTags.length).to.equal(0);
88 | dfd.resolve();
89 | });
90 | });
91 | });
92 | });
93 | }
94 | });
--------------------------------------------------------------------------------
/test/unit/client/context.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'context'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, Context) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('Context', function () {
14 |
15 | it('common client', function () {
16 | var ctx = new Context({
17 | _request: {
18 | url: {
19 | pathname: 'foo/bar/baz'
20 | },
21 | raw: { // this is expected on the server
22 | req: {
23 | headers: {}
24 | }
25 | }
26 | }
27 | });
28 |
29 | // this will be equal to whatever the clients path is when phantom runs or the file is opened in the browser
30 | chai.expect(ctx.location.pathname).to.not.be.equal('foo/bar/baz');
31 | });
32 |
33 | });
34 | }
35 | });
--------------------------------------------------------------------------------
/test/unit/client/destroyCmp.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'lazoView',
9 | 'destroyCmp'
10 | ], function (bdd, chai, expect, sinon, sinonChai, utils, LazoView, destroyCmp) {
11 | chai.use(sinonChai);
12 |
13 | with (bdd) {
14 | describe('Destroy Component', function () {
15 |
16 | it('should clean up a context tree', function () {
17 | var dfd = this.async();
18 | utils.setUpApp(function () {
19 | utils.createCtlTree(function (ctl) {
20 | var parentViewSpy = sinon.spy(ctl.currentView, 'remove');
21 | var parentCtlSpy = sinon.spy(ctl._getEl(), 'remove');
22 | var firstChildViewSpy = sinon.spy(ctl.children.foo[0].currentView, 'remove');
23 | var firstChildCtlSpy = sinon.spy(ctl.children.foo[0]._getEl(), 'remove');
24 | var secondChildViewSpy = sinon.spy(ctl.children.foo[1].currentView, 'remove');
25 | var secondChildCtlSpy = sinon.spy(ctl.children.foo[1]._getEl(), 'remove');
26 |
27 | destroyCmp(ctl);
28 | expect(parentViewSpy.calledOnce).to.be.true;
29 | expect(parentCtlSpy.calledOnce).to.be.true;
30 | expect(firstChildViewSpy.calledOnce).to.be.true;
31 | expect(firstChildCtlSpy.calledOnce).to.be.true;
32 | expect(secondChildViewSpy.calledOnce).to.be.true;
33 | expect(secondChildCtlSpy.calledOnce).to.be.true;
34 |
35 | dfd.resolve();
36 | });
37 | });
38 | });
39 |
40 | });
41 | }
42 | });
--------------------------------------------------------------------------------
/test/unit/client/loader.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'l'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, loader) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('Client Loader', function () {
14 |
15 | it('should not load server files', function () {
16 | var dfd = this.async();
17 | loader.load('foo/server/bar', null, function (module) {
18 | expect(module).to.be.null;
19 | dfd.resolve();
20 | }, {});
21 |
22 | });
23 |
24 | });
25 | }
26 | });
--------------------------------------------------------------------------------
/test/unit/client/public/views/state.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'uiStateMixin',
9 | 'jquery'
10 | ], function (bdd, chai, expect, sinon, sinonChai, utils, uiStateMixin, $) {
11 | chai.use(sinonChai);
12 |
13 | uiStateMixin.el = $('
')[0];
14 |
15 | with (bdd) {
16 | describe('Lazo View, Widget State Mixin', function () {
17 |
18 | it('should get the valid attribute states', function () {
19 | var states = uiStateMixin._getValidStates();
20 | expect(states.focus).to.be.true;
21 | expect(states.disabled).to.be.true;
22 | expect(states.visible).to.be.true;
23 | expect(states.hidden).to.be.true;
24 | });
25 |
26 | it('should get the states for a widget or view', function () {
27 | var states = uiStateMixin._getStates();
28 | expect(states.focus).to.be.equal('focus');
29 | expect(states.disabled).to.be.equal('disabled');
30 | expect(states.visible).to.be.equal('visible');
31 | expect(states.hidden).to.be.equal('hidden');
32 | });
33 |
34 | });
35 | }
36 | });
--------------------------------------------------------------------------------
/test/unit/client/viewManager.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'lazoView',
9 | 'viewManager'
10 | ], function (bdd, chai, expect, sinon, sinonChai, utils, LazoView, viewManager) {
11 | chai.use(sinonChai);
12 |
13 | with (bdd) {
14 | describe('View Manager', function () {
15 |
16 | it('should attach views in a tree', function () {
17 | var dfd = this.async();
18 | utils.setUpApp(function () {
19 | utils.createCtlTree(function (ctl) {
20 | var spy = sinon.spy(ctl.currentView, 'attach');
21 | viewManager.attachViews(ctl, function (err) {
22 | if (err) {
23 | throw err;
24 | }
25 | expect(spy.calledOnce).to.be.true;
26 | dfd.resolve();
27 | });
28 | });
29 | });
30 | });
31 |
32 | it('should clean up a tree branch', function () {
33 | var dfd = this.async();
34 | utils.setUpApp(function () {
35 | utils.createCtlTree(function (ctl) {
36 | var spy = sinon.spy(LazoView.prototype, 'remove');
37 |
38 | viewManager.cleanup(ctl, ctl.currentView.cid);
39 | expect(spy.calledThrice).to.be.true;
40 | dfd.resolve();
41 | });
42 | });
43 | });
44 |
45 | });
46 | }
47 | });
--------------------------------------------------------------------------------
/test/unit/conf.client.js:
--------------------------------------------------------------------------------
1 | define(['intern/dojo/text!lib/common/resolver/paths.json', 'test/mocks/lazo', 'intern/dojo/text!conf.json'],
2 | function (paths, lazo, conf) {
3 |
4 | 'use strict';
5 |
6 | paths = JSON.parse(paths);
7 | conf = JSON.parse(conf);
8 |
9 | try {
10 | window.LAZO = lazo;
11 | } catch (err) {
12 | global.LAZO = lazo;
13 | }
14 | LAZO.app.isServer = false;
15 | LAZO.app.isClient = true;
16 | LAZO.isServer = false;
17 | LAZO.isClient = true;
18 |
19 | var needle = '/{env}/';
20 | var serverPaths = {};
21 | var env = LAZO.app.isServer ? 'server' : 'client';
22 | var replace = '/' + env + '/';
23 |
24 | for (var k in paths.common) { // update env specific implementation paths
25 | paths.common[k] = paths.common[k].replace(needle, replace);
26 | }
27 | for (k in paths[env]) { // merge env specific paths
28 | paths.common[k] = paths[env][k];
29 | }
30 |
31 | return {
32 |
33 | proxyPort: 9000,
34 |
35 | proxyUrl: 'http://localhost:9000/',
36 |
37 | capabilities: {
38 | 'selenium-version': '2.40.0'
39 | },
40 |
41 | // latest 2 browser version available, https://saucelabs.com/platforms
42 | environments: [
43 | // IE
44 | { browserName: 'internet explorer', version: '11', platform: 'Windows 8.1' },
45 | // FF
46 | { browserName: 'firefox', version: '31', platform: [ 'OS X 10.9', 'Windows 7' ] },
47 | // Chrome
48 | { browserName: 'chrome', version: '36', platform: [ 'OS X 10.9', 'Windows 7' ] },
49 | // Safari
50 | { browserName: 'safari', version: '7', platform: 'OS X 10.9' }
51 | ],
52 |
53 | tunnel: 'SauceLabsTunnel',
54 |
55 | excludeInstrumentation: /^(?:test|node_modules|lib\/vendor)\//,
56 |
57 | useLoader: {
58 | 'host-browser': '../../node_modules/requirejs/require.js'
59 | },
60 |
61 | loader: {
62 | shim: conf.requirejs.client.shim,
63 | paths: paths.common,
64 | map: {
65 | intern: {
66 | dojo: 'intern/node_modules/dojo',
67 | chai: 'intern/node_modules/chai/chai'
68 | },
69 | '*': {
70 | // testing libs
71 | sinon: '../../node_modules/sinon/lib/sinon.js',
72 | 'sinon-chai': '../../node_modules/sinon-chai/lib/sinon-chai.js'
73 | }
74 | }
75 | }
76 |
77 | };
78 |
79 | });
--------------------------------------------------------------------------------
/test/unit/conf.client.local.js:
--------------------------------------------------------------------------------
1 | define(['intern/dojo/text!lib/common/resolver/paths.json', 'test/mocks/lazo', 'intern/dojo/text!conf.json'],
2 | function (paths, lazo, conf) {
3 |
4 | 'use strict';
5 |
6 | paths = JSON.parse(paths);
7 | conf = JSON.parse(conf);
8 |
9 | try {
10 | window.LAZO = lazo;
11 | // used to skip tests in testing environments that do not support specific
12 | // browser event, e.g., link.onload
13 | window.lazoLocalTesting = true;
14 | } catch (err) {
15 | global.LAZO = lazo;
16 | }
17 | LAZO.app.isServer = false;
18 | LAZO.app.isClient = true;
19 | LAZO.isServer = false;
20 | LAZO.isClient = true;
21 |
22 | var needle = '/{env}/';
23 | var serverPaths = {};
24 | var env = LAZO.app.isServer ? 'server' : 'client';
25 | var replace = '/' + env + '/';
26 |
27 | for (var k in paths.common) { // update env specific implementation paths
28 | paths.common[k] = paths.common[k].replace(needle, replace);
29 | }
30 | for (k in paths[env]) { // merge env specific paths
31 | paths.common[k] = paths[env][k];
32 | }
33 |
34 | return {
35 |
36 | environments: [
37 | // { browserName: 'firefox' },
38 | // { browserName: 'safari' },
39 | { browserName: 'chrome' }
40 | ],
41 |
42 | excludeInstrumentation: /^(?:test|node_modules|lib\/vendor)\//,
43 |
44 | useLoader: {
45 | 'host-browser': '../../node_modules/requirejs/require.js'
46 | },
47 |
48 | loader: {
49 | shim: conf.requirejs.client.shim,
50 | paths: paths.common,
51 | map: {
52 | intern: {
53 | dojo: 'intern/node_modules/dojo',
54 | chai: 'intern/node_modules/chai/chai'
55 | },
56 | '*': {
57 | // testing libs
58 | sinon: '../../node_modules/sinon/lib/sinon.js',
59 | 'sinon-chai': '../../node_modules/sinon-chai/lib/sinon-chai.js',
60 | 'bundler': 'lazoBundle'
61 | }
62 | }
63 | },
64 |
65 | webdriver: {
66 | host: 'localhost',
67 | port: 4444
68 | },
69 |
70 | useSauceConnect: false
71 |
72 | };
73 |
74 | });
--------------------------------------------------------------------------------
/test/unit/conf.client.phantomjs.js:
--------------------------------------------------------------------------------
1 | define(['intern/dojo/text!lib/common/resolver/paths.json', 'test/mocks/lazo', 'intern/dojo/text!conf.json'],
2 | function (paths, lazo, conf) {
3 |
4 | 'use strict';
5 |
6 | paths = JSON.parse(paths);
7 | conf = JSON.parse(conf);
8 |
9 | try {
10 | window.LAZO = lazo;
11 | } catch (err) {
12 | global.LAZO = lazo;
13 | }
14 | LAZO.app.isServer = false;
15 | LAZO.app.isClient = true;
16 | LAZO.isServer = false;
17 | LAZO.isClient = true;
18 |
19 | var needle = '/{env}/';
20 | var serverPaths = {};
21 | var env = LAZO.app.isServer ? 'server' : 'client';
22 | var replace = '/' + env + '/';
23 |
24 | for (var k in paths.common) { // update env specific implementation paths
25 | paths.common[k] = paths.common[k].replace(needle, replace);
26 | }
27 | for (k in paths[env]) { // merge env specific paths
28 | paths.common[k] = paths[env][k];
29 | }
30 |
31 | return {
32 |
33 | tunnel: 'NullTunnel',
34 |
35 | environments: [{ browserName: 'phantomjs' }],
36 |
37 | excludeInstrumentation: /^(?:test|node_modules|lib\/vendor)\//,
38 |
39 | useLoader: {
40 | 'host-browser': '../../node_modules/requirejs/require.js'
41 | },
42 |
43 | loader: {
44 | shim: conf.requirejs.client.shim,
45 | paths: paths.common,
46 | map: {
47 | intern: {
48 | dojo: 'intern/node_modules/dojo',
49 | chai: 'intern/node_modules/chai/chai'
50 | },
51 | '*': {
52 | // testing libs
53 | sinon: '../../node_modules/sinon/lib/sinon.js',
54 | 'sinon-chai': '../../node_modules/sinon-chai/lib/sinon-chai.js',
55 | 'bundler': 'lazoBundle'
56 | }
57 | }
58 | }
59 |
60 | };
61 |
62 | });
--------------------------------------------------------------------------------
/test/unit/conf.server.js:
--------------------------------------------------------------------------------
1 | define(['intern/dojo/text!lib/common/resolver/paths.json', 'test/mocks/lazo'], function (paths, lazo) {
2 |
3 | 'use strict';
4 |
5 | paths = JSON.parse(paths);
6 |
7 | try {
8 | window.LAZO = lazo;
9 | } catch (err) {
10 | global.LAZO = lazo;
11 | }
12 | LAZO.app.isServer = true;
13 | LAZO.app.isClient = false;
14 | LAZO.isServer = true;
15 | LAZO.isClient = false;
16 |
17 | var needle = '/{env}/';
18 | var serverPaths = {};
19 | var env = LAZO.app.isServer ? 'server' : 'client';
20 | var replace = '/' + env + '/';
21 |
22 | for (var k in paths.common) { // update env specific implementation paths
23 | paths.common[k] = paths.common[k].replace(needle, replace);
24 | }
25 | for (k in paths[env]) { // merge env specific paths
26 | paths.common[k] = paths[env][k];
27 | }
28 |
29 | return {
30 |
31 | excludeInstrumentation: /^(?:test|node_modules|lib\/vendor)\//,
32 |
33 | useLoader: {
34 | 'host-node': 'requirejs'
35 | },
36 |
37 | loader: {
38 | paths: paths.common,
39 | map: {
40 | intern: {
41 | dojo: 'intern/node_modules/dojo',
42 | chai: 'intern/node_modules/chai/chai'
43 | },
44 | '*': {
45 | // mocks
46 | request: 'test/mocks/server/request',
47 | 'continuation-local-storage': 'test/mocks/server/continuation-local-storage',
48 | hapi: 'test/mocks/server/hapi',
49 | 'bundler': 'lazoBundle'
50 | }
51 | }
52 | }
53 |
54 | };
55 |
56 | });
--------------------------------------------------------------------------------
/test/unit/server/assetsProvider.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'intern/dojo/node!path',
9 | 'assetsProvider'
10 | ], function (bdd, chai, expect, sinon, sinonChai, utils, path, assetsProvider) {
11 | chai.use(sinonChai);
12 |
13 | with (bdd) {
14 | describe('Assets Provider', function () {
15 |
16 | before(function () {
17 | LAZO.FILE_REPO_PATH = path.resolve('test/application');
18 | });
19 |
20 | function getCtx() {
21 | return {
22 | _request: {
23 | raw: {
24 | req: {
25 | headers: {
26 | 'accept-language': 'en-US,en;q=0.8'
27 | }
28 | }
29 | }
30 | }
31 | };
32 | }
33 |
34 | it('should get the assets for a list of components', function () {
35 | var ctx = getCtx();
36 | var dfd = this.async();
37 | assetsProvider.get(['foo', 'bar', 'baz'], ctx, {
38 | success: function (assets) {
39 | expect(assets.foo).to.be.empty;
40 | expect(assets.baz).to.be.empty;
41 | expect(assets.bar['info.pdf']).to.be.equal('/components/bar/assets/info.pdf');
42 |
43 | expect(assets.bar.name).to.be.equal('Käthe Kollwitz');
44 | expect(assets.bar['img/logo.png']).to.be.equal('/components/bar/assets/en-US/img/logo.png');
45 | dfd.resolve();
46 | }
47 | });
48 | });
49 |
50 | it('should get the assets for an application', function () {
51 | var ctx = getCtx();
52 | var dfd = this.async();
53 | assetsProvider.get(['app'], ctx, {
54 | success: function (assets) {
55 | expect(assets.app['info.pdf']).to.be.equal('/app/assets/info.pdf');
56 | expect(assets.app.name).to.be.equal('Käthe Kollwitz');
57 | expect(assets.app['img/logo.png']).to.be.equal('/app/assets/en-US/img/logo.png');
58 | dfd.resolve();
59 | }
60 | });
61 | });
62 |
63 | });
64 | }
65 | });
--------------------------------------------------------------------------------
/test/unit/server/common/app.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'lazoApp'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, LazoApp) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('Lazo Application - server', function () {
14 |
15 | it('should add http vary params', function () {
16 |
17 | var lazoApp = new LazoApp({});
18 | lazoApp.isServer = true;
19 | var count = lazoApp.getHttpVaryParams().length;
20 | var app = lazoApp.addHttpVaryParam('user-agent');
21 |
22 | var params = lazoApp.getHttpVaryParams();
23 | expect(params.length).to.equal(count + 1);
24 | expect(params[0]).to.equal('user-agent');
25 | expect(app == lazoApp).to.be.true;
26 |
27 | });
28 |
29 | it('should add http headers', function () {
30 | var lazoApp = new LazoApp({});
31 | lazoApp.isServer = true;
32 | var count = lazoApp.getHttpHeaders().length;
33 | var app = lazoApp.addHttpHeader('X-Frame-Options', 'deny');
34 |
35 | var headers = lazoApp.getHttpHeaders();
36 | expect(headers.length).to.equal(count + 1);
37 | expect(headers[0].name).to.equal('X-Frame-Options');
38 | expect(headers[0].value).to.equal('deny');
39 | expect(headers[0].options).to.equal(null);
40 | expect(app == lazoApp).to.be.true;
41 | });
42 | });
43 | }
44 | });
--------------------------------------------------------------------------------
/test/unit/server/common/resolver/requireConfigure.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'resolver/requireConfigure'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, conf) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('requirejs configure', function () {
14 |
15 | it('get server configuration', function (done) {
16 | var dfd = this.async();
17 | var config;
18 | var options = {
19 | basePath: process.cwd(),
20 | baseUrl: 'some/path'
21 | };
22 |
23 | conf.get('server', options, function (err, conf) {
24 | config = conf;
25 |
26 | expect(config.baseUrl).to.be.equal('some/path');
27 | expect(config.context).to.be.equal('application');
28 | expect(config.map['*'].s.indexOf('\/server\/')).to.not.be.equal(-1);
29 |
30 | dfd.resolve();
31 | });
32 | });
33 |
34 | });
35 | }
36 | });
--------------------------------------------------------------------------------
/test/unit/server/common/utils/document.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'utils/document'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, doc) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('document utils', function () {
14 |
15 | it('set, get html tag', function () {
16 | var htmlTag = '';
17 |
18 | expect(doc.getHtmlTag()).to.be.equal('');
19 | doc.setHtmlTag(htmlTag);
20 | expect(doc.getHtmlTag()).to.be.equal(htmlTag);
21 | });
22 |
23 |
24 | it('set, get body class', function () {
25 | var bodyClass = 'foobar';
26 |
27 | expect(doc.getBodyClass()).to.be.equal('');
28 | doc.setBodyClass(bodyClass);
29 | expect(doc.getBodyClass()).to.be.equal(bodyClass);
30 | });
31 |
32 | it('add tag', function () {
33 | var metaTag;
34 | var tags;
35 |
36 | expect(doc.getTags().length).to.be.equal(0);
37 | doc.addTag('meta', { name: 'description', content: 'the most awesome web page ever' }, 'tag content');
38 | tags = doc.getTags();
39 | expect(tags.length).to.be.equal(1);
40 | metaTag = tags[0];
41 | expect(metaTag.name).to.be.equal('meta');
42 | expect(metaTag.content).to.be.equal('tag content');
43 | expect(metaTag.attributes.name).to.be.equal('description');
44 | expect(metaTag.attributes.content).to.be.equal('the most awesome web page ever');
45 | });
46 |
47 | it('add, get page tags', function () {
48 |
49 | var ctx = {
50 | _rootCtx: {},
51 | meta: {}
52 | };
53 |
54 | expect(doc.getPageTags(ctx, true).length).to.equal(0);
55 | doc.addPageTag(ctx, true, 'meta', { description: 'text' });
56 | expect(doc.getPageTags(ctx, true).length).to.equal(1);
57 | });
58 | });
59 | }
60 | });
--------------------------------------------------------------------------------
/test/unit/server/context.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'context'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, Context) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('Context', function () {
14 |
15 | it('common server', function () {
16 | var ctx = new Context({
17 | _request: {
18 | url: {
19 | pathname: 'foo/bar/baz'
20 | },
21 | raw: { // this is expected on the server
22 | req: {
23 | headers: {
24 | host: 'localhost:8080'
25 | }
26 | }
27 | },
28 | server: {
29 | info: {
30 | protocol: 'http'
31 | }
32 | }
33 | },
34 | headers: {
35 | host: 'localhost:8080'
36 | }
37 | });
38 |
39 | expect(ctx.location.pathname).to.be.equal('foo/bar/baz');
40 | });
41 |
42 | it('handles undefined host', function () {
43 | var ctx = new Context({
44 | _request: {
45 | url: {
46 | pathname: 'foo/bar/baz'
47 | },
48 | raw: { // this is expected on the server
49 | req: {
50 | headers: {
51 | host: 'localhost:8080'
52 | }
53 | }
54 | },
55 | server: {
56 | info: {
57 | protocol: 'http'
58 | }
59 | }
60 | },
61 | headers: {}
62 | });
63 |
64 | expect(ctx.location.host).to.be.undefined;
65 | });
66 |
67 | });
68 | }
69 | });
--------------------------------------------------------------------------------
/test/unit/server/forbidden.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'forbidden'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, forbidden) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('forbidden', function () {
14 |
15 | it('should not allow access to "server" directories', function () {
16 | expect(forbidden('/server/foo/bar')).to.be.true;
17 | expect(forbidden('/foo/server/bar')).to.be.true;
18 | expect(forbidden('/server')).to.be.true;
19 | });
20 |
21 | it('should not allow access to "node_modules" directories', function () {
22 | expect(forbidden('/node_modules/foo/bar')).to.be.true;
23 | expect(forbidden('/foo/node_modules/bar')).to.be.true;
24 | expect(forbidden('/node_modules')).to.be.true;
25 | });
26 |
27 | it('should allow access to common directories', function () {
28 | expect(forbidden('/lib/foo/bar')).to.be.false;
29 | expect(forbidden('/foo/client/bar')).to.be.false;
30 | expect(forbidden('/common')).to.be.false;
31 | });
32 |
33 | });
34 | }
35 | });
--------------------------------------------------------------------------------
/test/unit/server/httpResponse.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'httpResponse',
9 | 'lazoCtl'
10 | ], function (bdd, chai, expect, sinon, sinonChai, utils, httpResponse, LazoController) {
11 | chai.use(sinonChai);
12 |
13 | with (bdd) {
14 | describe('httpResponse', function () {
15 |
16 | var getController = function (options) {
17 | var ctlOptions = {
18 | name: 'home',
19 | ctx: {
20 | response: {
21 | statusCode: null,
22 | httpHeaders: [],
23 | varyParams: []
24 | }
25 | }
26 | };
27 |
28 | var MyController = LazoController.extend({});
29 | MyController.create('home', ctlOptions, options);
30 | };
31 |
32 | it('can add httpHeader', function () {
33 |
34 | var count = httpResponse.getHttpHeaders().length;
35 | httpResponse.addHttpHeader('X-Frame-Options', 'deny');
36 | expect(httpResponse.getHttpHeaders().length).to.equal(count + 1);
37 |
38 | });
39 |
40 | it('can add vary param', function () {
41 |
42 | var count = httpResponse.getVaryParams().length;
43 | httpResponse.addVaryParam('user-agent');
44 | expect(httpResponse.getVaryParams().length).to.equal(count + 1);
45 |
46 | });
47 |
48 | it('can merge http response data', function () {
49 |
50 | var dfd = this.async();
51 | getController({
52 | success: function (myController) {
53 | var controller = myController;
54 | var headerCount = httpResponse.getHttpHeaders().length;
55 | var varyCount = httpResponse.getVaryParams().length;
56 |
57 | controller.setHttpStatusCode(410);
58 | controller.addHttpHeader('X-XSS-Protection', '1; mode=block');
59 | controller.addHttpVaryParam('accept');
60 |
61 | var responseData = httpResponse.mergeHttpResponseData(controller);
62 | expect(responseData).to.exist;
63 | expect(responseData.statusCode).to.equal(410);
64 | expect(responseData.httpHeaders.length).to.equal(headerCount + 1);
65 | expect(responseData.varyParams.length).to.equal(varyCount + 1);
66 |
67 | dfd.resolve();
68 | },
69 | error: function () {
70 | dfd.reject();
71 | }
72 | });
73 |
74 | });
75 |
76 | });
77 | }
78 | });
--------------------------------------------------------------------------------
/test/unit/server/loader.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'l'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, loader) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('Server Loader', function () {
14 |
15 | it('should not load client files', function () {
16 | var dfd = this.async();
17 |
18 | loader.load('foo/client/bar', null, function (module) {
19 | expect(module).to.be.null;
20 | dfd.resolve();
21 | }, {});
22 |
23 | });
24 |
25 | it('should not load client configured file', function () {
26 | var dfd = this.async();
27 |
28 | LAZO.conf.requirejs = {
29 | client: {
30 | paths: {
31 | aClientModule: 'foo'
32 | }
33 | }
34 | };
35 |
36 | loader.load('aClientModule', null, function (module) {
37 | expect(module).to.be.null;
38 | delete LAZO.conf.requirejs;
39 | dfd.resolve();
40 | }, {paths:['.']});
41 |
42 | });
43 |
44 |
45 |
46 | });
47 | }
48 | });
--------------------------------------------------------------------------------
/test/unit/server/public/controller.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'lazoCtl'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, LazoController) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('Lazo Controller', function () {
14 |
15 | var getController = function (options) {
16 | var ctlOptions = {
17 | name: 'home',
18 | ctx: {
19 | response: {
20 | statusCode: null,
21 | httpHeaders: [],
22 | varyParams: []
23 | }
24 | }
25 | };
26 |
27 | var MyController = LazoController.extend({});
28 | MyController.create('home', ctlOptions, options);
29 | };
30 |
31 | it('should get/set the http status code', function () {
32 | var dfd = this.async();
33 | getController({
34 | success: function (controller) {
35 |
36 | expect(controller.getHttpStatusCode()).to.equal(200);
37 | var ctl = controller.setHttpStatusCode(410);
38 | expect(controller.getHttpStatusCode()).to.equal(410);
39 | expect(ctl == controller).to.be.true;
40 |
41 | dfd.resolve();
42 | },
43 | error: function () {
44 | dfd.reject();
45 | }
46 | });
47 | });
48 |
49 | it('should add http vary params', function () {
50 | var dfd = this.async();
51 | getController({
52 | success: function (controller) {
53 |
54 | expect(controller.getHttpVaryParams().length).to.equal(0);
55 | var ctl = controller.addHttpVaryParam('user-agent');
56 |
57 | var params = controller.getHttpVaryParams();
58 | expect(params.length).to.equal(1);
59 | expect(params[0]).to.equal('user-agent');
60 | expect(ctl == controller).to.be.true;
61 |
62 | dfd.resolve();
63 | },
64 | error: function () {
65 | dfd.reject();
66 | }
67 | });
68 | });
69 |
70 | it('should add http headers', function () {
71 | var dfd = this.async();
72 | getController({
73 | success: function (controller) {
74 |
75 | expect(controller.getHttpHeaders().length).to.equal(0);
76 | var ctl = controller.addHttpHeader('X-Frame-Options', 'deny');
77 |
78 | var headers = controller.getHttpHeaders();
79 | expect(headers.length).to.equal(1);
80 | expect(headers[0].name).to.equal('X-Frame-Options');
81 | expect(headers[0].value).to.equal('deny');
82 | expect(headers[0].options).to.equal(null);
83 | expect(ctl == controller).to.be.true;
84 |
85 | dfd.resolve();
86 | },
87 | error: function () {
88 | dfd.reject();
89 | }
90 | });
91 | });
92 |
93 | });
94 | }
95 | });
--------------------------------------------------------------------------------
/test/unit/server/streamHandler.skip.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!bdd',
3 | 'intern/chai!',
4 | 'intern/chai!expect',
5 | 'sinon',
6 | 'sinon-chai',
7 | 'test/unit/utils',
8 | 'handlers/stream'
9 | ], function (bdd, chai, expect, sinon, sinonChai, utils, StreamHandler) {
10 | chai.use(sinonChai);
11 |
12 | with (bdd) {
13 | describe('Stream Handler Test', function () {
14 |
15 | var request = {};
16 | var lazoSpy;
17 | var handler = {
18 | 'func' : function (req, options) {
19 | options.success("done");
20 | }
21 | };
22 |
23 | LAZO.require = function(path, cb){
24 | cb(handler);
25 | };
26 | lazoSpy = sinon.spy(LAZO, 'require');
27 |
28 | afterEach(function (done) {
29 | lazoSpy.reset();
30 | done();
31 | });
32 |
33 | it('Custom Action Without Callback and without component', function () {
34 | this.skip();
35 | var req = {
36 | params : {
37 | componentName: null,
38 | action: "func"
39 | },
40 | paylood: null,
41 | reply : function(){}
42 | };
43 |
44 | var spy = sinon.spy();
45 | StreamHandler(req, spy);
46 | expect(JSON.stringify("done")).to.be.equal(spy.args[0][0]);
47 | expect("app/server/utilActions").to.be.equal(lazoSpy.args[0][0][0]);
48 | });
49 |
50 | it('Custom Action Without Callback and with component', function () {
51 | this.skip();
52 | var req = {
53 | params : {
54 | compName: "test",
55 | action: "func"
56 | },
57 | paylood: null,
58 | reply : function(){}
59 | };
60 |
61 | var spy = sinon.spy();
62 | StreamHandler(req, spy);
63 | expect(JSON.stringify("done")).to.be.equal(spy.args[0][0]);
64 | expect("components/test/server/utilActions").to.be.equal(lazoSpy.args[0][0][0]);
65 | });
66 |
67 |
68 | });
69 | }
70 | });
--------------------------------------------------------------------------------
/test/unit/utils.js:
--------------------------------------------------------------------------------
1 | define(['test/mocks/lazo'], function (lazo) {
2 |
3 | function createView(ctl, id, LazoView, _) {
4 | var el;
5 |
6 | el = LAZO.app.isClient ? $('
') : null;
7 | return new LazoView({
8 | el: el,
9 | templateEngine: 'micro',
10 | getTemplate: function (options) {
11 | options.success('I am a template!');
12 | },
13 | ctl: ctl
14 | });
15 | }
16 |
17 | // these paths do not exist until after the intern configuration has
18 | // been set. requirejs will be defined at this point.
19 | return {
20 |
21 | setUpApp: function (callback) {
22 | requirejs(['underscore'], function (_) {
23 | // var template = _.template('I am a template!');
24 | LAZO.require = requirejs;
25 | LAZO.app.getDefaultTemplateEngineName = function () {};
26 | LAZO.app.getTemplateEngine = function () {
27 | return {
28 | compile: function (template) {
29 | return _.template(template);
30 | },
31 | execute: function (compiledTemplate, data) {
32 | return compiledTemplate(data);
33 | },
34 | engine: _.template
35 | };
36 | };
37 |
38 | callback();
39 | });
40 | },
41 |
42 | createCtlTree: function (callback) {
43 |
44 | function _getEl() {
45 | if (LAZO.app.isClient) {
46 | return this.$el || (this.$el = $('
'));
47 | }
48 | }
49 |
50 | requirejs(['lazoView', 'underscore'], function (LazoView, _) {
51 | var childCtl;
52 | var ctl = {
53 | currentView: null,
54 | children: {
55 | foo: []
56 | },
57 | _getEl: _getEl
58 | };
59 |
60 | for (var i = 0; i < 3; i++) {
61 | if (!i) {
62 | ctl.currentView = createView(ctl, i, LazoView, _);
63 | ctl.currentView.getTemplate = function (options) {
64 | options.success('
');
65 | };
66 | ctl.cid = i;
67 | ctl.name = 'name' + i;
68 | ctl.ctx = {};
69 | } else {
70 | childCtl = {
71 | currentView: null,
72 | cid: i,
73 | name: 'name' + i,
74 | ctx: {},
75 | _getEl: _getEl
76 | };
77 | childCtl.currentView = createView(childCtl, i, LazoView, _);
78 | ctl.children.foo.push(childCtl);
79 | }
80 | }
81 |
82 | callback(ctl);
83 | });
84 | }
85 |
86 | };
87 |
88 | });
--------------------------------------------------------------------------------
/version.js:
--------------------------------------------------------------------------------
1 | module.exports = function () {
2 | var path = require('path');
3 | var fs = require('fs');
4 | var lazoPath = path.dirname(module.filename);
5 | var packageJson = JSON.parse(fs.readFileSync(path.normalize(lazoPath + '/package.json'), 'utf8'));
6 | return 'v' + packageJson.version;
7 | }
--------------------------------------------------------------------------------