Skip to content

Commit

Permalink
Individual sandbox injection into widgets
Browse files Browse the repository at this point in the history
Turned sandbox modules into sandbox factories. Added RequireJS path
mapping to replace widget sandboxes with individual sandbox instances.

Allowing sandbox extensions through overriding a factory method in Core.

Added per-sandbox logging as first example of sandbox-specific
functionality.
  • Loading branch information
atesgoral committed Nov 20, 2012
1 parent c737d0d commit a9c821b
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 107 deletions.
4 changes: 1 addition & 3 deletions spec/SpecRunner.html
Expand Up @@ -60,9 +60,7 @@
// Set the base library
dom: 'src/aura/lib/dom',

core: 'src/extensions/backbone/core',
perms: 'src/extensions/backbone/permissions',
sandbox: 'src/extensions/backbone/sandbox',
backboneSandbox: 'src/extensions/backbone/sandbox',

aura_base: 'src/aura/base',
aura_core: 'src/aura/core',
Expand Down
6 changes: 5 additions & 1 deletion src/apps/demo/js/app.js
Expand Up @@ -9,9 +9,13 @@ if (typeof Object.create !== 'function') {

// Starts main modules
// Publishing from core because that's the way that Nicholas did it...
define(['core'], function(core) {
define(['aura_core', 'backboneSandbox'], function(core, backboneSandbox) {
'use strict';

core.getSandbox = function (sandbox) {
return backboneSandbox.extend(sandbox);
};

core.start([{
channel: 'todos',
options: {
Expand Down
44 changes: 39 additions & 5 deletions src/aura/core.js
Expand Up @@ -9,7 +9,7 @@
// * [Nicholas Zakas: Scalable JavaScript Application Architecture](http://www.youtube.com/watch?v=vXjVFPosQHw&feature=youtube_gdata_player)
// * [Writing Modular JavaScript: New Premium Tutorial](http://net.tutsplus.com/tutorials/javascript-ajax/writing-modular-javascript-new-premium-tutorial/)
// include 'deferred' if using zepto
define(['aura_base'], function(base) {
define(['aura_base', 'aura_sandbox'], function(base, sandbox) {

'use strict';

Expand All @@ -18,6 +18,7 @@ define(['aura_base'], function(base) {
var emitQueue = [];
var isWidgetLoading = false;
var WIDGETS_PATH = 'widgets'; // Path to widgets
var sandboxSerial = 0; // For unique widget sandbox module names

// Load in the base library, such as Zepto or jQuery. the following are
// required for Aura to run:
Expand Down Expand Up @@ -106,6 +107,13 @@ define(['aura_base'], function(base) {
return WIDGETS_PATH;
};

// Handle logging request from channel
core.log = function(channel) {
var args = Array.prototype.slice.call(arguments, 0);
args[0] = '[' + channel + ']';
console.log.apply(console, args);
};

// Subscribe to an event
//
// * **param:** {string} channel Event name
Expand Down Expand Up @@ -241,7 +249,8 @@ define(['aura_base'], function(base) {
var l = list.length;
var promises = [];

function load(file, options) {
function load(channel, options) {
var file = decamelize(channel);
var dfd = core.data.deferred();
var widgetsPath = core.getWidgetsPath();
var requireConfig = require.s.contexts._.config;
Expand All @@ -250,7 +259,33 @@ define(['aura_base'], function(base) {
widgetsPath = requireConfig.paths.widgets;
}

require([widgetsPath + '/' + file + '/main'], function(main) {
var widgetPath = widgetsPath + '/' + file;
// Unique sandbox module to be used by this widget
var widgetSandboxPath = 'sandbox$' + sandboxSerial++;

// Construct RequireJS map configuration
var sandboxMap = {};
// Every module whose path prefix matches widgetSandbox will get the unique sandbox for this widget
sandboxMap[widgetPath] = {
sandbox: widgetSandboxPath
};

var req = require.config({
map: sandboxMap
});

// Instantiate unique sandbox
var widgetSandbox = sandbox.create(core, channel);

// Apply application extensions
if (core.getSandbox) {
widgetSandbox = core.getSandbox(widgetSandbox, channel);
}

// Define the unique sandbox
define(widgetSandboxPath, widgetSandbox);

req([widgetPath + '/main'], function(main) {
try {
main(options);
} catch (e) {
Expand All @@ -277,9 +312,8 @@ define(['aura_base'], function(base) {

for (; i < l; i++) {
var widget = list[i];
var file = decamelize(widget.channel);

promises.push(load(file, widget.options || {}));
promises.push(load(widget.channel, widget.options || {}));
}

core.data.when.apply($, promises).done(core.emptyEmitQueue);
Expand Down
109 changes: 59 additions & 50 deletions src/aura/sandbox.js
Expand Up @@ -5,67 +5,76 @@
// Note: Handling permissions/security is optional here
// The permissions check can be removed
// to just use the mediator directly.
define(['aura_core', 'aura_perms'], function(mediator, permissions) {
define(['aura_perms'], function(permissions) {
'use strict';

var sandbox = {};
return {
create: function(mediator, channel) {
var sandbox = {};

// * **param:** {string} subscriber Module name
// * **param:** {string} channel Event name
// * **param:** {object} callback Module
sandbox.on = function(channel, subscriber, callback, context) {
if (permissions.validate(channel, subscriber)) {
mediator.on(channel, subscriber, callback, context || this);
}
};
sandbox.log = function() {
var args = Array.prototype.concat.apply([channel], arguments);
mediator.log.apply(mediator, args);
};

// * **param:** {string} channel Event name
sandbox.emit = function(channel) {
mediator.emit.apply(mediator, arguments);
};
// * **param:** {string} subscriber Module name
// * **param:** {string} channel Event name
// * **param:** {object} callback Module
sandbox.on = function(channel, subscriber, callback, context) {
if (permissions.validate(channel, subscriber)) {
mediator.on(channel, subscriber, callback, context || this);
}
};

// * **param:** {Object/Array} an array with objects or single object containing channel and element
sandbox.start = function(list) {
mediator.start.apply(mediator, arguments);
};
// * **param:** {string} channel Event name
sandbox.emit = function(channel) {
mediator.emit.apply(mediator, arguments);
};

// * **param:** {string} channel Event name
// * **param:** {string} el Element name
sandbox.stop = function(channel, el) {
mediator.stop.apply(mediator, arguments);
};
// * **param:** {Object/Array} an array with objects or single object containing channel and element
sandbox.start = function(list) {
mediator.start.apply(mediator, arguments);
};

sandbox.dom = {
// * **param:** {string} selector CSS selector for the element
// * **param:** {string} context CSS selector for the context in which
// to search for selector
// * **returns:** {object} Found elements or empty array
find: function(selector, context) {
return mediator.dom.find(selector, context);
}
};
// * **param:** {string} channel Event name
// * **param:** {string} el Element name
sandbox.stop = function(channel, el) {
mediator.stop.apply(mediator, arguments);
};

sandbox.events = {
// * **param:** {object} context Element to listen on
// * **param:** {string} events Events to trigger, e.g. click, focus, etc.
// * **param:** {string} selector Items to listen for
// * **param:** {object} data
// * **param:** {object} callback
listen: function(context, events, selector, callback) {
mediator.events.listen(context, events, selector, callback);
},
bindAll: mediator.events.bindAll
};
sandbox.dom = {
// * **param:** {string} selector CSS selector for the element
// * **param:** {string} context CSS selector for the context in which
// to search for selector
// * **returns:** {object} Found elements or empty array
find: function(selector, context) {
return mediator.dom.find(selector, context);
}
};

sandbox.util = {
each: mediator.util.each,
extend: mediator.util.extend
};
sandbox.events = {
// * **param:** {object} context Element to listen on
// * **param:** {string} events Events to trigger, e.g. click, focus, etc.
// * **param:** {string} selector Items to listen for
// * **param:** {object} data
// * **param:** {object} callback
listen: function(context, events, selector, callback) {
mediator.events.listen(context, events, selector, callback);
},
bindAll: mediator.events.bindAll
};

sandbox.data = mediator.data;
sandbox.util = {
each: mediator.util.each,
extend: mediator.util.extend
};

sandbox.template = mediator.template;
sandbox.data = mediator.data;

return sandbox;
sandbox.template = mediator.template;

return sandbox;

}
};
});
3 changes: 1 addition & 2 deletions src/config.js
Expand Up @@ -80,8 +80,7 @@ define(function() {
widgets: "../../../widgets",

// Backbone Extension
core: '../../../extensions/backbone/core',
sandbox: '../../../extensions/backbone/sandbox',
backboneSandbox: '../../../extensions/backbone/sandbox',
text: '../../../extensions/backbone/lib/text',
backbone: '../../../extensions/backbone/lib/backbone',
i18n: '../../../i18n',
Expand Down
12 changes: 0 additions & 12 deletions src/extensions/backbone/core.js

This file was deleted.

52 changes: 27 additions & 25 deletions src/extensions/backbone/sandbox.js
@@ -1,38 +1,40 @@
// ## Sandbox Extension
// @fileOverview Extend the aura-sandbox (facade pattern)
// @todo This is a stupid place to include jquery ui
define(['aura_sandbox', 'core', 'perms', 'jquery_ui'], function(sandbox, core, perms) {
define(['perms', 'backbone', 'localstorage', 'jquery_ui'], function(perms, Backbone, Store) {
'use strict';

var auraSandbox = Object.create(sandbox);
auraSandbox.data.Store = core.data.Store;
auraSandbox.mvc = {};
auraSandbox.widgets = {};
return {
extend: function(sandbox, channel) {
sandbox.data.Store = Store;
sandbox.mvc = {};
sandbox.widgets = {};

auraSandbox.mvc.View = function(view) {
return core.mvc.View.extend(view);
};

auraSandbox.mvc.Model = function(model) {
return core.mvc.Model.extend(model);
};
sandbox.mvc.View = function(view) {
return Backbone.View.extend(view);
};

auraSandbox.mvc.Collection = function(collection) {
return core.mvc.Collection.extend(collection);
};
sandbox.mvc.Model = function(model) {
return Backbone.Model.extend(model);
};

auraSandbox.mvc.Router = function(router) {
return core.mvc.Router.extend(router);
};
sandbox.mvc.Collection = function(collection) {
return Backbone.Collection.extend(collection);
};

auraSandbox.widgets.stop = function(channel, el) {
return sandbox.stop.apply(this, arguments);
};
sandbox.mvc.Router = function(router) {
return Backbone.Router.extend(router);
};

auraSandbox.widgets.start = function(channel, options) {
return sandbox.start.apply(this, arguments);
};
sandbox.widgets.stop = function(channel, el) {
return sandbox.stop.apply(this, arguments);
};

return auraSandbox;
sandbox.widgets.start = function(channel, options) {
return sandbox.start.apply(this, arguments);
};

return sandbox;
}
};
});
4 changes: 2 additions & 2 deletions src/widgets/boilerplate/main.js
Expand Up @@ -9,11 +9,11 @@ define(['sandbox', './views/app'], function(sandbox, AppView) {
sandbox.emit('bootstrap', 'boilerplate');

sandbox.on('bootstrap', 'boilerplate', function(from) {
console.log('Boilerplate-bootstrap message from from: ' + from);
sandbox.log('Boilerplate-bootstrap message from: ' + from);
});

sandbox.on('*', 'calendar', function(from){
console.log('Wildcard event from:', from);
sandbox.log('Wildcard event from:', from);
});

};
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/calendar/main.js
Expand Up @@ -16,8 +16,8 @@ define(['sandbox', './views/app', './collections/events', 'fullcalendar'], funct
sandbox.emit('bootstrap', 'calendar');
sandbox.emit('*', 'calendar', 'bubblegum');
sandbox.on('bootstrap', 'calendar', function(from, data) {
console.log('Calendar-bootstrap message from from: ' + from);
console.log('Additional data:', data);
sandbox.log('Calendar-bootstrap message from: ' + from);
sandbox.log('Additional data:', data);
sandbox.emit('*','controls');
});
};
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/controls/main.js
Expand Up @@ -9,10 +9,10 @@ define(['sandbox', './views/app'], function(sandbox, AppView) {
sandbox.emit('bootstrap', 'controls');

sandbox.on('bootstrap', 'controls', function(from) {
console.log('Controls-bootstrap message from from: ' + from);
sandbox.log('Controls-bootstrap message from: ' + from);
});
sandbox.on('*', 'calendar', function(from){
console.log('A wildcard was caught from:', from);
sandbox.log('A wildcard was caught from:', from);
});

};
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/router/main.js
Expand Up @@ -24,11 +24,11 @@ define(['sandbox', 'underscore'], function(sandbox, _) {

sandbox.emit('bootstrap', 'router');
sandbox.on('bootstrap', function(from) {
console.log('Router-bootstrap message from: ' + from);
sandbox.log('Router-bootstrap message from: ' + from);
});

sandbox.on('router', 'router', function() {
console.log('Route in router widget: ', Array.prototype.slice.call(arguments));
sandbox.log('Route in router widget: ', Array.prototype.slice.call(arguments));
});
};

Expand Down
2 changes: 1 addition & 1 deletion src/widgets/todos/main.js
Expand Up @@ -8,7 +8,7 @@ define(['sandbox', './views/app'], function(sandbox, AppView) {

sandbox.emit('bootstrap', 'todos');
sandbox.on('bootstrap', 'todos', function(from) {
console.log('Todos-bootstrap message from from: ' + from);
sandbox.log('Todos-bootstrap message from: ' + from);
});
};

Expand Down

0 comments on commit a9c821b

Please sign in to comment.