Commit 8152c9f8 authored by Pascal Hartig's avatar Pascal Hartig

Merge pull request #556 from cujojs/cujo-lib-update

Update to latest cujo releases
parents 9972d21e b8d9ae5c
# Cujojs TodoMVC
# cujoJS TodoMVC
[Cujojs](http://cujojs.com) is an *architectural framework* for building highly modular, scalable, maintainable applications in Javascript. It provides architectural plumbing, such as modules (AMD and CommonJS), declarative application composition, declarative connections, and aspect oriented programming.
[cujoJS](http://cujojs.com) is an *architectural framework* for building highly modular, scalable, maintainable applications in Javascript. It provides architectural plumbing, such as modules (AMD and CommonJS), declarative application composition, declarative connections, and aspect oriented programming.
It is not a typical MV\* framework, although it does provide MV\* building blocks, such as templating and data binding.
## Highlights:
Some things we feel are interesting about cujojs's TodoMVC as compared to other implementations:
Some things we feel are interesting about cujoJS's TodoMVC as compared to other implementations:
* Application composition is separate from application logic
* Code is *highly* modular and organized into components, each consisting of
......
......@@ -3,5 +3,3 @@ TODO before release
* implement filters (using routing or another method)
* build javascript using cram.js
* use curl's preloads feature rather than .next() in run.js
* use a theme.css file
......@@ -2,25 +2,25 @@
define(function () {
'use strict';
var textProp;
var textProp, updateRemainingCount;
/**
* Self-optimizing function to set the text of a node
*/
var updateRemainingCount = function () {
updateRemainingCount = function (nodes, value) {
// sniff for proper textContent property
textProp = 'textContent' in document.documentElement ? 'textContent' : 'innerText';
// resume normally
updateRemainingCount = setTextProp;
updateRemainingCount(arguments);
updateRemainingCount(nodes, value);
};
var setTextProp = function (nodes, value) {
function setTextProp(nodes, value) {
for (var i = 0; i < nodes.length; i++) {
nodes[i][textProp] = '' + value;
}
};
}
return {
/**
......@@ -84,8 +84,10 @@ define(function () {
* Check/uncheck all todos
*/
toggleAll: function () {
var todos = this.todos;
var complete = this.masterCheckbox.checked;
var todos, complete;
todos = this.todos;
complete = this.masterCheckbox.checked;
todos.forEach(function (todo) {
todo.complete = complete;
......@@ -99,8 +101,10 @@ define(function () {
* checked or unchecked.
*/
updateCount: function () {
var total = 0;
var checked = 0;
var total, checked;
total = 0;
checked = 0;
this.todos.forEach(function (todo) {
total++;
......@@ -128,4 +132,5 @@ define(function () {
updateRemainingCount(this.remainingNodes, remaining);
}
};
});
<footer id="info">
<p>${edit}</p>
<p>${templateBy} <a href="http://github.com/sindresorhus">Sindre Sorhus</a></p>
<p>${createdBy} <a href="http://cujojs.com">cujojs</a></p>
<p>${createdBy} <a href="http://cujojs.com">cujoJS</a></p>
<p>${partOf} <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
......@@ -10,7 +10,7 @@
<label></label>
<button class="destroy"></button>
</div>
<input class="edit" value="">
<form><input class="edit" name="text"></form>
</li>
</ul>
</section>
......@@ -10,8 +10,8 @@ define({
// Render and insert the create view
createView: {
render: {
template: { module: 'text!create/template.html' },
replace: { module: 'i18n!create/strings' }
template: { module: 'text!app/create/template.html' },
replace: { module: 'i18n!app/create/strings' }
},
insert: { first: 'root' }
},
......@@ -26,9 +26,9 @@ define({
// data and mapping data fields to the DOM
listView: {
render: {
template: { module: 'text!list/template.html' },
replace: { module: 'i18n!list/strings' },
css: { module: 'css!list/structure.css' }
template: { module: 'text!app/list/template.html' },
replace: { module: 'i18n!app/list/strings' },
css: { module: 'css!app/list/structure.css' }
},
insert: { after: 'createView' },
bind: {
......@@ -38,7 +38,7 @@ define({
text: 'label, .edit',
complete: [
'.toggle',
{ attr: 'classList', handler: { module: 'list/setCompletedClass' } }
{ attr: 'classList', handler: { module: 'app/list/setCompletedClass' } }
]
}
}
......@@ -48,9 +48,9 @@ define({
// filters, and clear completed button.
controlsView: {
render: {
template: { module: 'text!controls/template.html' },
replace: { module: 'i18n!controls/strings' },
css: { module: 'css!controls/structure.css' }
template: { module: 'text!app/controls/template.html' },
replace: { module: 'i18n!app/controls/strings' },
css: { module: 'css!app/controls/structure.css' }
},
insert: { after: 'listView' }
},
......@@ -59,8 +59,8 @@ define({
// is still fully internationalized.
footerView: {
render: {
template: { module: 'text!footer/template.html' },
replace: { module: 'i18n!footer/strings' }
template: { module: 'text!app/footer/template.html' },
replace: { module: 'i18n!app/footer/strings' }
},
insert: { after: 'root' }
},
......@@ -81,10 +81,10 @@ define({
todos: {
create: {
module: 'cola/Hub',
module: 'cola/Collection',
args: {
strategyOptions: {
validator: { module: 'create/validateTodo' }
validator: { module: 'app/create/validateTodo' }
}
}
},
......@@ -100,11 +100,11 @@ define({
// view controllers. Since this is a relatively simple application,
// a single controller fits well.
todoController: {
prototype: { create: 'controller' },
create: 'app/controller',
properties: {
todos: { $ref: 'todos' },
createTodo: { compose: 'parseForm | todos.add' },
createTodo: { compose: 'form.getValues | todos.add' },
removeTodo: { compose: 'todos.remove' },
updateTodo: { compose: 'todos.update' },
......@@ -123,7 +123,8 @@ define({
'change:.toggle': 'updateTodo',
'click:#toggle-all': 'toggleAll',
'dblclick:label': 'todos.edit',
'change,focusout:.edit': 'todos.submit' // also need way to submit on [enter]
'change,focusout:.edit': 'todos.submit',
'submit:form': 'todos.submit'
},
controlsView: {
'click:#clear-completed': 'removeCompleted'
......@@ -139,9 +140,9 @@ define({
}
},
parseForm: { module: 'cola/dom/formToObject' },
cleanTodo: { module: 'create/cleanTodo' },
generateMetadata: { module: 'create/generateMetadata' },
form: { module: 'cola/dom/form' },
cleanTodo: { module: 'app/create/cleanTodo' },
generateMetadata: { module: 'app/create/generateMetadata' },
toggleEditingState: {
create: {
......
......@@ -2,21 +2,20 @@
(function (curl) {
'use strict';
var config = {
baseUrl: 'app',
paths: {
theme: '../theme',
curl: '../lib/curl/src/curl'
},
pluginPath: 'curl/plugin',
curl({
main: 'wire!app/main',
packages: [
{ name: 'wire', location: '../lib/wire', main: 'wire' },
{ name: 'when', location: '../lib/when', main: 'when' },
{ name: 'aop', location: '../lib/aop', main: 'aop' },
{ name: 'cola', location: '../lib/cola', main: 'cola' },
{ name: 'poly', location: '../lib/poly', main: 'poly' }
]
};
{ name: 'curl', location: 'bower_components/curl/src/curl' },
{ name: 'wire', location: 'bower_components/wire', main: 'wire' },
{ name: 'when', location: 'bower_components/when', main: 'when' },
{ name: 'meld', location: 'bower_components/meld', main: 'meld' },
{ name: 'cola', location: 'bower_components/cola', main: 'cola' },
{ name: 'poly', location: 'bower_components/poly', main: 'poly' }
],
preloads: ['poly/string', 'poly/array'],
// Turn off i18n locale sniffing. Change or remove this line if you want to
// test specific locales or try automatic locale-sniffing.
locale: false
});
curl(config, ['poly/string', 'poly/array']).next(['wire!main']);
})(curl);
{
"name": "todomvc-cujo",
"name": "todomvc-cujoJS",
"version": "0.0.0",
"dependencies": {
"todomvc-common": "~0.1.4",
"curl": "~0.7.3",
"cola": "latest",
"poly": "~0.5.1",
"when": "~2.0.1",
"when": "~2.1.0",
"wire": "~0.9.4",
"meld": "~1.3.0"
}
......
[submodule "test/curl"]
path = test/curl
url = https://unscriptable@github.com/cujojs/curl.git
[submodule "test/util"]
path = test/util
url = https://unscriptable@github.com/cujojs/util.git
[submodule "support/when"]
path = support/when
url = https://github.com/cujojs/when.git
/**
* Collection
*/
(function(define) {
define(function(require) {
var Base, resolver, eventTypes, simpleStrategy;
Base = require('./hub/Base');
resolver = require('./collectionAdapterResolver');
simpleStrategy = require('./network/strategy/default');
eventTypes = extend(Base.prototype.eventTypes, {
// collection item events. most of these come with data. devs can
// decide to use these events for their own purposes or send
// different data than described here, the following list outlines
// the intended behavior.
add: 1, // data == item added
remove: 1, // data == item removed
target: 1, // data == item targeted TODO: rename this to "point"?
// multi-item events
select: 1, // select an item (data == item)
unselect: 1, // deselect an item (data == item)
// batch events
collect: 1, // start of batch mode (until abort or submit) (data = batch purpose)
deliver: 1 // collected items (data = batch purpose with collected items array as property)
});
function Collection(options) {
Base.call(this, options);
if(!options) {
options = {};
}
this.strategy = options.strategy;
if (!this.strategy) this.strategy = simpleStrategy(options.strategyOptions);
}
Collection.prototype = Object.create(Base.prototype, {
eventTypes: { value: eventTypes },
resolver: { value: resolver },
forEach: {
value: function forEach(lambda) {
var provider = this.getProvider();
return provider && provider.forEach(lambda);
}
},
findItem: {
value: function (anything) {
var info = this._findItemFor(anything);
return info && info.item;
}
},
findNode: {
value: function (anything) {
var info = this._findNodeFor(anything);
return info && info.node;
}
},
getProvider: {
value: function () {
var a, i = this.adapters.length;
while(a = this.adapters[--i]) {
if(a.provide) return a;
}
}
},
_findNodeFor: {
value: function (anything) {
var node, i, adapters, adapter;
adapters = this.adapters;
// loop through adapters that have the findNode() method
// to try to find out which adapter and which node
i = 0;
while (!node && (adapter = adapters[i++])) {
if (adapter.findNode) {
node = adapter.findNode(anything);
}
}
return node && { node: node };
}
}
});
return Collection;
function extend(base, mixin) {
var extended = Object.create(base);
for(var p in mixin) {
extended[p] = mixin[p];
}
return extended;
}
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
/**
* Model
* @author: brian
*/
(function(define) {
define(function(require) {
var Base, resolver, defaultModelStrategy;
Base = require('./hub/Base');
resolver = require('./objectAdapterResolver');
defaultModelStrategy = require('./network/strategy/defaultModel');
function Model(options) {
Base.call(this, options);
if(!options) {
options = {};
}
this.strategy = options.strategy;
if (!this.strategy) this.strategy = defaultModelStrategy(options.strategyOptions);
}
Model.prototype = Object.create(Base.prototype, {
resolver: { value: resolver }
});
return Model;
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
......@@ -14,13 +14,23 @@ define(function (require) {
*/
function ObjectAdapter(obj, options) {
if(!options) {
options = {};
}
this._obj = obj;
this._options = options;
if('provide' in options) {
this.provide = options.provide;
}
}
ObjectAdapter.prototype = {
provide: true,
update: function (item) {
var self = this;
......@@ -39,6 +49,18 @@ define(function (require) {
},
properties: function(lambda) {
var self = this;
return when(this._obj, function(obj) {
function properties(l) {
l(obj);
}
self.properties = properties;
return properties(lambda);
});
},
getOptions: function () {
return this._options;
}
......@@ -67,4 +89,4 @@ define(function (require) {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(require); }
));
));
\ No newline at end of file
......@@ -98,4 +98,4 @@ define(function (require) {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(require); }
));
));
\ No newline at end of file
(function (define) {
define(function () {
"use strict";
return {
/**
* Finds an adapter for the given object and the role.
* This is overly simplistic for now. We can replace this
* resolver later.
* @param object {Object}
* @description Loops through all Adapters registered with
* AdapterResolver.register, calling each Adapter's canHandle
* method. Adapters added later are found first.
*/
resolve: function(object) {
var adapters, i, Adapter;
adapters = this.adapters;
if (adapters) {
i = adapters.length;
while ((Adapter = adapters[--i])) {
if (Adapter.canHandle(object)) {
return Adapter;
}
}
}
},
register: function(Adapter) {
var adapters = this.adapters;
if(adapters.indexOf(Adapter) === -1) {
adapters.push(Adapter);
}
}
};
});
}(
typeof define == 'function'
? define
: function (factory) { module.exports = factory(); }
));
\ No newline at end of file
{
"name": "cola",
"version": "0.0.0",
"commit": "27b8c7e8fe88feef62a746d29d30af945dcb244d",
"repository": {
"type": "git",
"url": "git://github.com/cujojs/cola"
}
}
\ No newline at end of file
......@@ -21,9 +21,12 @@ function(when, propertiesKey, byProperty) {
defaultQuerySelectorAll = { $ref: 'dom.all!' };
defaultOn = { $ref: 'on!' };
function initBindOptions(incomingOptions, pluginOptions) {
function initBindOptions(incomingOptions, pluginOptions, resolver) {
var options, identifier, comparator;
if(resolver.isRef(incomingOptions)) {
incomingOptions = { to: incomingOptions };
}
options = copyOwnProps(incomingOptions, pluginOptions);
if(!options.querySelector) {
......@@ -56,7 +59,7 @@ function(when, propertiesKey, byProperty) {
function doBind(facet, options, wire) {
var target = facet.target;
return when(wire(initBindOptions(facet.options, options)),
return when(wire(initBindOptions(facet.options, options, wire.resolver)),
function(options) {
var to = options.to;
if (!to) throw new Error('wire/cola: "to" must be specified');
......@@ -78,18 +81,24 @@ function(when, propertiesKey, byProperty) {
};
return {
wire$plugin: function(ready, destroyed, pluginOptions) {
wire$plugin: function(pluginOptions) {
var options, p;
var options = {};
options = {};
for(var p in pluginOptions) {
if(!(p in excludeOptions)) {
options[p] = pluginOptions[p];
if(arguments.length) {
pluginOptions = arguments[arguments.length-1];
for(p in pluginOptions) {
if(!(p in excludeOptions)) {
options[p] = pluginOptions[p];
}
}
}
function bindFacet(resolver, facet, wire) {
when.chain(doBind(facet, options, wire), resolver);
resolver.resolve(doBind(facet, options, wire));
}
return {
......
/**
* collectionAdapterResolver
* @author: brian
*/
(function(define) {
define(function(require) {
var adapterResolver = require('./adapterResolver');
return Object.create(adapterResolver, {
adapters: { value: [
require('./adapter/Array'),
require('./dom/adapter/NodeList'),
require('./adapter/Query')
]}
});
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
......@@ -19,4 +19,4 @@ define(function (require) {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(require); }
));
));
\ No newline at end of file
......@@ -15,4 +15,4 @@ define(function () {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -5,9 +5,10 @@
define(function (require) {
"use strict";
var bindingHandler;
var bindingHandler, guess;
bindingHandler = require('../bindingHandler');
guess = require('../guess');
/**
* Creates a cola adapter for interacting with dom nodes. Be sure to
......@@ -21,12 +22,12 @@ define(function (require) {
this._rootNode = rootNode;
// set options
if (!options.bindings) options.bindings = {};
options.bindings = guessBindingsFromDom(this._rootNode, options);
this._options = options;
this._handlers = {};
this._createItemToDomHandlers();
this._createItemToDomHandlers(options.bindings);
}
NodeAdapter.prototype = {
......@@ -51,6 +52,10 @@ define(function (require) {
});
},
properties: function(lambda) {
lambda(this._item);
},
_itemToDom: function (item, hash) {
var p, handler;
for (p in hash) {
......@@ -59,10 +64,9 @@ define(function (require) {
}
},
_createItemToDomHandlers: function () {
var bindings, creator;
_createItemToDomHandlers: function (bindings) {
var creator;
bindings = this._options.bindings;
creator = bindingHandler(this._rootNode, this._options);
Object.keys(bindings).forEach(function (b) {
......@@ -85,9 +89,32 @@ define(function (require) {
return NodeAdapter;
function guessBindingsFromDom(rootNode, options) {
var nodeFinder, nodes, bindings;
bindings = options.bindings || {};
nodeFinder = options.nodeFinder || options.querySelectorAll || options.querySelector;
nodes = nodeFinder('[name],[data-cola-binding]', rootNode);
if(nodes) {
Array.prototype.forEach.call(nodes, function(n) {
var name, attr;
attr = n.name ? 'name' : 'data-cola-binding';
name = guess.getNodePropOrAttr(n, attr);
if(name && !(name in bindings)) {
bindings[name] = '[' + attr + '="' + name + '"]';
}
});
}
return bindings;
}
});
}(
typeof define == 'function'
? define
: function (factory) { module.exports = factory(require); }
));
));
\ No newline at end of file
......@@ -4,7 +4,7 @@ define(function(require) {
var SortedMap, classList, NodeAdapter,
defaultIdAttribute, defaultTemplateSelector, listElementsSelector,
colaListBindingStates, undef;
colaListBindingStates, allBindingStates, undef;
SortedMap = require('../../SortedMap');
classList = require('../classList');
......@@ -20,12 +20,17 @@ define(function(require) {
unbound: 'cola-list-unbound'
};
allBindingStates = Object.keys(colaListBindingStates).map(function(key) {
return colaListBindingStates[key];
}).join(' ');
/**
* Manages a collection of dom trees that are synced with a data
* collection.
* @constructor
* @param rootNode {Node} node to serve as a template for items
* in the collection / list.
* @param {object} options
* @param options.comparator {Function} comparator function to use for
* ordering nodes
* @param [options.containerNode] {Node} optional parent to all itemNodes. If
......@@ -295,14 +300,24 @@ define(function(require) {
},
_checkBoundState: function () {
var state, isBound, isEmpty;
state = {};
var states, isBound, isEmpty;
states = [];
isBound = this._itemCount != null;
isEmpty = this._itemCount == 0;
state[colaListBindingStates.unbound] = !isBound;
state[colaListBindingStates.empty] = isEmpty;
state[colaListBindingStates.bound] = isBound && !isEmpty;
classList.setClassSet(this._rootNode, state);
if(!isBound) {
states.push(colaListBindingStates.unbound);
}
if(isEmpty) {
states.push(colaListBindingStates.empty);
}
if(isBound && !isEmpty) {
states.push(colaListBindingStates.bound);
}
setBindingStates(states.join(' '), this._rootNode);
}
};
......@@ -312,6 +327,10 @@ define(function(require) {
return obj && obj.tagName && obj.insertBefore && obj.removeChild;
};
function setBindingStates(states, node) {
node.className = classList.addClass(states, classList.removeClass(allBindingStates, node.className));
}
function findTemplateNode (root, options) {
var useBestGuess, node;
......@@ -343,4 +362,4 @@ define(function(require) {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(require); }
));
));
\ No newline at end of file
......@@ -2,10 +2,11 @@
define(function (require) {
"use strict";
var slice, guess;
var slice, guess, form;
slice = Array.prototype.slice;
guess = require('./guess');
form = require('./form');
defaultNodeHandler.inverse = defaultInverseNodeHandler;
......@@ -169,20 +170,33 @@ define(function (require) {
function defaultNodeHandler (node, data, info) {
var attr, value, current;
attr = info.attr || guess.propForNode(node);
value = data[info.prop];
// always compare first to try to prevent unnecessary IE reflow/repaint
current = guess.getNodePropOrAttr(node, attr);
if (current !== value) {
guess.setNodePropOrAttr(node, attr, value);
if(node.form) {
form.setValues(node.form, data, function(_, name) {
return name === info.prop;
});
} else {
attr = info.attr || guess.propForNode(node);
value = data[info.prop];
// always compare first to try to prevent unnecessary IE reflow/repaint
current = guess.getNodePropOrAttr(node, attr);
if (current !== value) {
guess.setNodePropOrAttr(node, attr, value);
}
}
}
function defaultInverseNodeHandler (node, data, info) {
var attr, value;
attr = info.attr || guess.propForNode(node);
value = guess.getNodePropOrAttr(node, attr);
data[info.prop] = value;
if(node.form) {
value = form.getValues(node.form, function(el) {
return el === node || el.name === node.name;
});
data[info.prop] = value[info.prop];
} else {
attr = info.attr || guess.propForNode(node);
data[info.prop] = guess.getNodePropOrAttr(node, attr);
}
}
function createInverseHandler (binding, propToDom) {
......@@ -215,4 +229,4 @@ define(function (require) {
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(require); }
));
));
\ No newline at end of file
......@@ -5,6 +5,10 @@ define(function (require, exports) {
var splitClassNameRx = /\s+/;
var classRx = '(\\s+|^)(classNames)(\\b(?![\\-_])|$)';
var trimLeadingRx = /^\s+/;
var splitClassNamesRx = /(\b\s+\b)|(\s+)/g;
/**
* Returns the list of class names on a node as an array.
* @param node {HTMLElement}
......@@ -135,7 +139,41 @@ define(function (require, exports) {
return str.replace(outerSpacesRx, '');
}
function addClass (className, str) {
var newClass = removeClass(className, str);
if(newClass && className) {
newClass += ' ';
}
return newClass + className;
}
function removeClass (removes, tokens) {
var rx;
if (!removes) {
return tokens;
}
// convert space-delimited tokens with bar-delimited (regexp `or`)
removes = removes.replace(splitClassNamesRx, function (m, inner, edge) {
// only replace inner spaces with |
return edge ? '' : '|';
});
// create one-pass regexp
rx = new RegExp(classRx.replace('classNames', removes), 'g');
// remove all tokens in one pass (wish we could trim leading
// spaces in the same pass! at least the trim is not a full
// scan of the string)
return tokens.replace(rx, '').replace(trimLeadingRx, '');
}
return {
addClass: addClass,
removeClass: removeClass,
getClassList: getClassList,
setClassList: setClassList,
getClassSet: getClassSet,
......
......@@ -91,4 +91,4 @@ define(function (require) {
: function (factory) { module.exports = factory(require); },
this,
this.document
));
));
\ No newline at end of file
......@@ -3,16 +3,117 @@
(function (define) {
define(function () {
var forEach, slice;
forEach = Array.prototype.forEach;
slice = Array.prototype.slice;
return {
getValues: formToObject,
getMultiSelectValue: getMultiSelectValue,
setValues: objectToForm,
setElementValue: setElementValue,
setGroupValue: setGroupValue,
setMultiSelectValue: setMultiSelectValue,
isCheckable: isCheckable
};
function objectToForm(form, object, filter) {
var els;
els = form.elements;
if(typeof filter !== 'function') {
filter = alwaysInclude;
}
Object.keys(object).forEach(function(name) {
var el, value;
value = object[name];
el = els[name];
if(!filter(el, name, value)) return;
if(el.length) {
setGroupValue(el, value);
} else {
setElementValue(el, value);
}
});
return form;
}
function setGroupValue(group, value) {
var getBooleanValue;
getBooleanValue = Array.isArray(value)
? function(array, el) { return array.indexOf(el.value) >= 0; }
: function(value, el) { return el.value == value; };
forEach.call(group, function(el, i) {
if(isCheckable(el)) {
el.checked = getBooleanValue(value, el);
} else {
el.value = textValue(value[i]);
}
});
}
function setElementValue(el, value) {
if(isCheckable(el)) {
el.checked = !!value;
} else if(el.multiple && el.options) {
if(!Array.isArray(value)) {
el.value = textValue(value);
} else {
setMultiSelectValue(el, value);
}
} else {
el.value = textValue(value);
}
}
function setMultiSelectValue(select, values) {
var i, option, options;
options = select.options;
i = 0;
while ((option = options[i++])) {
if(values.indexOf(option.value) >= 0) {
option.selected = true;
}
}
}
function textValue(value) {
return value == null ? '' : value;
}
function isCheckable(el) {
return el.type == 'radio' || el.type == 'checkbox';
}
/**
* Simple routine to pull input values out of a form.
* @param form {HTMLFormElement}
* @return {Object} populated object
*/
return function formToObject (formOrEvent) {
function formToObject (formOrEvent, filter) {
var obj, form, els, seen, i, el, name, value;
form = formOrEvent.selectorTarget || formOrEvent.target || formOrEvent;
if(typeof filter !== 'function') {
filter = alwaysInclude;
}
obj = {};
els = form.elements;
......@@ -21,10 +122,10 @@ define(function () {
while ((el = els[i++])) {
name = el.name;
value = el.value;
// skip over non-named elements and fieldsets (that have no value)
if (!name || !('value' in el)) continue;
if (!name || !('value' in el) || !filter(el)) continue;
value = el.value;
if (el.type == 'radio') {
// only grab one radio value (to ensure that the property
......@@ -49,9 +150,14 @@ define(function () {
: [value];
}
}
else if (el.type == 'file') {
if (!(name in seen)) {
obj[name] = getFileInputValue(el);
}
}
else if (el.multiple && el.options) {
// grab all selected options
obj[name] = multiSelectToValue(el);
obj[name] = getMultiSelectValue(el);
}
else {
obj[name] = value;
......@@ -61,9 +167,17 @@ define(function () {
}
return obj;
};
}
function multiSelectToValue (select) {
function getFileInputValue (fileInput) {
if ('files' in fileInput) {
return fileInput.multiple ? slice.call(fileInput.files) : fileInput.files[0];
} else {
return fileInput.value;
}
}
function getMultiSelectValue (select) {
var values, options, i, option;
values = [];
options = select.options;
......@@ -74,9 +188,13 @@ define(function () {
return values;
}
function alwaysInclude() {
return true;
}
});
}(
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -22,4 +22,4 @@ define(function () {
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -54,9 +54,9 @@ define(function (require) {
if (Array.isArray(node)) {
// get unique list of events
return node.reduce(function (events, node) {
return guessEventsFor(node).filter(function (event) {
return events.concat(guessEventsFor(node).filter(function (event) {
return event && events.indexOf(event) < 0;
})
}));
},[]);
}
else if (isFormValueNode(node)) {
......@@ -148,4 +148,4 @@ define(function (require) {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(require); }
));
));
\ No newline at end of file
......@@ -29,4 +29,4 @@ define(function () {
: function (factory) { module.exports = factory(); },
this,
this.document
));
));
\ No newline at end of file
/**
* base
* @author: brian
*/
(function(define) {
define(function(require) {
var when, baseEvents, eventProcessor, simpleStrategy, defaultIdentifier,
beforePhase, propagatingPhase, afterPhase, canceledPhase,
undef;
when = require('when');
eventProcessor = require('./eventProcessor');
simpleStrategy = require('../network/strategy/default');
defaultIdentifier = require('../identifier/default');
// TODO: make these configurable/extensible
baseEvents = {
// basic item events. most of these come with data. devs can
// decide to use these events for their own purposes or send
// different data than described here, the following list outlines
// the intended behavior.
update: 1, // data == item updated
change: 1, // data == event type that caused the change
validate: 1, // data == validation result object with at least a boolean valid prop
// mode events
abort: 1, // abort the current mode (no data)
submit: 1, // finalize the current mode (no data)
// edit event
edit: 1, // enter edit mode (data == item to edit)
// network-level events (not to be used by adapters)
join: 1, // an adapter has joined (data == adapter)
sync: 1, // adapters need to sync (data == boolean. true == provider)
leave: 1 // an adapter has left (data == adapter)
};
/**
* Signal that event has not yet been pushed onto the network.
* Return false to prevent the event from being pushed.
*/
beforePhase = {};
/**
* Signal that event is currently being propagated to adapters.
*/
propagatingPhase = {};
/**
* Signal that an event has already been pushed onto the network.
* Return value is ignored since the event has already propagated.
*/
afterPhase = {};
/**
* Signal that an event was canceled and not pushed onto the network.
* Return value is ignored since the event has already propagated.
*/
canceledPhase = {};
function BaseHub(options) {
var eventTypes, t;
this.adapters = [];
if (!options) options = {};
this.identifier = options.identifier || defaultIdentifier;
this.eventProcessor = Object.create(eventProcessor, {
queue: { value: [] },
eventProcessor: { value: this.processEvent.bind(this) }
});
eventTypes = this.eventTypes;
for(t in eventTypes) {
this.addApi(t);
}
}
BaseHub.prototype = {
eventTypes: baseEvents,
dispatchEvent: function (name, data) {
try {
return this[name](data);
}
catch (ex) {
// TODO: do something with this exception
return false;
}
},
createAdapter: function (source, options) {
var Adapter = this.resolver.resolve(source);
return Adapter ? new Adapter(source, options) : source;
},
addSource: function (source, options) {
var adapter, proxy;
if (!options) options = {};
if (!options.identifier) options.identifier = this.identifier;
// create an adapter for this source
adapter = this.createAdapter(source, options);
proxy = this._createAdapterProxy(adapter, options);
proxy.origSource = source;
// save the proxied adapter
this.adapters.push(proxy);
this.eventProcessor.processEvent(proxy, null, 'join');
return adapter;
},
/*
1. call events.beforeXXX(data)
2. call strategy on each source/dest pair w/ event XXX and data
- cancel iteration if any strategy returns false for any pair
3. if not canceled, call events.XXX(data)
*/
processEvent: function (source, data, type) {
var context, strategyApi, self, strategy, adapters;
context = {};
self = this;
strategy = this.strategy;
adapters = this.adapters;
return when(
self.dispatchEvent(eventProcessor.makeBeforeEventName(type), data)
).then(
function (result) {
context.canceled = result === false;
if (context.canceled) return when.reject(context);
context.phase = beforePhase;
strategyApi = createStrategyApi(context, self.eventProcessor);
return strategy(source, undef, data, type, strategyApi);
}
).then(
function () {
context.phase = propagatingPhase;
return when.map(adapters, function (adapter) {
if (source != adapter) {
return strategy(source, adapter, data, type, strategyApi);
}
});
}
).then(
function () {
context.phase = context.canceled
? canceledPhase
: afterPhase;
return strategy(source, undef, data, type, strategyApi);
}
).then(
function (result) {
context.canceled = result === false;
if (context.canceled) return when.reject(context);
return self.dispatchEvent(eventProcessor.makeEventName(type), data);
}
).then(
function () {
return context;
}
);
},
destroy: function () {
var adapters, adapter;
adapters = this.adapters;
while ((adapter = adapters.pop())) {
if (typeof adapter.destroy == 'function') {
adapter.destroy();
}
}
},
addApi: function (name) {
this._addApiMethod(name);
this._addApiEvent(name);
},
_createAdapterProxy: function (adapter, options) {
var eventFinder, name, method, proxy;
proxy = Object.create(adapter);
// keep copy of original source so we can match it up later
if('provide' in options) {
proxy.provide = options.provide;
}
// sniff for event hooks
eventFinder = this.configureEventFinder(options.eventNames);
// override methods that require event hooks
for (name in adapter) {
method = adapter[name];
if (typeof method == 'function' && eventFinder(name)) {
// store original method on proxy (to stop recursion)
proxy[name] = callOriginalMethod(adapter, method);
// change public api of adapter to call back into hub
observeMethod(this.eventProcessor, adapter, name, method);
// ensure hub has a public method of the same name
this.addApi(name);
}
}
return proxy;
},
configureEventFinder: function (option) {
var eventTypes = this.eventTypes;
return typeof option == 'function'
? option
: function (name) { return name in eventTypes; };
},
_addApiMethod: function (name) {
var adapters, self, eventProcessor;
adapters = this.adapters;
eventProcessor = this.eventProcessor;
self = this;
if (!this[name]) {
this[name] = function (anything) {
var sourceInfo;
sourceInfo = self._findItemFor(anything);
if(!sourceInfo) {
sourceInfo = {
item: anything,
source: findAdapterForSource(arguments[1], adapters)
};
}
return eventProcessor.queueEvent(sourceInfo.source, sourceInfo.item, name);
};
}
},
_addApiEvent: function (name) {
var eventName = this.eventProcessor.makeEventName(name);
// add function stub to api
if (!this[eventName]) {
this[eventName] = function (data) {};
}
// add beforeXXX stub, too
eventName = this.eventProcessor.makeBeforeEventName(name);
if (!this[eventName]) {
this[eventName] = function (data) {};
}
},
_findItemFor: function (anything) {
var item, i, adapters, adapter;
adapters = this.adapters;
// loop through adapters that have the getItemForEvent() method
// to try to find out which adapter and which data item
i = 0;
while (!item && (adapter = adapters[i++])) {
if (adapter.findItem) {
item = adapter.findItem(anything);
}
}
return item && { item: item };
}
};
return BaseHub;
function createStrategyApi (context, eventProcessor) {
return {
queueEvent: function(source, data, type) {
return eventProcessor.queueEvent(source, data, type);
},
cancel: function () { context.canceled = true; },
isCanceled: function () { return !!context.canceled; },
handle: function () { context.handled = true; },
isHandled: function () { return !!context.handled; },
isBefore: function () { return isPhase(beforePhase); },
isAfter: function () { return isPhase(afterPhase); },
isAfterCanceled: function () { return isPhase(canceledPhase); },
isPropagating: function () { return isPhase(propagatingPhase); }
};
function isPhase (phase) {
return context.phase == phase;
}
}
function callOriginalMethod (adapter, orig) {
return function () {
return orig.apply(adapter, arguments);
};
}
function observeMethod (queue, adapter, type, origMethod) {
return adapter[type] = function (data) {
queue.queueEvent(adapter, data, type);
return origMethod.call(adapter, data);
};
}
function findAdapterForSource (source, adapters) {
var i, adapter, found;
// loop through adapters and find which one was created for this source
i = 0;
while (!found && (adapter = adapters[i++])) {
if (adapter.origSource == source) {
found = adapter;
}
}
return found;
}
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
/**
* eventQueue
* @author: brian
*/
(function(define) {
define(function(require) {
var when, enqueue;
when = require('when');
enqueue = require('../enqueue');
return {
makeBeforeEventName: function (name) {
return makeEventName('before', name);
},
makeEventName: function(name) {
return makeEventName('on', name);
},
/**
* Queue an event for processing later
* @param source
* @param data
* @param type
*/
queueEvent: function (source, data, type) {
// if queue length is zero, we need to start processing it again
var queueNeedsRestart = this.queue.length == 0;
// enqueue event
this.queue.push({ source: source, data: data, type: type });
// start processing, if necessary
return queueNeedsRestart && this._dispatchNextEvent();
},
/**
* Process an event immediately
* @param source
* @param data
* @param type
*/
processEvent: function(source, data, type) {
var self = this;
this.inflight = when(this.inflight).always(function() {
return self.eventProcessor(source, data, type);
});
return this.inflight;
},
_dispatchNextEvent: function () {
var event, remaining, deferred, self;
self = this;
// get the next event, if any
event = this.queue.shift();
remaining = this.queue.length;
// Ensure resolution is next turn, even if no event
// is actually dispatched.
deferred = when.defer();
enqueue(function () {
var inflight = event && self.processEvent(event.source, event.data, event.type);
deferred.resolve(inflight);
});
// Only continue processing the queue if it's not empty
if(remaining) {
deferred.promise.always(function() {
self._dispatchNextEvent();
});
}
return deferred.promise;
}
};
function makeEventName (prefix, name) {
return prefix + name.charAt(0).toUpperCase() + name.substr(1);
}
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
......@@ -13,4 +13,4 @@ define(function (require) {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(require); }
));
));
\ No newline at end of file
......@@ -21,4 +21,4 @@ define(function () {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -26,4 +26,4 @@ define(function () {
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -33,4 +33,4 @@ define(function () {
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -139,4 +139,4 @@ define(function () {
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -16,7 +16,7 @@ define(function (require) {
return function composeStrategies (strategies) {
return function (source, dest, data, type, api) {
when.reduce(strategies,
return when.reduce(strategies,
function(result, strategy) {
var strategyResult = strategy(source, dest, data, type, api);
return api.isCanceled()
......@@ -24,15 +24,19 @@ define(function (require) {
: strategyResult;
},
data
).always(function(result) { return result });
).then(propagateSuccess, propagateSuccess);
}
};
function propagateSuccess(x) {
return x;
}
});
}(
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(require); }
));
));
\ No newline at end of file
......@@ -47,4 +47,4 @@ define(function (require) {
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(require); }
));
));
\ No newline at end of file
(function (define) {
define(function (require) {
"use strict";
// Note: browser loaders and builders require that we don't "meta-program"
// the require() calls:
var compose, base, syncAfterJoin, syncModel, validate, changeEvent;
compose = require('./compose');
base = require('./base');
syncAfterJoin = require('./syncAfterJoin');
syncModel = require('./syncModel');
validate = require('./validate');
changeEvent = require('./changeEvent');
/**
* This is a composition of the strategies that Brian and I think
* make sense. :)
*
* @param options {Object} a conglomeration of all of the options for the
* strategies used.
* @param options.targetFirstItem {Boolean} if truthy, the strategy
* will automatically target the first item that is added to the network.
* If falsey, it will not automatically target.
* @param options.validator {Function} if provided, will be used
* to validate data items on add and update events
*
* @return {Function} a composite network strategy function
*/
return function (options) {
// compose them
return compose([
// Validate should be early so it can cancel other events
// when validation fails
validate(options),
// Change event support should be earlier than sync events
// so that it can translate them
changeEvent(options),
syncAfterJoin(options),
syncModel(options),
base(options)
]);
};
});
}(
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(require); }
));
\ No newline at end of file
......@@ -47,4 +47,4 @@ define(function (require) {
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(require); }
));
));
\ No newline at end of file
......@@ -49,4 +49,4 @@ define(function () {
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -112,4 +112,4 @@ define(function () {
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
(function (define) {
define(function () {
"use strict";
/**
* Creates a strategy to push all data from a source into the consumers
* in the network directly (rather than as a sequence of 'add' events
* in the network) when a sync event happens.
*
* @description This strategy helps eliminate loops and complexities
* when data providers and consumers are added at unpredictable times.
* During a sync, all 'add' events are squelched while providers push
* all items to all consumers.
*
* @param [options.providersAreConsumers] {Boolean} if truthy, providers
* are also treated as consumers and receive items from other providers.
* @return {Function} a network strategy function
*/
return function (options) {
var synced, providers, consumers, undef;
if (!options) options = {};
// TODO: consider putting these on the api object so they can be shared across strategies
// a list of all known providers and consumers
// these lists tend to be very small
providers = [];
consumers = [];
// the adapter currently being synced
synced = undef;
return function syncDataDirectly (source, dest, provide, type, api) {
// this strategy stops sync events before going on the network
if ('sync' == type && api.isBefore()) {
synced = source;
try {
if (provide) {
// provide data onto consumers in network
if (typeof source.properties != 'function') {
throw new Error('syncModel: provider doesn\'t have `properties()`.');
}
// keep track of providers
add(providers, synced);
// also add to consumers list, if specified
if (options.providersAreConsumers) {
add(consumers, synced);
}
// push data to all consumers
forEach(consumers, function (consumer) {
source.properties(function (item) {
consumer.update(item);
});
});
}
else {
// keep track of consumers
add(consumers, synced);
// provide data onto consumers in network
if (typeof source.update == 'function') {
// consume data from all providers
forEach(providers, function (provider) {
provider.properties(function (item) {
synced.update(item);
});
});
}
}
// the sync event never gets onto the network:
api.cancel();
}
finally {
synced = undef;
}
}
// stop 'add' events between adapters while sync'ing, but allow
// strategies interested in the event to see it before
else if ('add' == type && synced && !api.isBefore()) {
api.cancel();
}
// keep track of adapters that leave
else if ('leave' == type && api.isAfter()) {
// these just end up being noops if the source isn't in the list
remove(providers, source);
remove(consumers, source);
}
};
function add (list, adapter) {
list.push(adapter);
}
function remove (list, adapter) {
forEach(list, function (provider, i , providers) {
if (provider == adapter) {
providers.splice(i, 1);
}
});
}
function forEach (list, lambda) {
var i, obj;
i = list.length;
while ((obj = list[--i])) {
lambda(obj, i, list);
}
}
};
});
}(
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(); }
));
\ No newline at end of file
......@@ -31,4 +31,4 @@ define(function () {
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -37,4 +37,4 @@ define(function () {
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
/**
* objectAdapterResolver
* @author: brian
*/
(function(define) {
define(function(require) {
var adapterResolver = require('./adapterResolver');
return Object.create(adapterResolver, {
adapters: { value: [
require('./dom/adapter/Node'),
require('./adapter/Object')
]}
});
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
......@@ -29,4 +29,4 @@ define(function () {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -33,4 +33,4 @@ define(function () {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -47,4 +47,4 @@ define(function () {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -33,4 +33,4 @@ define(function () {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -142,4 +142,4 @@ define(function () {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
......@@ -33,4 +33,4 @@ define(function () {
// string because of "oddities" between `eval` and `this`.
return eval('' + this);
}
));
));
\ No newline at end of file
......@@ -49,4 +49,4 @@ define(function () {
typeof define == 'function'
? define
: function (factory) { module.exports = factory(); }
));
));
\ No newline at end of file
{
"name": "curl",
"version": "0.7.3",
"repository": {
"type": "git",
"url": "git://github.com/cujojs/curl"
}
}
\ No newline at end of file
{
"name": "curl",
"version": "0.7.3",
"description": "A small, fast module and resource loader with dependency management. (AMD, CommonJS Modules/1.1, CSS, HTML, etc.)",
"keywords": ["curl", "cujo", "amd", "loader", "module"],
"licenses": [
{
"type": "MIT",
"url": "http://www.opensource.org/licenses/mit-license.php"
}
],
"repositories": [
{
"type": "git",
"url": "https://github.com/cujojs/curl"
}
],
"bugs": "https://github.com/cujojs/curl/issues",
"maintainers": [
{
"name": "John Hann",
"web": "http://unscriptable.com"
}
],
"contributors": [
{
"name": "John Hann",
"web": "http://unscriptable.com"
},
{
"name": "Brian Cavalier",
"web": "http://hovercraftstudios.com"
}
],
"main": "./src/curl",
"directories": {
"test": "test"
}
}
......@@ -10,11 +10,14 @@
/**
* usage:
* curl({ debug: true }, ['curl/debug']).next(['other/modules'], function (otherModules) {
* curl({ preloads: ['curl/debug'] }, ['my/app'], function (myApp) {
* // do stuff while logging debug messages
* });
*
* The debug module must be used in conjunction with the debug: true config param!
* TODO: warn when main module still has leading dots (normalizePackageDescriptor)
* TODO: warn when a module id still has leading dots (toAbsId)
* TODO: use curl/tdd/undefine module instead of quick-and-dirty method below
* TODO: only add logging to some of the useful core functions
*
*/
define(['require', 'curl/_privileged'], function (require, priv) {
......
......@@ -14,7 +14,7 @@
define(/*=='curl/loader/cjsm11',==*/ function () {
var head /*, findRequiresRx, myId*/;
var head, insertBeforeEl /*, findRequiresRx, myId*/;
// findRequiresRx = /require\s*\(\s*['"](\w+)['"]\s*\)/,
......@@ -56,6 +56,9 @@ define(/*=='curl/loader/cjsm11',==*/ function () {
// }
head = document && (document['head'] || document.getElementsByTagName('head')[0]);
// to keep IE from crying, we need to put scripts before any
// <base> elements, but after any <meta>. this should do it:
insertBeforeEl = head && head.getElementsByTagName('base')[0] || null;
function wrapSource (source, resourceId, fullUrl) {
var sourceUrl = fullUrl ? '////@ sourceURL=' + fullUrl.replace(/\s/g, '%20') + '.js' : '';
......@@ -76,13 +79,12 @@ define(/*=='curl/loader/cjsm11',==*/ function () {
var el = document.createElement('script');
injectSource(el, source);
el.charset = 'utf-8';
// use insertBefore to keep IE from throwing Operation Aborted (thx Bryan Forbes!)
head.insertBefore(el, head.firstChild);
head.insertBefore(el, insertBeforeEl);
}
return {
'load': function (resourceId, require, loaded, config) {
// TODO: extract xhr from text! plugin and use that instead?
'load': function (resourceId, require, callback, config) {
// TODO: extract xhr from text! plugin and use that instead (after we upgrade to cram.js)
require(['text!' + resourceId + '.js', 'curl/_privileged'], function (source, priv) {
var moduleMap;
......@@ -104,10 +106,10 @@ define(/*=='curl/loader/cjsm11',==*/ function () {
globalEval(source);
}
// call loaded now that the module is defined
loaded(require(resourceId));
// call callback now that the module is defined
callback(require(resourceId));
});
}, callback['error'] || function (ex) { throw ex; });
});
}
......
......@@ -37,31 +37,19 @@
});
*/
define(/*=='async',==*/ function () {
define(/*=='curl/plugin/async',==*/ function () {
return {
'load': function (resourceId, require, callback, config) {
function resolved (resource) {
// return the resource to the callback
if (typeof callback.resolve == 'function') {
// promise-like callback
callback.resolve(resource);
}
else {
// just a function
callback(resource);
}
}
function rejected (error) {
// report that an error happened
if (typeof callback.reject == 'function') {
if (typeof callback.error == 'function') {
// promise-like callback
callback.reject(error);
callback.error(error);
}
// no way to report errors if the callback is not a promise
// no way to report errors if the callback doesn't have error()
}
// go get the module in the standard way
......@@ -72,16 +60,16 @@ define(/*=='async',==*/ function () {
module.then(
function (resource) {
if (arguments.length == 0) resource = module;
resolved(resource);
callback(resource);
},
rejected
);
}
else {
// just a callback
resolved(module);
// just a normal module
callback(module);
}
});
}, callback['error'] || function (ex) { throw ex; });
},
// for cram's analyze phase
......
......@@ -37,6 +37,108 @@ define(function () {
return parts;
}
var
// this actually tests for absolute urls and root-relative urls
// they're both non-relative
nonRelUrlRe = /^\/|^[^:]*:\/\//,
// Note: this will fail if there are parentheses in the url
findUrlRx = /url\s*\(['"]?([^'"\)]*)['"]?\)/g;
function translateUrls (cssText, baseUrl) {
return cssText.replace(findUrlRx, function (all, url) {
return 'url("' + translateUrl(url, baseUrl) + '")';
});
}
function translateUrl (url, parentPath) {
// if this is a relative url
if (!nonRelUrlRe.test(url)) {
// append path onto it
url = parentPath + url;
}
return url;
}
function createSheetProxy (sheet) {
return {
cssRules: function () {
return sheet.cssRules || sheet.rules;
},
insertRule: sheet.insertRule || function (text, index) {
var parts = text.split(/\{|\}/g);
sheet.addRule(parts[0], parts[1], index);
return index;
},
deleteRule: sheet.deleteRule || function (index) {
sheet.removeRule(index);
return index;
},
sheet: function () {
return sheet;
}
};
}
/***** style element functions *****/
var currentStyle;
function createStyle (cssText) {
clearTimeout(createStyle.debouncer);
if (createStyle.accum) {
createStyle.accum.push(cssText);
}
else {
createStyle.accum = [cssText];
currentStyle = doc.createStyleSheet ? doc.createStyleSheet() :
head.appendChild(doc.createElement('style'));
}
createStyle.debouncer = setTimeout(function () {
// Note: IE 6-8 won't accept the W3C method for inserting css text
var style, allCssText;
style = currentStyle;
currentStyle = undef;
allCssText = createStyle.accum.join('\n');
createStyle.accum = undef;
// for safari which chokes on @charset "UTF-8";
allCssText = allCssText.replace(/.+charset[^;]+;/g, '');
// TODO: hoist all @imports to the top of the file to follow w3c spec
'cssText' in style ? style.cssText = allCssText :
style.appendChild(doc.createTextNode(allCssText));
}, 0);
return currentStyle;
}
/*
// return the run-time API
callback({
'translateUrls': function (cssText, baseId) {
var baseUrl;
baseUrl = require['toUrl'](baseId);
baseUrl = baseUrl.substr(0, baseUrl.lastIndexOf('/') + 1);
return translateUrls(cssText, baseUrl);
},
'injectStyle': function (cssText) {
return createStyle(cssText);
},
'proxySheet': function (sheet) {
// for W3C, `sheet` is a reference to a <style> node so we need to
// return the sheet property.
if (sheet.sheet) sheet = sheet.sheet;
return createSheetProxy(sheet);
}
});
*/
return {
'build': function (writer, fetcher, config) {
......@@ -47,7 +149,6 @@ define(function () {
// plugin to write-out a resource
return function write (pluginId, resource, resolver) {
var opts, name, url, absId, text, output;
// TODO: implement !nowait and comma-sep files!
opts = parseSuffixes(resource);
name = opts.shift();
absId = resolver['toAbsMid'](name);
......@@ -59,6 +160,7 @@ define(function () {
// write out a define
// TODO: wait until sheet's rules are active before returning (use an amd promise)
// TODO: fix parser so that it doesn't choke on the word define( in a string
// TODO: write out api calls from this plugin-builder
output = 'def' + 'ine("' + pluginId + '!' + absId + '", ["' + pluginId + '!"], function (api) {\n' +
// translate urls
'\tvar cssText = "' + text + '";\n' +
......
define(function () {
return {
normalize: function (id, toAbsId) {
},
compile: function (absId, req, io, config) {
}
};
});
......@@ -20,7 +20,7 @@
* TODO: use "../domReady" instead of "curl/domReady" when curl's make.sh is updated to use cram
*/
define(/*=='domReady',==*/ ['curl/domReady'], function (domReady) {
define(/*=='curl/plugin/domReady',==*/ ['../domReady'], function (domReady) {
return {
......
/** MIT License (c) copyright B Cavalier & J Hann */
/**
* curl i18n plugin
*
* Fetch the user's locale-specific i18n bundle (e.g. strings/en-us.js"),
* any less-specific versions (e.g. "strings/en.js"), and a default i18n bundle
* (e.g. "strings.js"). All of these files are optional, but at least one
* is required or an error is propagated.
*
* If no locale-specific versions are found, use a default i18n bundle, if any.
*
* If multiple locale-specific versions are found, merge them such that
* the more specific versions override properties in the less specific.
*
* Browsers follow the language specifiers in the Language-Tags feature of
* RFC 5646: http://tools.ietf.org/html/rfc5646
*
* Example locales: "en", "en-US", "en-GB", "fr-FR", "kr", "cn", "cn-"
*
* These are lower-cased before being transformed into module ids. This
* plugin uses a simple algorithm to formulate file names from locales.
* It's probably easier to show an example than to describe it. Take a
* look at the examples section for more information. The algorithm may
* also be overridden via the localToModuleId configuration option.
*
* Nomenclature (to clarify usages of "bundle" herein):
*
* i18n bundle: A collection of javascript variables in the form of a JSON or
* javascript object and exported via an AMD define (or CommonJS exports
* if you are using the cjsm11 shim). These are typically strings, but
* can be just about anything language-specific or location-specific.
*
* AMD bundle: A concatenated set of AMD modules (or AMD-wrapped CommonJS
* modules) that can be loaded more efficiently than individual modules.
*
* Configuration options:
*
* locale {Boolean|String|Function} (default === false)
* If an explicit false value is provided, the plugin will not attempt
* to determine the browser's locale and will only use the default bundle.
* This is a great option for early development and when you want don't
* want this plugin to attempt to fetch (possibly unsupported) locales
* automatically in production.
* If an explicit true value is provided, the plugin will use the
* browser's clientInformation.language property (or fallback equivalent)
* to determine the locale and seek locale-specific i18n bundles.
* If this is a string, it is assumed to be an RFC 5646-compatible language
* specifier(s). The plugin will seek the i18n bundles for this locale.
* This is an excellent option to test specific locales.
* This option could also specify a function that returns language specifiers.
* A module id is passed as the only parameter to this function.
*
* localeToModuleId {Function} a function that translates a locale string to a
* module id where an AMD-formatted string bundle may be found. The default
* format is a module whose name is the locale located under the default
* (non-locale-specific) bundle. For example, if the default bundle is
* "myview/strings.js", the en-us version will be "myview/strings/en-us.js".
* Parameters: moduleId {String}, locale {String}, returns {String}
*
* During a build, locale-specific i18n bundles are not merged into a single
* bundle. For instance, if you specify that the bundles for "en" and "en-us"
* should be built into your AMD bundle, two separate bundles will be included.
* These will be merged at run-time. This method optimizes the size of the
* AMD bundle. In the future, we may add an option to pre-merge the i18n
* bundles, which could provide a small performance benefit under some
* circumstances.
*
* @examples
*
* `var strings = require("i18n!myapp/myview/strings");`
*
* If the current user's locale is "en-US", this plugin will simultaneously
* seek the following modules:
* * "myapp/myview/strings.js"
* * "myapp/myview/strings/en.js"
* * "myapp/myview/strings/en-us.js"
*
* If none are found, an error is propagated. If neither "en" or "en-us"
* is found, "strings" is used. If only "en" or "en-us" is found, it is used.
* If both are found, "en-us" is used to override "en" and the merged result
* is used.
*
* TODO: how to specify multiple locales be baked into AMD bundle (build time)?
*
*/
(function (global) {
define(/*=='curl/plugin/i18n',==*/ function () {
var appendLocaleRx;
// finds the end and an optional .js extension since some devs may have
// added it, which is legal since plugins sometimes require an extension.
appendLocaleRx = /(\.js)?$/;
return {
load: function (absId, require, loaded, config) {
var eb, toFile, locale, bundles, fetched, id, ids, specifiers, i;
eb = loaded.error;
if (!absId) {
eb(new Error('blank i18n bundle id.'));
}
// resolve config options
toFile = config['localeToModuleId'] || localeToModuleId;
locale = !('locale' in config) || config['locale']; // default: true
if (locale === true) locale = getLocale;
if (typeof locale == 'function') locale = locale(absId);
// keep track of what bundles we've found
ids = [absId];
bundles = [];
fetched = 0;
if (locale !== false) {
// get variations / specificities
// determine all the variations / specificities we might find
ids = ids.concat(locale.toLowerCase().split('-'));
specifiers = [];
// correct. start loop at 1! default bundle was already fetched
for (i = 1; i < ids.length; i++) {
// add next part to specifiers
specifiers[i - 1] = ids[i];
// create bundle id
id = toFile(absId, specifiers.join('-'));
// fetch and save found bundles, while silently skipping
// missing ones
fetch(require, id, i, got, countdown);
}
}
// get the default bundle, if any. this goes after we get
// variations to ensure that ids.length is set correctly.
fetch(require, absId, 0, got, countdown);
function got (bundle, i) {
bundles[i] = bundle;
countdown();
}
function countdown () {
var base;
if (++fetched == ids.length) {
if (bundles.length == 0) {
eb(new Error('No i18n bundles found: "' + absId + '", locale "' + locale + '"'));
}
else {
base = bundles[0] || {};
for (i = 1; i < bundles.length; i++) {
base = mixin(base, bundles[i]);
}
loaded(base);
}
}
}
}
};
function fetch (require, id, i, cb, eb) {
require([id], function (bundle) { cb(bundle, i); }, eb);
}
function mixin (base, props) {
if (props) {
for (var p in props) {
base[p] = props[p];
}
}
return base;
}
function getLocale () {
var ci = global['clientInformation'] || global.navigator;
return ci.language || ci['userLanguage'];
}
function localeToModuleId (absId, locale) {
return absId.replace(appendLocaleRx, locale ? '/' + locale : '');
}
});
}(this));
......@@ -29,14 +29,17 @@
*
*/
(function (global, doc, testGlobalVar) {
define(/*=='js',==*/ ['curl/_privileged'], function (priv) {
define(/*=='curl/plugin/js',==*/ ['curl/_privileged'], function (priv) {
"use strict";
var cache = {},
queue = [],
supportsAsyncFalse = doc && doc.createElement('script').async == true,
Promise,
waitForOrderedScript,
undef;
Promise = priv['Promise'];
function nameWithExt (name, defaultExt) {
return name.lastIndexOf('.') <= name.lastIndexOf('/') ?
name + '.' + defaultExt : name;
......@@ -102,10 +105,10 @@ define(/*=='js',==*/ ['curl/_privileged'], function (priv) {
// go get it (from cache hopefully)
fetch.apply(null, next);
}
promise['resolve'](def.resolved || true);
promise.resolve(def.resolved || true);
},
function (ex) {
promise['reject'](ex);
promise.reject(ex);
}
);
......@@ -113,39 +116,55 @@ define(/*=='js',==*/ ['curl/_privileged'], function (priv) {
return {
// the !options force us to cache ids in the plugin
// the !options force us to cache ids in the plugin and provide normalize
'dynamic': true,
'normalize': function (id, toAbsId, config) {
var end = id.indexOf('!');
return end >= 0 ? toAbsId(id.substr(0, end)) + id.substr(end) : toAbsId(id);
},
'load': function (name, require, callback, config) {
var order, exportsPos, exports, prefetch, def, promise;
var order, exportsPos, exports, prefetch, url, def, promise;
order = name.indexOf('!order') > 0; // can't be zero
exportsPos = name.indexOf('!exports=');
exports = exportsPos > 0 && name.substr(exportsPos + 9); // must be last option!
prefetch = 'prefetch' in config ? config['prefetch'] : true;
name = order || exportsPos > 0 ? name.substr(0, name.indexOf('!')) : name;
// add extension afterwards so js!-specific path mappings don't need extension, too
url = nameWithExt(require['toUrl'](name), 'js');
function reject (ex) {
(callback['error'] || function (ex) { throw ex; })(ex);
}
// if we've already fetched this resource, get it out of the cache
if (name in cache) {
callback(cache[name]);
if (url in cache) {
if (cache[url] instanceof Promise) {
cache[url].then(callback, reject);
}
else {
callback(cache[url]);
}
}
else {
cache[name] = undef;
def = {
name: name,
url: require['toUrl'](nameWithExt(name, 'js')),
url: url,
order: order,
exports: exports,
timeoutMsec: config['timeout']
};
promise = {
'resolve': function (o) {
cache[name] = o;
(callback['resolve'] || callback)(o);
cache[url] = promise = new Promise();
promise.then(
function (o) {
cache[url] = o;
callback(o);
},
'reject': callback['reject'] || function (ex) { throw ex; }
};
reject
);
// if this script has to wait for another
// or if we're loading, but not executing it
......
......@@ -70,7 +70,7 @@
return url.replace(isProtocolRelativeRx, protocol + '//');
}
define(/*=='link',==*/ {
define(/*=='curl/plugin/link',==*/ {
// 'normalize': function (resourceId, toAbsId) {
// // remove options
......@@ -80,7 +80,7 @@
'load': function (resourceId, require, callback, config) {
var url, link, fix;
url = require['toUrl'](nameWithExt(resourceId, 'css'));
url = nameWithExt(require['toUrl'](resourceId), 'css');
fix = 'fixSchemalessUrls' in config ? config['fixSchemalessUrls'] : doc.location.protocol;
url = fix ? fixProtocol(url, fix) : url;
link = createLink(doc, url);
......
......@@ -12,7 +12,7 @@
*
*/
define(/*=='text',==*/ function () {
define(/*=='curl/plugin/text',==*/ function () {
var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
......@@ -67,11 +67,8 @@ define(/*=='text',==*/ function () {
load: function (resourceName, req, callback, config) {
// remove suffixes (future)
// hook up callbacks
var cb = callback.resolve || callback,
eb = callback.reject || error;
// get the text
fetchText(req['toUrl'](resourceName), cb, eb);
fetchText(req['toUrl'](resourceName), callback, callback['error'] || error);
},
'plugin-builder': './builder/text'
......
......@@ -12,7 +12,7 @@
*
* @experimental
*/
define.amd.ssjs = true;
define['amd'].ssjs = true;
var require, load;
(function (freeRequire, globalLoad) {
define(/*=='curl/shim/ssjs',==*/ function (require, exports) {
......@@ -28,7 +28,7 @@ define(/*=='curl/shim/ssjs',==*/ function (require, exports) {
}
priv = require('curl/_privileged');
config = priv.cfg;
config = priv.config();
hasProtocolRx = /^\w+:/;
extractProtocolRx = /(^\w+:)?.*$/;
......
root = true
[*]
indent_style = tab
end_of_line = LF
\ No newline at end of file
.idea/
.npmignore
node_modules/
projectFilesBackup/
{
"browser": true,
"node": true,
"es5": true,
"predef": [
"define",
"module"
],
"boss": true,
"curly": true,
"eqnull": true,
"expr": true,
"globalstrict": false,
"laxbreak": true,
"newcap": true,
"noarg": true,
"noempty": true,
"nonew": true,
"quotmark": "single",
"strict": false,
"trailing": true,
"undef": true,
"unused": true,
"maxdepth": 4,
"maxcomplexity": 6
}
\ No newline at end of file
### 1.3.0
* [`meld()`](docs/api.md#adding-aspects) is now a function that adds aspects.
* **DEPRECATED:** `meld.add()`. Use `meld()` instead.
### 1.2.2
* Remove stray `console.log`.
### 1.2.1
* Fix for IE8-specific issue with meld's internal use of `Object.defineProperty`.
* Internally shim Object.create if not available to so that meld no longer requires an Object.create shim to advise constructors in pre-ES5 environments.
### 1.2.0
* `meld.joinpoint()` - [Access the current joinpoint](docs/api.md#meldjoinpoint) from any advice type.
* [Bundled aspects](docs/aspects.md):
* trace: trace method call entry/return/throw
* memoize: simple memoization for methods and functions
* cache: configurable caching aspect to do more than simple memoization
### 1.1.0
* Advice can be applied directly to methods on a function.
* Removed undocumented behavior that implicitly adds constructor prototype advice: to advise a prototype, pass the prototype as the advice target.
### 1.0.0
* **Removed browser global** - `window.meld` is no longer supported. See [this post on the cujo.js Google Group](https://groups.google.com/d/topic/cujojs/K0VGuvpYQ34/discussion) for an explanation.
* No functional change beyond browser global removal.
### 0.8.0
* 1.0.0 Release Candidate 1
* Documentation! Check out the new [reference](docs/reference.md) and [api](docs/api.md) docs.
* **Deprecated browser global** - meld.js will drop support for browser global for 1.0.0 and will support modular environments only.
### 0.7.2
* Fix for context when advising constructors: `this` is now the constructed instance in all advice functions.
### 0.7.1
* Fix for global name when using meld as a browser global. Thanks [@scothis](https://github.com/scothis)
* Update unit tests to run in browser using `buster server`, in addition to node. Thanks again, [@scothis](https://github.com/scothis) :)
### 0.7.0
* Advice can be applied directly to functions without a context.
* Advice can be applied to constructors.
* `joinpoint.proceed()` can be called multiple times. This makes it possible to implement "retry" types of advice.
### 0.6.0
* aop.js is now meld.js
* Use [Travis CI](http://travis-ci.org/cujojs/meld)
### 0.5.4
* Optimizations to run time advice invocation, especially around advice
* Fix for passing new args to `joinpoint.proceed()` in around advice
* Added `joinpoint.proceedApply(array)` for proceeding and supplying new arguments as an array
* Ported unit tests to [BusterJS](http://busterjs.org)
### 0.5.3
* First official release as part of [cujojs](http://github.com/cujojs)
* Minor doc and package.json tweaks
### 0.5.2
* Revert to larger, more builder-friendly module boilerplate. No functional change.
### 0.5.1
* Minor corrections and updates to `package.json`
### 0.5.0
* Rewritten Advisor that allows entire aspects to be unwoven (removed) easily.
\ No newline at end of file
[![Build Status](https://secure.travis-ci.org/cujojs/meld.png)](http://travis-ci.org/cujojs/meld)
[Aspect Oriented Programming](http://en.wikipedia.org/wiki/Aspect-oriented_programming "Aspect-oriented programming - Wikipedia, the free encyclopedia") for Javascript. It allows you to change the behavior of, or add behavior to methods and functions (including constructors) *non-invasively*.
As a simple example, instead of changing code, you can use meld to log the result of `myObject.doSomething`:
```js
var myObject = {
doSomething: function(a, b) {
return a + b;
}
};
// Call a function after myObject.doSomething returns
var remover = meld.after(myObject, 'doSomething', function(result) {
console.log('myObject.doSomething returned: ' + result);
});
myObject.doSomething(1, 2); // Logs: "myObject.doSomething returned: 3"
remover.remove();
myObject.doSomething(1, 2); // Nothing logged
```
# Docs
* [API](docs/api.md)
* [Reference](docs/reference.md)
* [Aspects](docs/aspects.md)
# Quick Start
### AMD
1. Get it using one of the following
1. `yeoman install meld`, or
1. `bower install meld`, or
1. `git clone https://github.com/cujojs/meld`, or
1. `git submodule add https://github.com/cujojs/meld`
1. Configure your loader with a package:
```js
packages: [
{ name: 'meld', location: 'path/to/meld', main: 'meld' },
// ... other packages ...
]
```
1. `define(['meld', ...], function(meld, ...) { ... });` or `require(['meld', ...], function(meld, ...) { ... });`
### Node
1. `npm install meld`
1. `var meld = require('meld');`
### RingoJS
1. `ringo-admin install cujojs/meld`
1. `var meld = require('meld');`
Running the Unit Tests
======================
Install [buster.js](http://busterjs.org/)
`npm install -g buster`
Run unit tests in Node:
`buster test`
# What's New
### 1.3.0
* [`meld()`](docs/api.md#adding-aspects) is now a function that adds aspects.
* **DEPRECATED:** `meld.add()`. Use `meld()` instead.
### 1.2.2
* Remove stray `console.log`.
### 1.2.1
* Fix for IE8-specific issue with meld's internal use of `Object.defineProperty`.
* Internally shim Object.create if not available to so that meld no longer requires an Object.create shim to advise constructors in pre-ES5 environments.
### 1.2.0
* `meld.joinpoint()` - [Access the current joinpoint](docs/api.md#meldjoinpoint) from any advice type.
* [Bundled aspects](docs/aspects.md):
* trace: trace method call entry/return/throw
* memoize: simple memoization for methods and functions
* cache: configurable caching aspect to do more than simple memoization
### 1.1.0
* Advice can be applied directly to methods on a function.
* Removed undocumented behavior that implicitly adds constructor prototype advice: to advise a prototype, pass the prototype as the advice target.
### 1.0.0
* **Removed browser global** - `window.meld` is no longer supported. See [this post on the cujo.js Google Group](https://groups.google.com/d/topic/cujojs/K0VGuvpYQ34/discussion) for an explanation.
* No functional change beyond browser global removal.
See the [full Changelog here](CHANGES.md)
# References
* [AspectJ](http://www.eclipse.org/aspectj/) and [Spring Framework AOP](http://static.springsource.org/spring/docs/3.0.x/reference/meld.html) for inspiration and great docs
* Implementation ideas from @phiggins42's [uber.js AOP](https://github.com/phiggins42/uber.js/blob/master/lib/meld.js)
* API ideas from [jquery-aop](http://code.google.com/p/jquery-aop/)
\ No newline at end of file
/** @license MIT License (c) copyright 2011-2013 original author or authors */
/**
* Caching aspect
* Requires JSON.stringify. See cujojs/poly if you need a JSON polyfill
*
* @author Brian Cavalier
* @author John Hann
*/
(function(define) {
define(function() {
/**
* Creates a new caching aspect that uses the supplied cache
* to store values using keys generated by the supplied keyGenerator.
*
* Requires JSON.stringify. See cujojs/poly if you need a JSON polyfill
*
* @param {object} cache
* @param {function} cache.has returns true if a supplied key exists in the cache
* @param {function} cache.get returns the value associated with the supplied key
* @param {function} cache.set associates the supplied key with the supplied value
* @param {function} [keyGenerator] creates a hash key given an array. Used to generate
* cache keys from function invocation arguments
* @return {object} caching aspect that can be added with meld.add
*/
return function(cache, keyGenerator) {
if(!keyGenerator) {
keyGenerator = JSON.stringify;
}
return {
around: function(joinpoint) {
var key, result;
key = keyGenerator(joinpoint.args);
if(cache.has(key)) {
result = cache.get(key);
} else {
result = joinpoint.proceed();
cache.set(key, result);
}
return result;
}
};
};
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright 2011-2013 original author or authors */
/**
* Simple memoization aspect
*
* @author Brian Cavalier
* @author John Hann
*/
(function(define) {
define(function(require) {
var createCacheAspect = require('./cache');
function SimpleCache() {
this._cache = {};
}
SimpleCache.prototype = {
has: function(key) { return key in this._cache; },
get: function(key) { return this._cache[key]; },
set: function(key, value) { this._cache[key] = value; }
};
/**
* Creates a simple memoizing aspect that can be used to memoize
* a function or method.
* @param {function} [keyGenerator] creates a hash key given an array. Used to generate
* memo cache keys from function invocation arguments
* @return {object} memoizing aspect that can be added with meld.add
*/
return function(keyGenerator) {
return createCacheAspect(new SimpleCache(), keyGenerator);
};
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright 2011-2013 original author or authors */
/**
* trace
* @author: brian@hovercraftstudios.com
*
* @author Brian Cavalier
* @author John Hann
*/
(function(define) {
define(function(require) {
var meld, joinpoint, depth, padding, simpleReporter;
meld = require('../meld');
joinpoint = meld.joinpoint;
// Call stack depth tracking for the default reporter
depth = 0;
// Padding characters for indenting traces. This will get expanded as needed
padding = '................................';
simpleReporter = {
onCall: function(info) {
console.log(indent(++depth) + info.method + ' CALL ', info.args);
},
onReturn: function(info) {
console.log(indent(depth--) + info.method + ' RETURN ', info.result);
},
onThrow: function(info) {
console.log(indent(depth--) + info.method + ' THROW ' + info.exception);
}
};
/**
* Creates an aspect that traces method/function calls and reports them
* to the supplied reporter.
* @param {object} [reporter] optional reporting API to which method call/return/throw
* info will be reported
* @param {function} [reporter.onCall] invoked when a method has been called
* @param {function} [reporter.onReturn] invoked when a method returns successfully
* @param {function} [reporter.onThrow] invoked when a method throws an exception
* @return {object} a tracing aspect that can be added with meld.add
*/
return function createTraceAspect(reporter) {
if(!reporter) {
reporter = simpleReporter;
}
return {
before: function() {
var jp = joinpoint();
reporter.onCall && reporter.onCall({ method: jp.method, target: jp.target, args: jp.args.slice() });
},
afterReturning: function(result) {
var jp = joinpoint();
reporter.onReturn && reporter.onReturn({ method: jp.method, target: jp.target, result: result });
},
afterThrowing: function(exception) {
var jp = joinpoint();
reporter.onThrow && reporter.onThrow({ method: jp.method, target: jp.target, exception: exception });
}
};
};
/**
* Create indentation padding for tracing info based on the supplied call stack depth
* @param {number} depth call stack depth
* @return {String} padding that can be used to indent tracing output
*/
function indent(depth) {
if(depth > padding.length) {
padding += padding;
}
return padding.slice(0, depth-1);
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
{
"name": "meld",
"version": "1.3.0",
"repository": {
"type": "git",
"url": "git://github.com/cujojs/meld.git"
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment