Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Agility app - use Bower components

  • Loading branch information...
commit 2b5c9aca4cc45fd00b19301cc46873a3ad4c7733 1 parent 1c739d6
@sindresorhus sindresorhus authored
View
9 architecture-examples/agilityjs/component.json
@@ -0,0 +1,9 @@
+{
+ "name": "agilityjs",
+ "version": "0.0.0",
+ "dependencies": {
+ "todomvc-common": "~0.1.0",
+ "agility": "~0.1.3",
+ "jquery": "~1.9.1"
+ }
+}
View
1,156 architecture-examples/agilityjs/components/agility/agility.js
@@ -0,0 +1,1156 @@
+/*
+
+ Agility.js
+ Licensed under the MIT license
+ Copyright (c) Artur B. Adib, 2011
+ http://agilityjs.com
+
+*/
+
+// Sandboxed, so kids don't get hurt. Inspired by jQuery's code:
+// Creates local ref to window for performance reasons (as JS looks up local vars first)
+// Redefines undefined as it could have been tampered with
+(function(window, undefined){
+
+ if (!window.jQuery) {
+ throw "agility.js: jQuery not found";
+ }
+
+ // Local references
+ var document = window.document,
+ location = window.location,
+
+ // In case $ is being used by another lib
+ $ = jQuery,
+
+ // Main agility object builder
+ agility,
+
+ // Internal utility functions
+ util = {},
+
+ // Default object prototype
+ defaultPrototype = {},
+
+ // Global object counter
+ idCounter = 0,
+
+ // Constant
+ ROOT_SELECTOR = '&';
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Modernizing old JS
+ //
+
+ // Modified from Douglas Crockford's Object.create()
+ // The condition below ensures we override other manual implementations (most are not adequate)
+ if (!Object.create || Object.create.toString().search(/native code/i)<0) {
+ Object.create = function(obj){
+ var Aux = function(){};
+ $.extend(Aux.prototype, obj); // simply setting Aux.prototype = obj somehow messes with constructor, so getPrototypeOf wouldn't work in IE
+ return new Aux();
+ };
+ }
+
+ // Modified from John Resig's Object.getPrototypeOf()
+ // The condition below ensures we override other manual implementations (most are not adequate)
+ if (!Object.getPrototypeOf || Object.getPrototypeOf.toString().search(/native code/i)<0) {
+ if ( typeof "test".__proto__ === "object" ) {
+ Object.getPrototypeOf = function(object){
+ return object.__proto__;
+ };
+ } else {
+ Object.getPrototypeOf = function(object){
+ // May break if the constructor has been tampered with
+ return object.constructor.prototype;
+ };
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // util.*
+ //
+
+ // Checks if provided obj is an agility object
+ util.isAgility = function(obj){
+ return obj._agility === true;
+ };
+
+ // Scans object for functions (depth=2) and proxies their 'this' to dest.
+ // * To ensure it works with previously proxied objects, we save the original function as
+ // a '._preProxy' method and when available always use that as the proxy source.
+ // * To skip a given method, create a sub-method called '_noProxy'.
+ util.proxyAll = function(obj, dest){
+ if (!obj || !dest) {
+ throw "agility.js: util.proxyAll needs two arguments";
+ }
+ for (var attr1 in obj) {
+ var proxied = obj[attr1];
+ // Proxy root methods
+ if (typeof obj[attr1] === 'function') {
+ proxied = obj[attr1]._noProxy ? obj[attr1] : $.proxy(obj[attr1]._preProxy || obj[attr1], dest);
+ proxied._preProxy = obj[attr1]._noProxy ? undefined : (obj[attr1]._preProxy || obj[attr1]); // save original
+ obj[attr1] = proxied;
+ }
+ // Proxy sub-methods (model.*, view.*, etc)
+ else if (typeof obj[attr1] === 'object') {
+ for (var attr2 in obj[attr1]) {
+ var proxied2 = obj[attr1][attr2];
+ if (typeof obj[attr1][attr2] === 'function') {
+ proxied2 = obj[attr1][attr2]._noProxy ? obj[attr1][attr2] : $.proxy(obj[attr1][attr2]._preProxy || obj[attr1][attr2], dest);
+ proxied2._preProxy = obj[attr1][attr2]._noProxy ? undefined : (obj[attr1][attr2]._preProxy || obj[attr1][attr2]); // save original
+ proxied[attr2] = proxied2;
+ }
+ } // for attr2
+ obj[attr1] = proxied;
+ } // if not func
+ } // for attr1
+ }; // proxyAll
+
+ // Reverses the order of events attached to an object
+ util.reverseEvents = function(obj, eventType){
+ var events = $(obj).data('events');
+ if (events !== undefined && events[eventType] !== undefined){
+ // can't reverse what's not there
+ var reverseEvents = [];
+ for (var e in events[eventType]){
+ if (!events[eventType].hasOwnProperty(e)) continue;
+ reverseEvents.unshift(events[eventType][e]);
+ }
+ events[eventType] = reverseEvents;
+ }
+ }; //reverseEvents
+
+ // Determines # of attributes of given object (prototype inclusive)
+ util.size = function(obj){
+ var size = 0, key;
+ for (key in obj) {
+ size++;
+ }
+ return size;
+ };
+
+ // Find controllers to be extended (with syntax '~'), redefine those to encompass previously defined controllers
+ // Example:
+ // var a = $$({}, '<button>A</button>', {'click &': function(){ alert('A'); }});
+ // var b = $$(a, {}, '<button>B</button>', {'~click &': function(){ alert('B'); }});
+ // Clicking on button B will alert both 'A' and 'B'.
+ util.extendController = function(object) {
+ for (var controllerName in object.controller) {
+ (function(){ // new scope as we need one new function handler per controller
+ var matches, extend, eventName,
+ previousHandler, currentHandler, newHandler;
+
+ if (typeof object.controller[controllerName] === 'function') {
+ matches = controllerName.match(/^(\~)*(.+)/); // 'click button', '~click button', '_create', etc
+ extend = matches[1];
+ eventName = matches[2];
+
+ if (!extend) return; // nothing to do
+
+ // Redefine controller:
+ // '~click button' ---> 'click button' = previousHandler + currentHandler
+ previousHandler = object.controller[eventName] ? (object.controller[eventName]._preProxy || object.controller[eventName]) : undefined;
+ currentHandler = object.controller[controllerName];
+ newHandler = function() {
+ if (previousHandler) previousHandler.apply(this, arguments);
+ if (currentHandler) currentHandler.apply(this, arguments);
+ };
+
+ object.controller[eventName] = newHandler;
+ delete object.controller[controllerName]; // delete '~click button'
+ } // if function
+ })();
+ } // for controllerName
+ };
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Default object prototype
+ //
+
+ defaultPrototype = {
+
+ _agility: true,
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // _container
+ //
+ // API and related auxiliary functions for storing child Agility objects.
+ // Not all methods are exposed. See 'shortcuts' below for exposed methods.
+ //
+
+ _container: {
+
+ // Adds child object to container, appends/prepends/etc view, listens for child removal
+ _insertObject: function(obj, selector, method){
+ var self = this;
+ if (!util.isAgility(obj)) {
+ throw "agility.js: append argument is not an agility object";
+ }
+ this._container.children[obj._id] = obj; // children is *not* an array; this is for simpler lookups by global object id
+ this.trigger(method, [obj, selector]);
+ obj._parent = this;
+ // ensures object is removed from container when destroyed:
+ obj.bind('destroy', function(event, id){
+ self._container.remove(id);
+ });
+ return this;
+ },
+
+ append: function(obj, selector) {
+ return this._container._insertObject.call(this, obj, selector, 'append');
+ },
+
+ prepend: function(obj, selector) {
+ return this._container._insertObject.call(this, obj, selector, 'prepend');
+ },
+
+ after: function(obj, selector) {
+ return this._container._insertObject.call(this, obj, selector, 'after');
+ },
+
+ before: function(obj, selector) {
+ return this._container._insertObject.call(this, obj, selector, 'before');
+ },
+
+ // Removes child object from container
+ remove: function(id){
+ delete this._container.children[id];
+ this.trigger('remove', id);
+ return this;
+ },
+
+ // Iterates over all child objects in container
+ each: function(fn){
+ $.each(this._container.children, fn);
+ return this; // for chainable calls
+ },
+
+ // Removes all objects in container
+ empty: function(){
+ this.each(function(){
+ this.destroy();
+ });
+ return this;
+ },
+
+ // Number of children
+ size: function() {
+ return util.size(this._container.children);
+ }
+
+ },
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // _events
+ //
+ // API and auxiliary functions for handling events. Not all methods are exposed.
+ // See 'shortcuts' below for exposed methods.
+ //
+
+ _events: {
+
+ // Parses event string like:
+ // 'event' : custom event
+ // 'event selector' : DOM event using 'selector'
+ // Returns { type:'event' [, selector:'selector'] }
+ parseEventStr: function(eventStr){
+ var eventObj = { type:eventStr },
+ spacePos = eventStr.search(/\s/);
+ // DOM event 'event selector', e.g. 'click button'
+ if (spacePos > -1) {
+ eventObj.type = eventStr.substr(0, spacePos);
+ eventObj.selector = eventStr.substr(spacePos+1);
+ }
+ return eventObj;
+ },
+
+ // Binds eventStr to fn. eventStr is parsed as per parseEventStr()
+ bind: function(eventStr, fn){
+ var eventObj = this._events.parseEventStr(eventStr);
+ // DOM event 'event selector', e.g. 'click button'
+ if (eventObj.selector) {
+ // Manually override root selector, as jQuery selectors can't select self object
+ if (eventObj.selector === ROOT_SELECTOR) {
+ this.view.$().bind(eventObj.type, fn);
+ }
+ else {
+ this.view.$().delegate(eventObj.selector, eventObj.type, fn);
+ }
+ }
+ // Custom event
+ else {
+ $(this._events.data).bind(eventObj.type, fn);
+ }
+ return this; // for chainable calls
+ }, // bind
+
+ // Triggers eventStr. Syntax for eventStr is same as that for bind()
+ trigger: function(eventStr, params){
+ var eventObj = this._events.parseEventStr(eventStr);
+ // DOM event 'event selector', e.g. 'click button'
+ if (eventObj.selector) {
+ // Manually override root selector, as jQuery selectors can't select self object
+ if (eventObj.selector === ROOT_SELECTOR) {
+ this.view.$().trigger(eventObj.type, params);
+ }
+ else {
+ this.view.$().find(eventObj.selector).trigger(eventObj.type, params);
+ }
+ }
+ // Custom event
+ else {
+ $(this._events.data).trigger('_'+eventObj.type, params);
+ // fire 'pre' hooks in reverse attachment order ( last first )
+ util.reverseEvents(this._events.data, 'pre:' + eventObj.type);
+ $(this._events.data).trigger('pre:' + eventObj.type, params);
+ // put the order of events back
+ util.reverseEvents(this._events.data, 'pre:' + eventObj.type);
+ $(this._events.data).trigger(eventObj.type, params);
+ if(this.parent())
+ this.parent().trigger((eventObj.type.match(/^child:/) ? '' : 'child:') + eventObj.type, params);
+ $(this._events.data).trigger('post:' + eventObj.type, params);
+ }
+ return this; // for chainable calls
+ } // trigger
+
+ }, // _events
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Model
+ //
+ // Main model API. All methods are exposed, but methods starting with '_'
+ // are meant to be used internally only.
+ //
+
+ model: {
+
+ // Setter
+ set: function(arg, params) {
+ var self = this;
+ var modified = []; // list of modified model attributes
+ if (typeof arg === 'object') {
+ var _clone = false;
+ if (params && params.reset) {
+ _clone = this.model._data; // hold on to data for change events
+ this.model._data = $.extend({}, arg); // erases previous model attributes without pointing to object
+ }
+ else {
+ $.extend(this.model._data, arg); // default is extend
+ }
+ for (var key in arg) {
+ delete _clone[ key ]; // no need to fire change twice
+ modified.push(key);
+ }
+ for (key in _clone) {
+ modified.push(key);
+ }
+ }
+ else {
+ throw "agility.js: unknown argument type in model.set()";
+ }
+
+ // Events
+ if (params && params.silent===true) return this; // do not fire events
+ this.trigger('change');
+ $.each(modified, function(index, val){
+ self.trigger('change:'+val);
+ });
+ return this; // for chainable calls
+ },
+
+ // Getter
+ get: function(arg){
+ // Full model getter
+ if (arg === undefined) {
+ return this.model._data;
+ }
+ // Attribute getter
+ if (typeof arg === 'string') {
+ return this.model._data[arg];
+ }
+ throw 'agility.js: unknown argument for getter';
+ },
+
+ // Resetter (to initial model upon object initialization)
+ reset: function(){
+ this.model.set(this.model._initData, {reset:true});
+ return this; // for chainable calls
+ },
+
+ // Number of model properties
+ size: function(){
+ return util.size(this.model._data);
+ },
+
+ // Convenience function - loops over each model property
+ each: function(fn){
+ $.each(this.model._data, fn);
+ return this; // for chainable calls
+ }
+
+ }, // model prototype
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // View
+ //
+ // Main view API. All methods are exposed, but methods starting with '_'
+ // are meant to be used internally only.
+ //
+
+ view: {
+
+ // Defaults
+ format: '<div/>',
+ style: '',
+
+ // Shortcut to view.$root or view.$root.find(), depending on selector presence
+ $: function(selector){
+ return (!selector || selector === ROOT_SELECTOR) ? this.view.$root : this.view.$root.find(selector);
+ },
+
+ // Render $root
+ // Only function to access $root directly other than $()
+ render: function(){
+ // Without format there is no view
+ if (this.view.format.length === 0) {
+ throw "agility.js: empty format in view.render()";
+ }
+ if (this.view.$root.size() === 0) {
+ this.view.$root = $(this.view.format);
+ }
+ else {
+ this.view.$root.html( $(this.view.format).html() ); // can't overwrite $root as this would reset its presence in the DOM and all events already bound, and
+ }
+ // Ensure we have a valid (non-empty) $root
+ if (this.view.$root.size() === 0) {
+ throw 'agility.js: could not generate html from format';
+ }
+ return this;
+ }, // render
+
+ // Parse data-bind string of the type '[attribute][=] variable[, [attribute][=] variable ]...'
+ // If the variable is not an attribute, it must occur by itself
+ // all pairs in the list are assumed to be attributes
+ // Returns { key:'model key', attr: [ {attr : 'attribute', attrVar : 'variable' }... ] }
+ _parseBindStr: function(str){
+ var obj = {key:null, attr:[]},
+ pairs = str.split(','),
+ regex = /([a-zA-Z0-9_\-]+)(?:[\s=]+([a-zA-Z0-9_\-]+))?/,
+ keyAssigned = false,
+ matched;
+
+ if (pairs.length > 0) {
+ for (var i = 0; i < pairs.length; i++) {
+ matched = pairs[i].match(regex);
+ // [ "attribute variable", "attribute", "variable" ]
+ // or [ "attribute=variable", "attribute", "variable" ]
+ // or
+ // [ "variable", "variable", undefined ]
+ // in some IE it will be [ "variable", "variable", "" ]
+ // or
+ // null
+ if (matched) {
+ if (typeof(matched[2]) === "undefined" || matched[2] === "") {
+ if (keyAssigned) {
+ throw new Error("You may specify only one key (" +
+ keyAssigned + " has already been specified in data-bind=" +
+ str + ")");
+ } else {
+ keyAssigned = matched[1];
+ obj.key = matched[1];
+ }
+ } else {
+ obj.attr.push({attr: matched[1], attrVar: matched[2]});
+ }
+ } // if (matched)
+ } // for (pairs.length)
+ } // if (pairs.length > 0)
+
+ return obj;
+ },
+
+ // Apply two-way (DOM <--> Model) bindings to elements with 'data-bind' attributes
+ bindings: function(){
+ var self = this;
+ var $rootNode = this.view.$().filter('[data-bind]');
+ var $childNodes = this.view.$('[data-bind]');
+ var createAttributePairClosure = function(bindData, node, i) {
+ var attrPair = bindData.attr[i]; // capture the attribute pair in closure
+ return function() {
+ node.attr(attrPair.attr, self.model.get(attrPair.attrVar));
+ };
+ };
+ $rootNode.add($childNodes).each(function(){
+ var $node = $(this);
+ var bindData = self.view._parseBindStr( $node.data('bind') );
+
+ var bindAttributesOneWay = function() {
+ // 1-way attribute binding
+ if (bindData.attr) {
+ for (var i = 0; i < bindData.attr.length; i++) {
+ self.bind('_change:'+bindData.attr[i].attrVar,
+ createAttributePairClosure(bindData, $node, i));
+ } // for (bindData.attr)
+ } // if (bindData.attr)
+ }; // bindAttributesOneWay()
+
+ // <input type="checkbox">: 2-way binding
+ if ($node.is('input:checkbox')) {
+ // Model --> DOM
+ self.bind('_change:'+bindData.key, function(){
+ $node.prop("checked", self.model.get(bindData.key)); // this won't fire a DOM 'change' event, saving us from an infinite event loop (Model <--> DOM)
+ });
+ // DOM --> Model
+ $node.change(function(){
+ var obj = {};
+ obj[bindData.key] = $(this).prop("checked");
+ self.model.set(obj); // not silent as user might be listening to change events
+ });
+ // 1-way attribute binding
+ bindAttributesOneWay();
+ }
+
+ // <select>: 2-way binding
+ else if ($node.is('select')) {
+ // Model --> DOM
+ self.bind('_change:'+bindData.key, function(){
+ var nodeName = $node.attr('name');
+ var modelValue = self.model.get(bindData.key);
+ $node.val(modelValue);
+ });
+ // DOM --> Model
+ $node.change(function(){
+ var obj = {};
+ obj[bindData.key] = $node.val();
+ self.model.set(obj); // not silent as user might be listening to change events
+ });
+ // 1-way attribute binding
+ bindAttributesOneWay();
+ }
+
+ // <input type="radio">: 2-way binding
+ else if ($node.is('input:radio')) {
+ // Model --> DOM
+ self.bind('_change:'+bindData.key, function(){
+ var nodeName = $node.attr('name');
+ var modelValue = self.model.get(bindData.key);
+ $node.siblings('input[name="'+nodeName+'"]').filter('[value="'+modelValue+'"]').prop("checked", true); // this won't fire a DOM 'change' event, saving us from an infinite event loop (Model <--> DOM)
+ });
+ // DOM --> Model
+ $node.change(function(){
+ if (!$node.prop("checked")) return; // only handles check=true events
+ var obj = {};
+ obj[bindData.key] = $node.val();
+ self.model.set(obj); // not silent as user might be listening to change events
+ });
+ // 1-way attribute binding
+ bindAttributesOneWay();
+ }
+
+ // <input type="search"> (model is updated after every keypress event)
+ else if ($node.is('input[type="search"]')) {
+ // Model --> DOM
+ self.bind('_change:'+bindData.key, function(){
+ $node.val(self.model.get(bindData.key)); // this won't fire a DOM 'change' event, saving us from an infinite event loop (Model <--> DOM)
+ });
+ // Model <-- DOM
+ $node.keypress(function(){
+ // Without timeout $node.val() misses the last entered character
+ setTimeout(function(){
+ var obj = {};
+ obj[bindData.key] = $node.val();
+ self.model.set(obj); // not silent as user might be listening to change events
+ }, 50);
+ });
+ // 1-way attribute binding
+ bindAttributesOneWay();
+ }
+
+ // <input type="text">, <input>, and <textarea>: 2-way binding
+ else if ($node.is('input:text, textarea')) {
+ // Model --> DOM
+ self.bind('_change:'+bindData.key, function(){
+ $node.val(self.model.get(bindData.key)); // this won't fire a DOM 'change' event, saving us from an infinite event loop (Model <--> DOM)
+ });
+ // Model <-- DOM
+ $node.change(function(){
+ var obj = {};
+ obj[bindData.key] = $(this).val();
+ self.model.set(obj); // not silent as user might be listening to change events
+ });
+ // 1-way attribute binding
+ bindAttributesOneWay();
+ }
+
+ // all other <tag>s: 1-way binding
+ else {
+ if (bindData.key) {
+ self.bind('_change:'+bindData.key, function(){
+ if (self.model.get(bindData.key)) {
+ $node.text(self.model.get(bindData.key).toString());
+ } else {
+ $node.text('');
+ }
+ });
+ }
+ bindAttributesOneWay();
+ }
+ }); // nodes.each()
+ return this;
+ }, // bindings()
+
+ // Triggers _change and _change:* events so that view is updated as per view.bindings()
+ sync: function(){
+ var self = this;
+ // Trigger change events so that view is updated according to model
+ this.model.each(function(key, val){
+ self.trigger('_change:'+key);
+ });
+ if (this.model.size() > 0) {
+ this.trigger('_change');
+ }
+ return this;
+ },
+
+ // Applies style dynamically
+ stylize: function(){
+ var objClass,
+ regex = new RegExp(ROOT_SELECTOR, 'g');
+ if (this.view.style.length === 0 || this.view.$().size() === 0) {
+ return;
+ }
+ // Own style
+ // Object gets own class name ".agility_123", and <head> gets a corresponding <style>
+ if (this.view.hasOwnProperty('style')) {
+ objClass = 'agility_' + this._id;
+ var styleStr = this.view.style.replace(regex, '.'+objClass);
+ $('head', window.document).append('<style type="text/css">'+styleStr+'</style>');
+ this.view.$().addClass(objClass);
+ }
+ // Inherited style
+ // Object inherits CSS class name from first ancestor to have own view.style
+ else {
+ // Returns id of first ancestor to have 'own' view.style
+ var ancestorWithStyle = function(object) {
+ while (object !== null) {
+ object = Object.getPrototypeOf(object);
+ if (object.view.hasOwnProperty('style'))
+ return object._id;
+ }
+ return undefined;
+ }; // ancestorWithStyle
+
+ var ancestorId = ancestorWithStyle(this);
+ objClass = 'agility_' + ancestorId;
+ this.view.$().addClass(objClass);
+ }
+ return this;
+ }
+
+ }, // view prototype
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Controller
+ //
+ // Default controllers, i.e. event handlers. Event handlers that start
+ // with '_' are of internal use only, and take precedence over any other
+ // handler without that prefix. (See trigger()).
+ //
+
+ controller: {
+
+ // Triggered after self creation
+ _create: function(event){
+ this.view.stylize();
+ this.view.bindings(); // Model-View bindings
+ this.view.sync(); // syncs View with Model
+ },
+
+ // Triggered upon removing self
+ _destroy: function(event){
+ // destroy any appended agility objects
+ this._container.empty();
+ // destroy self
+ this.view.$().remove();
+ },
+
+ // Triggered after child obj is appended to container
+ _append: function(event, obj, selector){
+ this.view.$(selector).append(obj.view.$());
+ },
+
+ // Triggered after child obj is prepended to container
+ _prepend: function(event, obj, selector){
+ this.view.$(selector).prepend(obj.view.$());
+ },
+
+ // Triggered after child obj is inserted in the container
+ _before: function(event, obj, selector){
+ if (!selector) throw 'agility.js: _before needs a selector';
+ this.view.$(selector).before(obj.view.$());
+ },
+
+ // Triggered after child obj is inserted in the container
+ _after: function(event, obj, selector){
+ if (!selector) throw 'agility.js: _after needs a selector';
+ this.view.$(selector).after(obj.view.$());
+ },
+
+ // Triggered after a child obj is removed from container (or self-removed)
+ _remove: function(event, id){
+ },
+
+ // Triggered after model is changed
+ '_change': function(event){
+ }
+
+ }, // controller prototype
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Shortcuts
+ //
+
+ //
+ // Self
+ //
+ destroy: function() {
+ this.trigger('destroy', this._id); // parent must listen to 'remove' event and handle container removal!
+ // can't return this as it might not exist anymore!
+ },
+ parent: function(){
+ return this._parent;
+ },
+
+ //
+ // _container shortcuts
+ //
+ append: function(){
+ this._container.append.apply(this, arguments);
+ return this; // for chainable calls
+ },
+ prepend: function(){
+ this._container.prepend.apply(this, arguments);
+ return this; // for chainable calls
+ },
+ after: function(){
+ this._container.after.apply(this, arguments);
+ return this; // for chainable calls
+ },
+ before: function(){
+ this._container.before.apply(this, arguments);
+ return this; // for chainable calls
+ },
+ remove: function(){
+ this._container.remove.apply(this, arguments);
+ return this; // for chainable calls
+ },
+ size: function(){
+ return this._container.size.apply(this, arguments);
+ },
+ each: function(){
+ return this._container.each.apply(this, arguments);
+ },
+ empty: function(){
+ return this._container.empty.apply(this, arguments);
+ },
+
+ //
+ // _events shortcuts
+ //
+ bind: function(){
+ this._events.bind.apply(this, arguments);
+ return this; // for chainable calls
+ },
+ trigger: function(){
+ this._events.trigger.apply(this, arguments);
+ return this; // for chainable calls
+ }
+
+ }; // prototype
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Main object builder
+ //
+
+ // Main agility object builder
+ agility = function(){
+
+ // Real array of arguments
+ var args = Array.prototype.slice.call(arguments, 0),
+
+ // Object to be returned by builder
+ object = {},
+
+ prototype = defaultPrototype;
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Define object prototype
+ //
+
+ // Inherit object prototype
+ if (typeof args[0] === "object" && util.isAgility(args[0])) {
+ prototype = args[0];
+ args.shift(); // remaining args now work as though object wasn't specified
+ } // build from agility object
+
+ // Build object from prototype as well as the individual prototype parts
+ // This enables differential inheritance at the sub-object level, e.g. object.view.format
+ object = Object.create(prototype);
+ object.model = Object.create(prototype.model);
+ object.view = Object.create(prototype.view);
+ object.controller = Object.create(prototype.controller);
+ object._container = Object.create(prototype._container);
+ object._events = Object.create(prototype._events);
+
+ // Fresh 'own' properties (i.e. properties that are not inherited at all)
+ object._id = idCounter++;
+ object._parent = null;
+ object._events.data = {}; // event bindings will happen below
+ object._container.children = {};
+ object.view.$root = $(); // empty jQuery object
+
+ // Cloned own properties (i.e. properties that are inherited by direct copy instead of by prototype chain)
+ // This prevents children from altering parents models
+ object.model._data = prototype.model._data ? $.extend(true, {}, prototype.model._data) : {};
+ object._data = prototype._data ? $.extend(true, {}, prototype._data) : {};
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Extend model, view, controller
+ //
+
+ // Just the default prototype
+ if (args.length === 0) {
+ }
+
+ // Prototype differential from single {model,view,controller} object
+ else if (args.length === 1 && typeof args[0] === 'object' && (args[0].model || args[0].view || args[0].controller) ) {
+ for (var prop in args[0]) {
+ if (prop === 'model') {
+ $.extend(object.model._data, args[0].model);
+ }
+ else if (prop === 'view') {
+ $.extend(object.view, args[0].view);
+ }
+ else if (prop === 'controller') {
+ $.extend(object.controller, args[0].controller);
+ util.extendController(object);
+ }
+ // User-defined methods
+ else {
+ object[prop] = args[0][prop];
+ }
+ }
+ } // {model, view, controller} arg
+
+ // Prototype differential from separate {model}, {view}, {controller} arguments
+ else {
+
+ // Model from string
+ if (typeof args[0] === 'object') {
+ $.extend(object.model._data, args[0]);
+ }
+ else if (args[0]) {
+ throw "agility.js: unknown argument type (model)";
+ }
+
+ // View format from shorthand string (..., '<div>whatever</div>', ...)
+ if (typeof args[1] === 'string') {
+ object.view.format = args[1]; // extend view with .format
+ }
+ // View from object (..., {format:'<div>whatever</div>'}, ...)
+ else if (typeof args[1] === 'object') {
+ $.extend(object.view, args[1]);
+ }
+ else if (args[1]) {
+ throw "agility.js: unknown argument type (view)";
+ }
+
+ // View style from shorthand string (..., ..., 'p {color:red}', ...)
+ if (typeof args[2] === 'string') {
+ object.view.style = args[2];
+ args.splice(2, 1); // so that controller code below works
+ }
+
+ // Controller from object (..., ..., {method:function(){}})
+ if (typeof args[2] === 'object') {
+ $.extend(object.controller, args[2]);
+ util.extendController(object);
+ }
+ else if (args[2]) {
+ throw "agility.js: unknown argument type (controller)";
+ }
+
+ } // ({model}, {view}, {controller}) args
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Bootstrap: Bindings, initializations, etc
+ //
+
+ // Save model's initial state (so it can be .reset() later)
+ object.model._initData = $.extend({}, object.model._data);
+
+ // object.* will have their 'this' === object. This should come before call to object.* below.
+ util.proxyAll(object, object);
+
+ // Initialize $root, needed for DOM events binding below
+ object.view.render();
+
+ // Bind all controllers to their events
+
+ var bindEvent = function(ev, handler){
+ if (typeof handler === 'function') {
+ object.bind(ev, handler);
+ }
+ };
+
+ for (var eventStr in object.controller) {
+ var events = eventStr.split(';');
+ var handler = object.controller[eventStr];
+ $.each(events, function(i, ev){
+ ev = ev.trim();
+ bindEvent(ev, handler);
+ });
+ }
+
+
+ // Auto-triggers create event
+ object.trigger('create');
+
+ return object;
+
+ }; // agility
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Global objects
+ //
+
+ // $$.document is a special Agility object, whose view is attached to <body>
+ // This object is the main entry point for all DOM operations
+ agility.document = agility({
+ view: {
+ $: function(selector){ return selector ? $(selector, 'body') : $('body'); }
+ },
+ controller: {
+ // Override default controller
+ // (don't render, don't stylize, etc)
+ _create: function(){}
+ }
+ });
+
+ // Shortcut to prototype for plugins
+ agility.fn = defaultPrototype;
+
+ // isAgility test
+ agility.isAgility = function(obj) {
+ if (typeof obj !== 'object') return false;
+ return util.isAgility(obj);
+ };
+
+ // Globals
+ window.agility = window.$$ = agility;
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Bundled plugin: persist
+ //
+
+ // Main initializer
+ agility.fn.persist = function(adapter, params){
+ var id = 'id'; // name of id attribute
+
+ this._data.persist = $.extend({adapter:adapter}, params);
+ this._data.persist.openRequests = 0;
+ if (params && params.id) {
+ id = params.id;
+ }
+
+ // Creates persist methods
+
+ // .save()
+ // Creates new model or update existing one, depending on whether model has 'id' property
+ this.save = function(){
+ var self = this;
+ if (this._data.persist.openRequests === 0) {
+ this.trigger('persist:start');
+ }
+ this._data.persist.openRequests++;
+ this._data.persist.adapter.call(this, {
+ type: this.model.get(id) ? 'PUT' : 'POST', // update vs. create
+ id: this.model.get(id),
+ data: this.model.get(),
+ complete: function(){
+ self._data.persist.openRequests--;
+ if (self._data.persist.openRequests === 0) {
+ self.trigger('persist:stop');
+ }
+ },
+ success: function(data, textStatus, jqXHR){
+ if (data[id]) {
+ // id in body
+ self.model.set({id:data[id]}, {silent:true});
+ }
+ else if (jqXHR.getResponseHeader('Location')) {
+ // parse id from Location
+ self.model.set({ id: jqXHR.getResponseHeader('Location').match(/\/([0-9]+)$/)[1] }, {silent:true});
+ }
+ self.trigger('persist:save:success');
+ },
+ error: function(){
+ self.trigger('persist:error');
+ self.trigger('persist:save:error');
+ }
+ });
+
+ return this; // for chainable calls
+ }; // save()
+
+ // .load()
+ // Loads model with given id
+ this.load = function(){
+ var self = this;
+ if (this.model.get(id) === undefined) throw 'agility.js: load() needs model id';
+
+ if (this._data.persist.openRequests === 0) {
+ this.trigger('persist:start');
+ }
+ this._data.persist.openRequests++;
+ this._data.persist.adapter.call(this, {
+ type: 'GET',
+ id: this.model.get(id),
+ complete: function(){
+ self._data.persist.openRequests--;
+ if (self._data.persist.openRequests === 0) {
+ self.trigger('persist:stop');
+ }
+ },
+ success: function(data, textStatus, jqXHR){
+ self.model.set(data);
+ self.trigger('persist:load:success');
+ },
+ error: function(){
+ self.trigger('persist:error');
+ self.trigger('persist:load:error');
+ }
+ });
+
+ return this; // for chainable calls
+ }; // load()
+
+ // .erase()
+ // Erases model with given id
+ this.erase = function(){
+ var self = this;
+ if (this.model.get(id) === undefined) throw 'agility.js: erase() needs model id';
+
+ if (this._data.persist.openRequests === 0) {
+ this.trigger('persist:start');
+ }
+ this._data.persist.openRequests++;
+ this._data.persist.adapter.call(this, {
+ type: 'DELETE',
+ id: this.model.get(id),
+ complete: function(){
+ self._data.persist.openRequests--;
+ if (self._data.persist.openRequests === 0) {
+ self.trigger('persist:stop');
+ }
+ },
+ success: function(data, textStatus, jqXHR){
+ self.destroy();
+ self.trigger('persist:erase:success');
+ },
+ error: function(){
+ self.trigger('persist:error');
+ self.trigger('persist:erase:error');
+ }
+ });
+
+ return this; // for chainable calls
+ }; // erase()
+
+ // .gather()
+ // Loads collection and appends/prepends (depending on method) at selector. All persistence data including adapter comes from proto, not self
+ this.gather = function(proto, method, selectorOrQuery, query){
+ var selector, self = this;
+ if (!proto) throw "agility.js plugin persist: gather() needs object prototype";
+ if (!proto._data.persist) throw "agility.js plugin persist: prototype doesn't seem to contain persist() data";
+
+ // Determines arguments
+ if (query) {
+ selector = selectorOrQuery;
+ }
+ else {
+ if (typeof selectorOrQuery === 'string') {
+ selector = selectorOrQuery;
+ }
+ else {
+ selector = undefined;
+ query = selectorOrQuery;
+ }
+ }
+
+ if (this._data.persist.openRequests === 0) {
+ this.trigger('persist:start');
+ }
+ this._data.persist.openRequests++;
+ proto._data.persist.adapter.call(proto, {
+ type: 'GET',
+ data: query,
+ complete: function(){
+ self._data.persist.openRequests--;
+ if (self._data.persist.openRequests === 0) {
+ self.trigger('persist:stop');
+ }
+ },
+ success: function(data){
+ $.each(data, function(index, entry){
+ var obj = $$(proto, entry);
+ if (typeof method === 'string') {
+ self[method](obj, selector);
+ }
+ });
+ self.trigger('persist:gather:success', {data:data});
+ },
+ error: function(){
+ self.trigger('persist:error');
+ self.trigger('persist:gather:error');
+ }
+ });
+
+ return this; // for chainable calls
+ }; // gather()
+
+ return this; // for chainable calls
+ }; // fn.persist()
+
+ // Persistence adapters
+ // These are functions. Required parameters:
+ // {type: 'GET' || 'POST' || 'PUT' || 'DELETE'}
+ agility.adapter = {};
+
+ // RESTful JSON adapter using jQuery's ajax()
+ agility.adapter.restful = function(_params){
+ var params = $.extend({
+ dataType: 'json',
+ url: (this._data.persist.baseUrl || 'api/') + this._data.persist.collection + (_params.id ? '/'+_params.id : '')
+ }, _params);
+ $.ajax(params);
+ };
+
+})(window);
View
9,597 architecture-examples/agilityjs/components/jquery/jquery.js
9,597 additions, 0 deletions not shown
View
414 architecture-examples/agilityjs/components/todomvc-common/base.css
@@ -0,0 +1,414 @@
+html,
+body {
+ margin: 0;
+ padding: 0;
+}
+
+button {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ background: none;
+ font-size: 100%;
+ vertical-align: baseline;
+ font-family: inherit;
+ color: inherit;
+ -webkit-appearance: none;
+ /*-moz-appearance: none;*/
+ -ms-appearance: none;
+ -o-appearance: none;
+ appearance: none;
+}
+
+body {
+ font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ line-height: 1.4em;
+ background: #eaeaea url('bg.png');
+ color: #4d4d4d;
+ width: 550px;
+ margin: 0 auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-font-smoothing: antialiased;
+ -ms-font-smoothing: antialiased;
+ -o-font-smoothing: antialiased;
+ font-smoothing: antialiased;
+}
+
+#todoapp {
+ background: #fff;
+ background: rgba(255, 255, 255, 0.9);
+ margin: 130px 0 40px 0;
+ border: 1px solid #ccc;
+ position: relative;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 2px;
+ box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
+ 0 25px 50px 0 rgba(0, 0, 0, 0.15);
+}
+
+#todoapp:before {
+ content: '';
+ border-left: 1px solid #f5d6d6;
+ border-right: 1px solid #f5d6d6;
+ width: 2px;
+ position: absolute;
+ top: 0;
+ left: 40px;
+ height: 100%;
+}
+
+#todoapp input::-webkit-input-placeholder {
+ font-style: italic;
+}
+
+#todoapp input:-moz-placeholder {
+ font-style: italic;
+ color: #a9a9a9;
+}
+
+#todoapp h1 {
+ position: absolute;
+ top: -120px;
+ width: 100%;
+ font-size: 70px;
+ font-weight: bold;
+ text-align: center;
+ color: #b3b3b3;
+ color: rgba(255, 255, 255, 0.3);
+ text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
+ -webkit-text-rendering: optimizeLegibility;
+ -moz-text-rendering: optimizeLegibility;
+ -ms-text-rendering: optimizeLegibility;
+ -o-text-rendering: optimizeLegibility;
+ text-rendering: optimizeLegibility;
+}
+
+#header {
+ padding-top: 15px;
+ border-radius: inherit;
+}
+
+#header:before {
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ height: 15px;
+ z-index: 2;
+ border-bottom: 1px solid #6c615c;
+ background: #8d7d77;
+ background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
+ background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
+ background: -moz-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
+ background: -o-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
+ background: -ms-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
+ background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
+ border-top-left-radius: 1px;
+ border-top-right-radius: 1px;
+}
+
+#new-todo,
+.edit {
+ position: relative;
+ margin: 0;
+ width: 100%;
+ font-size: 24px;
+ font-family: inherit;
+ line-height: 1.4em;
+ border: 0;
+ outline: none;
+ color: inherit;
+ padding: 6px;
+ border: 1px solid #999;
+ box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+ -webkit-font-smoothing: antialiased;
+ -moz-font-smoothing: antialiased;
+ -ms-font-smoothing: antialiased;
+ -o-font-smoothing: antialiased;
+ font-smoothing: antialiased;
+}
+
+#new-todo {
+ padding: 16px 16px 16px 60px;
+ border: none;
+ background: rgba(0, 0, 0, 0.02);
+ z-index: 2;
+ box-shadow: none;
+}
+
+#main {
+ position: relative;
+ z-index: 2;
+ border-top: 1px dotted #adadad;
+}
+
+label[for='toggle-all'] {
+ display: none;
+}
+
+#toggle-all {
+ position: absolute;
+ top: -42px;
+ left: -4px;
+ width: 40px;
+ text-align: center;
+ border: none; /* Mobile Safari */
+}
+
+#toggle-all:before {
+ content: '»';
+ font-size: 28px;
+ color: #d9d9d9;
+ padding: 0 25px 7px;
+}
+
+#toggle-all:checked:before {
+ color: #737373;
+}
+
+#todo-list {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+#todo-list li {
+ position: relative;
+ font-size: 24px;
+ border-bottom: 1px dotted #ccc;
+}
+
+#todo-list li:last-child {
+ border-bottom: none;
+}
+
+#todo-list li.editing {
+ border-bottom: none;
+ padding: 0;
+}
+
+#todo-list li.editing .edit {
+ display: block;
+ width: 506px;
+ padding: 13px 17px 12px 17px;
+ margin: 0 0 0 43px;
+}
+
+#todo-list li.editing .view {
+ display: none;
+}
+
+#todo-list li .toggle {
+ text-align: center;
+ width: 40px;
+ /* auto, since non-WebKit browsers doesn't support input styling */
+ height: auto;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ margin: auto 0;
+ border: none; /* Mobile Safari */
+ -webkit-appearance: none;
+ /*-moz-appearance: none;*/
+ -ms-appearance: none;
+ -o-appearance: none;
+ appearance: none;
+}
+
+#todo-list li .toggle:after {
+ content: '';
+ line-height: 43px; /* 40 + a couple of pixels visual adjustment */
+ font-size: 20px;
+ color: #d9d9d9;
+ text-shadow: 0 -1px 0 #bfbfbf;
+}
+
+#todo-list li .toggle:checked:after {
+ color: #85ada7;
+ text-shadow: 0 1px 0 #669991;
+ bottom: 1px;
+ position: relative;
+}
+
+#todo-list li label {
+ word-break: break-word;
+ padding: 15px;
+ margin-left: 45px;
+ display: block;
+ line-height: 1.2;
+ -webkit-transition: color 0.4s;
+ -moz-transition: color 0.4s;
+ -ms-transition: color 0.4s;
+ -o-transition: color 0.4s;
+ transition: color 0.4s;
+}
+
+#todo-list li.completed label {
+ color: #a9a9a9;
+ text-decoration: line-through;
+}
+
+#todo-list li .destroy {
+ display: none;
+ position: absolute;
+ top: 0;
+ right: 10px;
+ bottom: 0;
+ width: 40px;
+ height: 40px;
+ margin: auto 0;
+ font-size: 22px;
+ color: #a88a8a;
+ -webkit-transition: all 0.2s;
+ -moz-transition: all 0.2s;
+ -ms-transition: all 0.2s;
+ -o-transition: all 0.2s;
+ transition: all 0.2s;
+}
+
+#todo-list li .destroy:hover {
+ text-shadow: 0 0 1px #000,
+ 0 0 10px rgba(199, 107, 107, 0.8);
+ -webkit-transform: scale(1.3);
+ -moz-transform: scale(1.3);
+ -ms-transform: scale(1.3);
+ -o-transform: scale(1.3);
+ transform: scale(1.3);
+}
+
+#todo-list li .destroy:after {
+ content: '';
+}
+
+#todo-list li:hover .destroy {
+ display: block;
+}
+
+#todo-list li .edit {
+ display: none;
+}
+
+#todo-list li.editing:last-child {
+ margin-bottom: -1px;
+}
+
+#footer {
+ color: #777;
+ padding: 0 15px;
+ position: absolute;
+ right: 0;
+ bottom: -31px;
+ left: 0;
+ height: 20px;
+ z-index: 1;
+ text-align: center;
+}
+
+#footer:before {
+ content: '';
+ position: absolute;
+ right: 0;
+ bottom: 31px;
+ left: 0;
+ height: 50px;
+ z-index: -1;
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
+ 0 6px 0 -3px rgba(255, 255, 255, 0.8),
+ 0 7px 1px -3px rgba(0, 0, 0, 0.3),
+ 0 43px 0 -6px rgba(255, 255, 255, 0.8),
+ 0 44px 2px -6px rgba(0, 0, 0, 0.2);
+}
+
+#todo-count {
+ float: left;
+ text-align: left;
+}
+
+#filters {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ position: absolute;
+ right: 0;
+ left: 0;
+}
+
+#filters li {
+ display: inline;
+}
+
+#filters li a {
+ color: #83756f;
+ margin: 2px;
+ text-decoration: none;
+}
+
+#filters li a.selected {
+ font-weight: bold;
+}
+
+#clear-completed {
+ float: right;
+ position: relative;
+ line-height: 20px;
+ text-decoration: none;
+ background: rgba(0, 0, 0, 0.1);
+ font-size: 11px;
+ padding: 0 10px;
+ border-radius: 3px;
+ box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
+}
+
+#clear-completed:hover {
+ background: rgba(0, 0, 0, 0.15);
+ box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
+}
+
+#info {
+ margin: 65px auto 0;
+ color: #a6a6a6;
+ font-size: 12px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
+ text-align: center;
+}
+
+#info a {
+ color: inherit;
+}
+
+/*
+ Hack to remove background from Mobile Safari.
+ Can't use it globally since it destroys checkboxes in Firefox and Opera
+*/
+@media screen and (-webkit-min-device-pixel-ratio:0) {
+ #toggle-all,
+ #todo-list li .toggle {
+ background: none;
+ }
+
+ #todo-list li .toggle {
+ height: 40px;
+ }
+
+ #toggle-all {
+ top: -56px;
+ left: -15px;
+ width: 65px;
+ height: 41px;
+ -webkit-transform: rotate(90deg);
+ transform: rotate(90deg);
+ -webkit-appearance: none;
+ appearance: none;
+ }
+}
+
+.hidden{
+ display:none;
+}
View
7 architecture-examples/agilityjs/components/todomvc-common/base.js
@@ -0,0 +1,7 @@
+(function () {
+ 'use strict';
+
+ if (location.hostname === 'todomvc.com') {
+ var _gaq=[['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script'));
+ }
+})();
View
BIN  architecture-examples/agilityjs/components/todomvc-common/bg.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
12 architecture-examples/agilityjs/components/todomvc-common/component.json
@@ -0,0 +1,12 @@
+{
+ "name": "todomvc-common",
+ "version": "0.1.0",
+ "gitHead": "63628bfbeff187f6db5bc982a0a222e66e62901e",
+ "_id": "todomvc-common@0.1.0",
+ "readme": "ERROR: No README.md file found!",
+ "description": "ERROR: No README.md file found!",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/TasteJS/todomvc-common.git"
+ }
+}
View
11 architecture-examples/agilityjs/index.html
@@ -4,10 +4,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Agility.js • TodoMVC</title>
- <link rel="stylesheet" href="../../assets/base.css">
- <!--[if IE]>
- <script src="../../assets/ie.js"></script>
- <![endif]-->
+ <link rel="stylesheet" href="components/todomvc-common/base.css">
</head>
<body>
<section id="todoapp">
@@ -50,9 +47,9 @@
<p>Created by <a href="http://github.com/tshm/todomvc/">Tosh Shimayama</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
- <script src="../../assets/base.js"></script>
- <script src="../../assets/jquery.min.js"></script>
- <script src="js/lib/agility.min.js"></script>
+ <script src="components/todomvc-common/base.js"></script>
+ <script src="components/jquery/jquery.js"></script>
+ <script src="components/agility/agility.js"></script>
<script src="js/localstorage.js"></script>
<script src="js/app.js"></script>
</body>
View
77 architecture-examples/agilityjs/js/lib/agility.min.js
@@ -1,77 +0,0 @@
-/*
-
- Agility.js
- Copyright (c) Artur B. Adib, 2011
- http://agilityjs.com
-
- Licensed under the MIT license
- http://www.opensource.org/licenses/mit-license.php
-
-*/
-
-(function(window,undefined){if(!window.jQuery){throw"agility.js: jQuery not found";}
-var document=window.document,location=window.location,$=jQuery,agility,util={},defaultPrototype={},idCounter=0,ROOT_SELECTOR='&';if(!Object.create||Object.create.toString().search(/native code/i)<0){Object.create=function(obj){var Aux=function(){};$.extend(Aux.prototype,obj);return new Aux();};}
-if(!Object.getPrototypeOf||Object.getPrototypeOf.toString().search(/native code/i)<0){if(typeof"test".__proto__==="object"){Object.getPrototypeOf=function(object){return object.__proto__;};}else{Object.getPrototypeOf=function(object){return object.constructor.prototype;};}}
-util.isAgility=function(obj){return obj._agility===true;};util.proxyAll=function(obj,dest){if(!obj||!dest){throw"agility.js: util.proxyAll needs two arguments";}
-for(var attr1 in obj){var proxied=obj[attr1];if(typeof obj[attr1]==='function'){proxied=obj[attr1]._noProxy?obj[attr1]:$.proxy(obj[attr1]._preProxy||obj[attr1],dest);proxied._preProxy=obj[attr1]._noProxy?undefined:(obj[attr1]._preProxy||obj[attr1]);obj[attr1]=proxied;}
-else if(typeof obj[attr1]==='object'){for(var attr2 in obj[attr1]){var proxied2=obj[attr1][attr2];if(typeof obj[attr1][attr2]==='function'){proxied2=obj[attr1][attr2]._noProxy?obj[attr1][attr2]:$.proxy(obj[attr1][attr2]._preProxy||obj[attr1][attr2],dest);proxied2._preProxy=obj[attr1][attr2]._noProxy?undefined:(obj[attr1][attr2]._preProxy||obj[attr1][attr2]);proxied[attr2]=proxied2;}}
-obj[attr1]=proxied;}}};util.reverseEvents=function(obj,eventType){var events=$(obj).data('events');if(events!==undefined&&events[eventType]!==undefined){var reverseEvents=[];for(var e in events[eventType]){if(!events[eventType].hasOwnProperty(e))continue;reverseEvents.unshift(events[eventType][e]);}
-events[eventType]=reverseEvents;}};util.size=function(obj){var size=0,key;for(key in obj){size++;}
-return size;};util.extendController=function(object){for(var controllerName in object.controller){(function(){var matches,extend,eventName,previousHandler,currentHandler,newHandler;if(typeof object.controller[controllerName]==='function'){matches=controllerName.match(/^(\~)*(.+)/);extend=matches[1];eventName=matches[2];if(!extend)return;previousHandler=object.controller[eventName]?(object.controller[eventName]._preProxy||object.controller[eventName]):undefined;currentHandler=object.controller[controllerName];newHandler=function(){if(previousHandler)previousHandler.apply(this,arguments);if(currentHandler)currentHandler.apply(this,arguments);};object.controller[eventName]=newHandler;delete object.controller[controllerName];}})();}};defaultPrototype={_agility:true,_container:{_insertObject:function(obj,selector,method){var self=this;if(!util.isAgility(obj)){throw"agility.js: append argument is not an agility object";}
-this._container.children[obj._id]=obj;this.trigger(method,[obj,selector]);obj.bind('destroy',function(event,id){self._container.remove(id);});return this;},append:function(obj,selector){return this._container._insertObject.call(this,obj,selector,'append');},prepend:function(obj,selector){return this._container._insertObject.call(this,obj,selector,'prepend');},after:function(obj,selector){return this._container._insertObject.call(this,obj,selector,'after');},before:function(obj,selector){return this._container._insertObject.call(this,obj,selector,'before');},remove:function(id){delete this._container.children[id];this.trigger('remove',id);return this;},each:function(fn){$.each(this._container.children,fn);return this;},empty:function(){this.each(function(){this.destroy();});return this;},size:function(){return util.size(this._container.children);}},_events:{parseEventStr:function(eventStr){var eventObj={type:eventStr},spacePos=eventStr.search(/\s/);if(spacePos>-1){eventObj.type=eventStr.substr(0,spacePos);eventObj.selector=eventStr.substr(spacePos+1);}
-return eventObj;},bind:function(eventStr,fn){var eventObj=this._events.parseEventStr(eventStr);if(eventObj.selector){if(eventObj.selector===ROOT_SELECTOR){this.view.$().bind(eventObj.type,fn);}
-else{this.view.$().delegate(eventObj.selector,eventObj.type,fn);}}
-else{$(this._events.data).bind(eventObj.type,fn);}
-return this;},trigger:function(eventStr,params){var eventObj=this._events.parseEventStr(eventStr);if(eventObj.selector){if(eventObj.selector===ROOT_SELECTOR){this.view.$().trigger(eventObj.type,params);}
-else{this.view.$().find(eventObj.selector).trigger(eventObj.type,params);}}
-else{$(this._events.data).trigger('_'+eventObj.type,params);util.reverseEvents(this._events.data,'pre:'+eventObj.type);$(this._events.data).trigger('pre:'+eventObj.type,params);util.reverseEvents(this._events.data,'pre:'+eventObj.type);$(this._events.data).trigger(eventObj.type,params);$(this._events.data).trigger('post:'+eventObj.type,params);}
-return this;}},model:{set:function(arg,params){var self=this;var modified=[];if(typeof arg==='object'){if(params&&params.reset){this.model._data=$.extend({},arg);}
-else{$.extend(this.model._data,arg);}
-for(var key in arg){modified.push(key);}}
-else{throw"agility.js: unknown argument type in model.set()";}
-if(params&&params.silent===true)return this;this.trigger('change');$.each(modified,function(index,val){self.trigger('change:'+val);});return this;},get:function(arg){if(arg===undefined){return this.model._data;}
-if(typeof arg==='string'){return this.model._data[arg];}
-throw'agility.js: unknown argument for getter';},reset:function(){this.model.set(this.model._initData,{reset:true});return this;},size:function(){return util.size(this.model._data);},each:function(fn){$.each(this.model._data,fn);return this;}},view:{format:'<div/>',style:'',$:function(selector){return(!selector||selector===ROOT_SELECTOR)?this.view.$root:this.view.$root.find(selector);},render:function(){if(this.view.format.length===0){throw"agility.js: empty format in view.render()";}
-if(this.view.$root.size()===0){this.view.$root=$(this.view.format);}
-else{this.view.$root.html($(this.view.format).html());}
-if(this.view.$root.size()===0){throw'agility.js: could not generate html from format';}
-return this;},_parseBindStr:function(str){var obj={key:null,attr:[]},pairs=str.split(','),regex=/([a-zA-Z0-9_\-]+)(?:[\s=]+([a-zA-Z0-9_\-]+))?/,matched;if(pairs.length>0){matched=pairs[0].match(regex);if(matched){if(typeof(matched[2])==="undefined"||matched[2]===""){obj.key=matched[1];}else{obj.attr.push({attr:matched[1],attrVar:matched[2]});}}
-if(pairs.length>1){for(var i=1;i<pairs.length;i++){matched=pairs[i].match(regex);if(matched){if(typeof(matched[2])!=="undefined"){obj.attr.push({attr:matched[1],attrVar:matched[2]});}}}}}
-return obj;},bindings:function(){var self=this;var $rootNode=this.view.$().filter('[data-bind]');var $childNodes=this.view.$('[data-bind]');var createAttributePairClosure=function(bindData,node,i){var attrPair=bindData.attr[i];return function(){node.attr(attrPair.attr,self.model.get(attrPair.attrVar));};};$rootNode.add($childNodes).each(function(){var $node=$(this);var bindData=self.view._parseBindStr($node.data('bind'));var bindAttributesOneWay=function(){if(bindData.attr){for(var i=0;i<bindData.attr.length;i++){self.bind('_change:'+bindData.attr[i].attrVar,createAttributePairClosure(bindData,$node,i));}}};if($node.is('input[type="checkbox"]')){self.bind('_change:'+bindData.key,function(){$node.prop("checked",self.model.get(bindData.key));});$node.change(function(){var obj={};obj[bindData.key]=$(this).prop("checked");self.model.set(obj);});bindAttributesOneWay();}
-else if($node.is('select')){self.bind('_change:'+bindData.key,function(){var nodeName=$node.attr('name');var modelValue=self.model.get(bindData.key);$node.val(modelValue);});$node.change(function(){var obj={};obj[bindData.key]=$node.val();self.model.set(obj);});bindAttributesOneWay();}
-else if($node.is('input[type="radio"]')){self.bind('_change:'+bindData.key,function(){var nodeName=$node.attr('name');var modelValue=self.model.get(bindData.key);$node.siblings('input[name="'+nodeName+'"]').filter('[value="'+modelValue+'"]').prop("checked",true);});$node.change(function(){if(!$node.prop("checked"))return;var obj={};obj[bindData.key]=$node.val();self.model.set(obj);});bindAttributesOneWay();}
-else if($node.is('input[type="search"]')){self.bind('_change:'+bindData.key,function(){$node.val(self.model.get(bindData.key));});$node.keypress(function(){setTimeout(function(){var obj={};obj[bindData.key]=$node.val();self.model.set(obj);},50);});bindAttributesOneWay();}
-else if($node.is('input[type="text"], textarea')){self.bind('_change:'+bindData.key,function(){$node.val(self.model.get(bindData.key));});$node.change(function(){var obj={};obj[bindData.key]=$(this).val();self.model.set(obj);});bindAttributesOneWay();}
-else{if(bindData.key){self.bind('_change:'+bindData.key,function(){if(self.model.get(bindData.key)){$node.text(self.model.get(bindData.key).toString());}else{$node.text('');}});}
-bindAttributesOneWay();}});return this;},sync:function(){var self=this;this.model.each(function(key,val){self.trigger('_change:'+key);});if(this.model.size()>0){this.trigger('_change');}
-return this;},stylize:function(){var objClass,regex=new RegExp(ROOT_SELECTOR,'g');if(this.view.style.length===0||this.view.$().size()===0){return;}
-if(this.view.hasOwnProperty('style')){objClass='agility_'+this._id;var styleStr=this.view.style.replace(regex,'.'+objClass);$('head',window.document).append('<style type="text/css">'+styleStr+'</style>');this.view.$().addClass(objClass);}
-else{var ancestorWithStyle=function(object){while(object!==null){object=Object.getPrototypeOf(object);if(object.view.hasOwnProperty('style'))
-return object._id;}
-return undefined;};var ancestorId=ancestorWithStyle(this);objClass='agility_'+ancestorId;this.view.$().addClass(objClass);}
-return this;}},controller:{_create:function(event){this.view.stylize();this.view.bindings();this.view.sync();},_destroy:function(event){this._container.empty();this.view.$().remove();},_append:function(event,obj,selector){this.view.$(selector).append(obj.view.$());},_prepend:function(event,obj,selector){this.view.$(selector).prepend(obj.view.$());},_before:function(event,obj,selector){if(!selector)throw'agility.js: _before needs a selector';this.view.$(selector).before(obj.view.$());},_after:function(event,obj,selector){if(!selector)throw'agility.js: _after needs a selector';this.view.$(selector).after(obj.view.$());},_remove:function(event,id){},'_change':function(event){}},destroy:function(){this.trigger('destroy',this._id);},append:function(){this._container.append.apply(this,arguments);return this;},prepend:function(){this._container.prepend.apply(this,arguments);return this;},after:function(){this._container.after.apply(this,arguments);return this;},before:function(){this._container.before.apply(this,arguments);return this;},remove:function(){this._container.remove.apply(this,arguments);return this;},size:function(){return this._container.size.apply(this,arguments);},each:function(){return this._container.each.apply(this,arguments);},empty:function(){return this._container.empty.apply(this,arguments);},bind:function(){this._events.bind.apply(this,arguments);return this;},trigger:function(){this._events.trigger.apply(this,arguments);return this;}};agility=function(){var args=Array.prototype.slice.call(arguments,0),object={},prototype=defaultPrototype;if(typeof args[0]==="object"&&util.isAgility(args[0])){prototype=args[0];args.shift();}
-object=Object.create(prototype);object.model=Object.create(prototype.model);object.view=Object.create(prototype.view);object.controller=Object.create(prototype.controller);object._container=Object.create(prototype._container);object._events=Object.create(prototype._events);object._id=idCounter++;object._events.data={};object._container.children={};object.view.$root=$();object.model._data=prototype.model._data?$.extend(true,{},prototype.model._data):{};object._data=prototype._data?$.extend(true,{},prototype._data):{};if(args.length===0){}
-else if(args.length===1&&typeof args[0]==='object'&&(args[0].model||args[0].view||args[0].controller)){for(var prop in args[0]){if(prop==='model'){$.extend(object.model._data,args[0].model);}
-else if(prop==='view'){$.extend(object.view,args[0].view);}
-else if(prop==='controller'){$.extend(object.controller,args[0].controller);util.extendController(object);}
-else{object[prop]=args[0][prop];}}}
-else{if(typeof args[0]==='object'){$.extend(object.model._data,args[0]);}
-else if(args[0]){throw"agility.js: unknown argument type (model)";}
-if(typeof args[1]==='string'){object.view.format=args[1];}
-else if(typeof args[1]==='object'){$.extend(object.view,args[1]);}
-else if(args[1]){throw"agility.js: unknown argument type (view)";}
-if(typeof args[2]==='string'){object.view.style=args[2];args.splice(2,1);}
-if(typeof args[2]==='object'){$.extend(object.controller,args[2]);util.extendController(object);}
-else if(args[2]){throw"agility.js: unknown argument type (controller)";}}
-object.model._initData=$.extend({},object.model._data);util.proxyAll(object,object);object.view.render();for(var ev in object.controller){if(typeof object.controller[ev]==='function'){object.bind(ev,object.controller[ev]);}}
-object.trigger('create');return object;};agility.document=agility({view:{$:function(selector){return selector?$(selector,'body'):$('body');}},controller:{_create:function(){}}});agility.fn=defaultPrototype;agility.isAgility=function(obj){if(typeof obj!=='object')return false;return util.isAgility(obj);};window.agility=window.$$=agility;agility.fn.persist=function(adapter,params){var id='id';this._data.persist=$.extend({adapter:adapter},params);this._data.persist.openRequests=0;if(params&&params.id){id=params.id;}
-this.save=function(){var self=this;if(this._data.persist.openRequests===0){this.trigger('persist:start');}
-this._data.persist.openRequests++;this._data.persist.adapter.call(this,{type:this.model.get(id)?'PUT':'POST',id:this.model.get(id),data:this.model.get(),complete:function(){self._data.persist.openRequests--;if(self._data.persist.openRequests===0){self.trigger('persist:stop');}},success:function(data,textStatus,jqXHR){if(data[id]){self.model.set({id:data[id]},{silent:true});}
-else if(jqXHR.getResponseHeader('Location')){self.model.set({id:jqXHR.getResponseHeader('Location').match(/\/([0-9]+)$/)[1]},{silent:true});}
-self.trigger('persist:save:success');},error:function(){self.trigger('persist:error');self.trigger('persist:save:error');}});return this;};this.load=function(){var self=this;if(this.model.get(id)===undefined)throw'agility.js: load() needs model id';if(this._data.persist.openRequests===0){this.trigger('persist:start');}
-this._data.persist.openRequests++;this._data.persist.adapter.call(this,{type:'GET',id:this.model.get(id),complete:function(){self._data.persist.openRequests--;if(self._data.persist.openRequests===0){self.trigger('persist:stop');}},success:function(data,textStatus,jqXHR){self.model.set(data);self.trigger('persist:load:success');},error:function(){self.trigger('persist:error');self.trigger('persist:load:error');}});return this;};this.erase=function(){var self=this;if(this.model.get(id)===undefined)throw'agility.js: erase() needs model id';if(this._data.persist.openRequests===0){this.trigger('persist:start');}
-this._data.persist.openRequests++;this._data.persist.adapter.call(this,{type:'DELETE',id:this.model.get(id),complete:function(){self._data.persist.openRequests--;if(self._data.persist.openRequests===0){self.trigger('persist:stop');}},success:function(data,textStatus,jqXHR){self.destroy();self.trigger('persist:erase:success');},error:function(){self.trigger('persist:error');self.trigger('persist:erase:error');}});return this;};this.gather=function(proto,method,selectorOrQuery,query){var selector,self=this;if(!proto)throw"agility.js plugin persist: gather() needs object prototype";if(!proto._data.persist)throw"agility.js plugin persist: prototype doesn't seem to contain persist() data";if(query){selector=selectorOrQuery;}
-else{if(typeof selectorOrQuery==='string'){selector=selectorOrQuery;}
-else{selector=undefined;query=selectorOrQuery;}}
-if(this._data.persist.openRequests===0){this.trigger('persist:start');}
-this._data.persist.openRequests++;proto._data.persist.adapter.call(proto,{type:'GET',data:query,complete:function(){self._data.persist.openRequests--;if(self._data.persist.openRequests===0){self.trigger('persist:stop');}},success:function(data){$.each(data,function(index,entry){var obj=$$(proto,entry);if(typeof method==='string'){self[method](obj,selector);}});self.trigger('persist:gather:success',{data:data});},error:function(){self.trigger('persist:error');self.trigger('persist:gather:error');}});return this;};return this;};agility.adapter={};agility.adapter.restful=function(_params){var params=$.extend({dataType:'json',url:(this._data.persist.baseUrl||'api/')+this._data.persist.collection+(_params.id?'/'+_params.id:'')},_params);$.ajax(params);};})(window);
Please sign in to comment.
Something went wrong with that request. Please try again.