diff --git a/builder.json b/builder.json index ceb7040389c..2b19fdf10ec 100644 --- a/builder.json +++ b/builder.json @@ -15,12 +15,12 @@ "isDefault": true }, "can/observe": { - "name": "can.Observe", - "description": "Observable key-value bindings", + "name": "can.Map, can.List, can.compute", + "description": "Observables and key-value bindings", "type": "core", "isDefault": true }, - "can/observe/compute": { + "can/compute": { "name": "can.compute", "description": "can.compute lets you make observable values", "type": "core", @@ -67,7 +67,7 @@ "type": "plugin", "description": "Live-binding Handlebars and Mustache views" }, - "can/view/mustache/bindings": { + "can/view/bindings": { "name": "can.view.bindings", "type": "plugin", "description": "Two way bindings and in-template declarative event handlers." @@ -92,27 +92,27 @@ "type": "plugin", "description": "Proxy construct methods" }, - "can/observe/delegate": { + "can/map/delegate": { "name": "can.Observe.delegate", "type": "plugin", "description": "Listen to Observe attributes" }, - "can/observe/setter": { + "can/map/setter": { "name": "can.Observe.setter", "type": "plugin", "description": "Use setter methods on Observes" }, - "can/observe/attributes": { + "can/map/attributes": { "name": "can.Observe.attributes", "type": "plugin", "description": "Define Observe attributes" }, - "can/observe/validations": { + "can/map/validations": { "name": "can.Observe.validations", "type": "plugin", "description": "Validate Observe attributes" }, - "can/observe/backup": { + "can/map/backup": { "name": "can.Observe.backup", "type": "plugin", "description": "Backup and restore an Observes state" diff --git a/can.md b/can.md index dc88d17f7fa..2f44e421754 100644 --- a/can.md +++ b/can.md @@ -15,7 +15,7 @@ framework: - [can.Construct] - inheritable constructor functions - [can.Control] - declarative event bindings - - [can.Observe], [can.Observe.List], [can.compute] - observable objects, list, and values. + - [can.Map], [can.List], [can.compute] - observable objects, list, and values. - [can.Model] - observes connected to a RESTful JSON interface - [can.view] - template loading, caching, rendering - [can.EJS] - live binding templates @@ -26,11 +26,11 @@ The following modules are typically distributed as plugins: - [can.Mustache] - Live binding Handlebars and Mustache templates - [can.Construct.proxy] - Proxy construct methods - [can.Construct.super] - Call super methods - - [can.Observe.delegate] - Listen to Observe attributes - - [can.Observe.setter] - Use setter methods on Observes - - [can.Observe.attributes] - Define Observe attributes - - [can.Observe.validations] - Validate attributes - - [can.Observe.backup] - Backup and restore an Observe's state + - [can.Map.delegate] - Listen to Observe attributes + - [can.Map.setter] - Use setter methods on Observes + - [can.Map.attributes] - Define Observe attributes + - [can.Map.validations] - Validate attributes + - [can.Map.backup] - Backup and restore an Observe's state - [can.Control.plugin] - Registers a jQuery plugin function for Controls[1] - [can.view.modifiers View modifiers] - Use jQuery modifiers to render views[1] diff --git a/changelog.md b/changelog.md index 4241961c82a..9475b7817bf 100644 --- a/changelog.md +++ b/changelog.md @@ -15,7 +15,7 @@ __1.1.6__ ( May 30 2013 ) - change: [Binding to an Observe.compute value is broken](https://github.com/bitovi/canjs/issues/372) - change: [Added validatesNumericalityOf to validations](https://github.com/bitovi/canjs/issues/370) - change: [Include can.Object in CanJS distribution](https://github.com/bitovi/canjs/issues/368) -- change: [can.Observe makes can.Deferred into an observable](https://github.com/bitovi/canjs/issues/367) +- change: [can.Map makes can.Deferred into an observable](https://github.com/bitovi/canjs/issues/367) - change: [Update sub to not break when str is undefined](https://github.com/bitovi/canjs/issues/365) - change: [List memory leak fix](https://github.com/bitovi/canjs/issues/363) - change: [Integrated incremental live lists](https://github.com/bitovi/canjs/issues/361) @@ -65,7 +65,7 @@ __1.1.5__ ( Mar 28 2013 ) - change: [fixing computes from converting type](https://github.com/bitovi/canjs/issues/278) - change: [can.view with Deferreds doesn't pass failures](https://github.com/bitovi/canjs/issues/276) - change: [HTML comments trip EJS rendering](https://github.com/bitovi/canjs/issues/271) -- change: [can.Observe.prototype.each overrides {{#each}} Mustache helper.](https://github.com/bitovi/canjs/issues/258) +- change: [can.Map.prototype.each overrides {{#each}} Mustache helper.](https://github.com/bitovi/canjs/issues/258) - change: [Any model with a "." in the key name will cause observe.js _set() to throw and error](https://github.com/bitovi/canjs/issues/257) - change: [Item.List splice method does not convert inserted elements to Item type](https://github.com/bitovi/canjs/issues/253) - change: [Mustache: DOM exception when applying certain block patterns](https://github.com/bitovi/canjs/issues/243) @@ -93,7 +93,7 @@ __1.1.4__ ( February 5, 2013 ) - fix: [Observe.List sort doesn't use custom method passed](https://github.com/bitovi/canjs/issues/169) - fix: [test&fix: null values crashing validations](https://github.com/bitovi/canjs/pull/145) - fix: [EJS rendering null value](https://github.com/bitovi/canjs/pull/118) -- fix: [can.Observe sort plugin doesn't trigger add events](https://github.com/bitovi/canjs/issues/205) +- fix: [can.Map sort plugin doesn't trigger add events](https://github.com/bitovi/canjs/issues/205) - fix: [Observe.List sort plugin erroring on item removal](https://github.com/bitovi/canjs/pull/88) - fix: [Live binding on observe.lists nested in an observe doesn't work](https://github.com/bitovi/canjs/issues/204) - fix: [Observe.List sort doesn't use custom method passed](https://github.com/bitovi/canjs/issues/169) @@ -114,7 +114,7 @@ __1.1.3__ ( December 11, 2012 ) - fix: [compute only updates once when a list's contents are replaced](https://github.com/bitovi/canjs/commit/9cb47dfabba5dbe3bef161e6aae4a5ce2965ac49) - add: [Updated jQuery hashchange plugin](https://github.com/bitovi/canjs/pull/201) - add: [Generate computes from an observe property](https://github.com/bitovi/canjs/issues/203) -- add: [Add can.Observe.List.prototype.replace](https://github.com/bitovi/canjs/issues/194) +- add: [Add can.List.prototype.replace](https://github.com/bitovi/canjs/issues/194) - add: [Return resolved data models in view callback](https://github.com/bitovi/canjs/issues/1log 83) @@ -161,18 +161,18 @@ __1.1.0__ ( November 13, 2012 ) - can.Control - add: [control does not listen to touchmove event on controller itself](https://github.com/bitovi/canjs/issues/104) - - can.Observe + - can.Map - add: [List binding on .length of an object](https://github.com/bitovi/canjs/issues/142) - fix: [validation error that incorrectly labels an attribute with a value of 0 as empty](https://github.com/bitovi/canjs/pull/132) - add: [you can now pluralise event names to listen to matching events of that type (rather than firing a single event)](https://github.com/bitovi/canjs/issues/122) - add: [compound sets now behave correctly](https://github.com/bitovi/canjs/issues/119) - - fix: [can.Observe.delegate sets wrong event.currentTarget](https://github.com/bitovi/canjs/issues/123) - - add: [ability to assign object as attribute type in can.Observe](https://github.com/bitovi/canjs/issues/107) + - fix: [can.Map.delegate sets wrong event.currentTarget](https://github.com/bitovi/canjs/issues/123) + - add: [ability to assign object as attribute type in can.Map](https://github.com/bitovi/canjs/issues/107) - can.Model - fix: [can.Model with attributes that are models gets corrupted when you call attr()](https://github.com/bitovi/canjs/pull/141) - add: [missing dependency to can/model](https://github.com/bitovi/canjs/pull/140) - - Moved can/model/elements to can/observe/elements and renamed `models` to `instances` + - Moved can/model/elements to can/map/elements and renamed `models` to `instances` - fix: [can.Model.List doesn't fire the change event on the expando properties ](https://github.com/bitovi/canjs/issues/129) __1.0.7__ (June 25nd 2012) diff --git a/component/component.js b/component/component.js index 09edc14e9ff..256ab1c7b05 100644 --- a/component/component.js +++ b/component/component.js @@ -1,4 +1,4 @@ -steal("can/util","can/control","can/observe","can/view/mustache","can/view/mustache/bindings",function(can){ +steal("can/util","can/control","can/observe","can/view/mustache","can/view/bindings",function(can){ var ignoreAttributesRegExp = /data-view-id|class|id/i /** diff --git a/component/component_test.js b/component/component_test.js index 1edd54bbf79..c4470545b91 100644 --- a/component/component_test.js +++ b/component/component_test.js @@ -74,7 +74,7 @@ test("basic tabs",function(){ }, removePanel: function(panel){ var panels = this.attr("panels"); - can.Map.startBatch(); + can.batch.start(); panels.splice(panels.indexOf(panel),1); if(panel === this.attr("active")){ if(panels.length){ @@ -83,7 +83,7 @@ test("basic tabs",function(){ this.removeAttr("active") } } - can.Map.stopBatch() + can.batch.stop() }, makeActive: function(panel){ this.attr("active",panel); diff --git a/component/examples/1.html b/component/examples/1.html index 2ccfd8e5f22..8f00fbf9903 100644 --- a/component/examples/1.html +++ b/component/examples/1.html @@ -10,7 +10,7 @@ - + + diff --git a/construct/proxy/proxy.js b/construct/proxy/proxy.js index b70b07f11b7..b2886480a2a 100644 --- a/construct/proxy/proxy.js +++ b/construct/proxy/proxy.js @@ -58,7 +58,7 @@ proxy = function( funcs ) { } can.Construct.proxy = can.Construct.prototype.proxy = proxy; // this corrects the case where can/control loads after can/construct/proxy, so static props don't have proxy - var correctedClasses = [can.Observe, can.Control, can.Model], + var correctedClasses = [can.Map, can.Control, can.Model], i = 0; for (; i < correctedClasses.length; i++ ) { if(correctedClasses[i]){ diff --git a/list/list.js b/list/list.js new file mode 100644 index 00000000000..8ff6a20ab9f --- /dev/null +++ b/list/list.js @@ -0,0 +1,1055 @@ +steal("can/util","can/map", function(can, Map){ + + + + // Helpers for `observable` lists. + var splice = [].splice, + /** + * @constructor can.List + * @inherits can.Map + * @download can/map + * @test can/map/qunit.html + * @parent canjs + * + * Use for observable array-like objects. + * + * @signature `new can.List([array])` + * + * Create an observable array-like object. + * + * @param {Array} [array] items to seed the List with + * @return {can.List} an instance of `can.List` with the elements from _array_ + * + * @signature `can.List([name,] [staticProperties,] instanceProperties)` + * + * Creates a new extended constructor function. + * + * This is deprecated. In CanJS 1.2, by default, calling the constructor function + * without `new` will create a `new` instance. Use [can.Construct.extend can.Map.extend] + * instead of calling the constructor to extend. + * + * @body + * + * ## Working with Lists + * + * `can.List` extends `[can.Map]`, so all the ways that you're used to working with + * Observes also work here, including [can.Map.prototype.bind bind], [can.Map.prototype.unbind unbind], + * and [can.Map.prototype.each each]. And just as you can directly read properties normally + * off of an Map, you can use array accessors ([]) to read elements directly off of a List. + * + * The one function of `can.Map` that works slightly differently is `attr`. As expected when working with + * arrays, top-level keys passed into `attr` are required to be numeric. (Strings may still be used when getting + * or modifying deep properties). Any top-level keys that are non-numeric are ignored. In addition, as might be + * expected, a call to argument-less `attr` returns an array instead of an object. + * + * Just as you shouldn't set properties of an Map directly, you shouldn't change elements + * of a List directly. Always use `attr` to set the elements of a List, or use [can.List.push push], + * [can.List.pop pop], [can.List.shift shift], [can.List.unshift unshift], or [can.List.splice splice]. + * + * Here is a tour through the forms of `can.List`'s `attr` that parallels the one found under [can.Map.prototype.attr attr]: + * + * @codestart + * var people = new can.List(['Alex', 'Bill']); + * + * // set an element: + * people.attr(0, 'Adam'); + * people[0] = 'Adam'; // don't do this! + * + * // get an element: + * people.attr(0); // 'Adam' + * people[0]; // 'Adam' + * + * // get all elements: + * people.attr(); // ['Adam', 'Bill'] + * + * // extend the array: + * people.attr(4, 'Charlie'); + * people.attr(); // ['Adam', 'Bill', undefined, undefined, 'Charlie'] + * + * // merge the elements: + * people.attr(['Alice', 'Bob', 'Eve']); + * people.attr(); // ['Alice', 'Bob', 'Eve', undefined, 'Charlie'] + * @codeend + * + * ## Listening to changes + * + * As with `can.Map`s, the real power of observable arrays comes from being able to + * react to changes in the member elements of the array. Lists emit five types of events: + * - the _change_ event fires on every change to a List. + * - the _set_ event is fired when an element is set. + * - the _add_ event is fired when an element is added to the List. + * - the _remove_ event is fired when an element is removed from the List. + * - the _length_ event is fired when the length of the List changes. + * + * This example presents a brief concrete survey of the times these events are fired: + * + * @codestart + * var list = new can.List(['Alice', 'Bob', 'Eve']); + * + * list.bind('change', function() { console.log('An element changed.'); }); + * list.bind('set', function() { console.log('An element was set.'); }); + * list.bind('add', function() { console.log('An element was added.'); }); + * list.bind('remove', function() { console.log('An element was removed.'); }); + * list.bind('length', function() { console.log('The length of the list changed.'); }); + * + * list.attr(0, 'Alexis'); // 'An element changed.' + * // 'An element was set.' + * + * list.attr(3, 'Xerxes'); // 'An element changed.' + * // 'An element was added.' + * // 'The length of the list was changed.' + * + * list.attr(['Adam', 'Bill']); // 'An element changed.' + * // 'An element was set.' + * // 'An element was changed.' + * // 'An element was set.' + * + * list.pop(); // 'An element changed.' + * // 'An element was removed.' + * // 'The length of the list was changed.' + * @codeend + * + * More information about binding to these events can be found under [can.List.attr attr]. + */ + list = Map( + /** + * @prototype + */ + { + setup: function( instances, options ) { + this.length = 0; + can.cid(this, ".map") + this._init = 1; + if( can.isDeferred(instances) ) { + this.replace(instances) + } else { + this.push.apply(this, can.makeArray(instances || [])); + } + // this change needs to be ignored + this.bind('change'+this._cid,can.proxy(this._changes,this)); + can.extend(this, options); + delete this._init; + }, + _triggerChange: function(attr, how, newVal, oldVal){ + + Map.prototype._triggerChange.apply(this,arguments) + // `batchTrigger` direct add and remove events... + if ( !~ attr.indexOf('.')){ + + if( how === 'add' ) { + can.batch.trigger(this, how, [newVal,+attr]); + can.batch.trigger(this,'length',[this.length]); + } else if( how === 'remove' ) { + can.batch.trigger(this, how, [oldVal, +attr]); + can.batch.trigger(this,'length',[this.length]); + } else { + can.batch.trigger(this,how,[newVal, +attr]) + } + + } + + }, + __get : function(attr){ + return attr ? this[attr] : this; + }, + ___set : function(attr, val){ + this[attr] = val; + if(+attr >= this.length){ + this.length = (+attr+1) + } + }, + _each: function(callback){ + var data = this.__get(); + for(var i =0; i < data.length; i++){ + callback(data[i],i) + } + }, + _bindsetup: Map.helpers.makeBindSetup("*"), + // Returns the serialized form of this list. + /** + * @hide + * Returns the serialized form of this list. + */ + serialize: function() { + return Map.helpers.serialize(this, 'serialize', []); + }, + /** + * @function can.List.prototype.each each + * @description Call a function on each element of a List. + * @signature `list.each( callback(item, index) )` + * + * `each` iterates through the Map, calling a function + * for each element. + * + * @param {function(*, Number)} callback the function to call for each element + * The value and index of each element will be passed as the first and second + * arguments, respectively, to the callback. If the callback returns false, + * the loop will stop. + * + * @return {can.List} this List, for chaining + * + * @body + * @codestart + * var i = 0; + * new can.Map([1, 10, 100]).each(function(element, index) { + * i += element; + * }); + * + * i; // 111 + * + * i = 0; + * new can.Map([1, 10, 100]).each(function(element, index) { + * i += element; + * if(index >= 1) { + * return false; + * } + * }); + * + * i; // 11 + * @codeend + */ + // + /** + * @function can.List.prototype.splice splice + * @description Insert and remove elements from a List. + * @signature `list.splice(index[, howMany[, ...newElements]])` + * @param {Number} index where to start removing or inserting elements + * + * @param {Number} [howMany] the number of elements to remove + * If _howMany_ is not provided, `splice` will all elements from `index` to the end of the List. + * + * @param {*} newElements elements to insert into the List + * + * @return {Array} the elements removed by `splice` + * + * @body + * `splice` lets you remove elements from and insert elements into a List. + * + * This example demonstrates how to do surgery on a list of numbers: + * + * @codestart + * var list = new can.List([0, 1, 2, 3]); + * + * // starting at index 2, remove one element and insert 'Alice' and 'Bob': + * list.splice(2, 1, 'Alice', 'Bob'); + * list.attr(); // [0, 1, 'Alice', 'Bob', 3] + * @codeend + * + * ## Events + * + * `splice` causes the List it's called on to emit _change_ events, + * _add_ events, _remove_ events, and _length_ events. If there are + * any elements to remove, a _change_ event, a _remove_ event, and a + * _length_ event will be fired. If there are any elements to insert, a + * separate _change_ event, an _add_ event, and a separate _length_ event + * will be fired. + * + * This slightly-modified version of the above example should help + * make it clear how `splice` causes events to be emitted: + * + * @codestart + * var list = new can.List(['a', 'b', 'c', 'd']); + * list.bind('change', function(ev, attr, how, newVals, oldVals) { + * console.log('change: ' + attr + ', ' + how + ', ' + newVals + ', ' + oldVals); + * }); + * list.bind('add', function(ev, newVals, where) { + * console.log('add: ' + newVals + ', ' + where); + * }); + * list.bind('remove', function(ev, oldVals, where) { + * console.log('remove: ' + oldVals + ', ' + where); + * }); + * list.bind('length', function(ev, length) { + * console.log('length: ' + length + ', ' + this.attr()); + * }); + * + * // starting at index 2, remove one element and insert 'Alice' and 'Bob': + * list.splice(2, 1, 'Alice', 'Bob'); // change: 2, 'remove', undefined, ['c'] + * // remove: ['c'], 2 + * // length: 5, ['a', 'b', 'Alice', 'Bob', 'd'] + * // change: 2, 'add', ['Alice', 'Bob'], ['c'] + * // add: ['Alice', 'Bob'], 2 + * // length: 5, ['a', 'b', 'Alice', 'Bob', 'd'] + * @codeend + * + * More information about binding to these events can be found under [can.List.attr attr]. + */ + splice: function( index, howMany ) { + var args = can.makeArray(arguments), + i; + + for ( i = 2; i < args.length; i++ ) { + var val = args[i]; + if ( Map.helpers.canMakeObserve(val) ) { + args[i] = Map.helpers.hookupBubble(val, "*", this, this.constructor.Map, this.constructor) + } + } + if ( howMany === undefined ) { + howMany = args[1] = this.length - index; + } + var removed = splice.apply(this, args); + can.batch.start(); + if ( howMany > 0 ) { + this._triggerChange(""+index, "remove", undefined, removed); + Map.helpers.unhookup(removed, this._cid); + } + if ( args.length > 2 ) { + this._triggerChange(""+index, "add", args.slice(2), removed); + } + can.batch.stop(); + return removed; + }, + /** + * @description Get or set elements in a List. + * @function can.List.prototype.attr attr + * @signature `list.attr()` + * + * Gets a collection of all the elements in this `can.List`. + * + * @return {Array} array with all the elements in this List. + * + * @signature `list.attr(index)` + * + * Reads a element from this `can.List`. + * + * @param {Number} index the element to read + * @return {*} the value at _index_. + * + * @signature `list.attr(index, value)` + * + * Assigns _value_ to the index _index_ on this `can.List`, expanding the list if necessary. + * + * @param {Number} index the element to set + * @param {*} the value to assign at _index_ + * @return {can.List} this List, for chaining + * + * @signature `list.attr(elements[, replaceCompletely])` + * + * Merges the members of _elements_ into this List, replacing each from the beginning in order. If + * _elements_ is longer than the current List, the current List will be expanded. If _elements_ + * is shorter than the current List, the extra existing members are not affected (unless + * _replaceCompletely_ is `true`). To remove elements without replacing them, use `[can.List.prototype.removeAttr removeAttr]`. + * + * @param {Array} elements an array of elements to merge in + * + * @param {bool} [replaceCompletely=false] whether to completely replace the elements of List + * If _replaceCompletely_ is `true` and _elements_ is shorter than the List, the existing + * extra members of the List will be removed. + * + * @return {can.List} this List, for chaining + * + * @body + * `attr` gets or sets elements on the `can.List` it's called on. Here's a tour through + * how all of its forms work: + * + * @codestart + * var people = new can.List(['Alex', 'Bill']); + * + * // set an element: + * people.attr(0, 'Adam'); + * + * // get an element: + * people.attr(0); // 'Adam' + * people[0]; // 'Adam' + * + * // get all elements: + * people.attr(); // ['Adam', 'Bill'] + * + * // extend the array: + * people.attr(4, 'Charlie'); + * people.attr(); // ['Adam', 'Bill', undefined, undefined, 'Charlie'] + * + * // merge the elements: + * people.attr(['Alice', 'Bob', 'Eve']); + * people.attr(); // ['Alice', 'Bob', 'Eve', undefined, 'Charlie'] + * @codeend + * + * ## Deep properties + * + * `attr` can also set and read deep properties. All you have to do is specify + * the property name as you normally would if you weren't using `attr`. + * + * @codestart + * var people = new can.List([{name: 'Alex'}, {name: 'Bob'}]); + * + * // set a property: + * people.attr('0.name', 'Alice'); + * + * // get a property: + * people.attr('0.name'); // 'Alice' + * people[0].attr('name'); // 'Alice' + * + * // get all properties: + * people.attr(); // [{name: 'Alice'}, {name: 'Bob'}] + * @codeend + * + * The discussion of deep properties under `[can.Map.prototype.attr]` may also + * be enlightening. + * + * ## Events + * + * `can.List`s emit five types of events in response to changes. They are: + * - the _change_ event fires on every change to a List. + * - the _set_ event is fired when an element is set. + * - the _add_ event is fired when an element is added to the List. + * - the _remove_ event is fired when an element is removed from the List. + * - the _length_ event is fired when the length of the List changes. + * + * * ## The _change_ event + * + * The first event that is fired is the _change_ event. The _change_ event is useful + * if you want to react to all changes on an List. + * + * @codestart + * var list = new can.List([]); + * list.bind('change', function(ev, index, how, newVal, oldVal) { + * console.log('Something changed.'); + * }); + * @codeend + * + * The parameters of the event handler for the _change_ event are: + * + * - _ev_ The event object. + * - _index_ Where the change took place. + * - _how_ Whether elements were added, removed, or set. + * Possible values are `'add'`, `'remove'`, or `'set'`. + * - _newVal_ The elements affected after the change + * _newVal_ will be a single value when an index is set, an Array when elements + * were added, and `undefined` if elements were removed. + * - _oldVal_ The elements affected before the change. + * _newVal_ will be a single value when an index is set, an Array when elements + * were removed, and `undefined` if elements were added. + * + * Here is a concrete tour through the _change_ event handler's arguments: + * + * @codestart + * var list = new can.List(); + * list.bind('change', function(ev, index, how, newVal, oldVal) { + * console.log(ev + ', ' + index + ', ' + how + ', ' + newVal + ', ' + oldVal); + * }); + * + * list.attr(['Alexis', 'Bill']); // [object Object], 0, add, ['Alexis', 'Bill'], undefined + * list.attr(2, 'Eve'); // [object Object], 2, add, Eve, undefined + * list.attr(0, 'Adam'); // [object Object], 0, set, Adam, Alexis + * list.attr(['Alice', 'Bob']); // [object Object], 0, set, Alice, Adam + * // [object Object], 1, set, Bob, Bill + * list.removeAttr(1); // [object Object], 1, remove, undefined, Bob + * @codeend + * + * ## The _set_ event + * + * _set_ events are fired when an element at an index that already exists in the List is + * modified. Actions can cause _set_ events to fire never also cause _length_ events + * to fire (although some functions, such as `[can.List.prototype.splice splice]` + * may cause unrelated sets of events to fire after being batched). + * + * The parameters of the event handler for the _set_ event are: + * + * - _ev_ The event object. + * - _newVal_ The new value of the element. + * - _index_ where the set took place. + * + * Here is a concrete tour through the _set_ event handler's arguments: + * + * @codestart + * var list = new can.List(); + * list.bind('set', function(ev, newVal, index) { + * console.log(newVal + ', ' + index); + * }); + * + * list.attr(['Alexis', 'Bill']); + * list.attr(2, 'Eve'); + * list.attr(0, 'Adam'); // Adam, 0 + * list.attr(['Alice', 'Bob']); // Alice, 0 + * // Bob, 1 + * list.removeAttr(1); + * @codeend + * + * ## The _add_ event + * + * _add_ events are fired when elements are added or inserted + * into the List. + * + * The parameters of the event handler for the _add_ event are: + * + * - _ev_ The event object. + * - _newElements_ The new elements. + * If more than one element is added, _newElements_ will be an array. + * Otherwise, it is simply the new element itself. + * - _index_ Where the add or insert took place. + * + * Here is a concrete tour through the _add_ event handler's arguments: + * + * @codestart + * var list = new can.List(); + * list.bind('add', function(ev, newElements, index) { + * console.log(newElements + ', ' + index); + * }); + * + * list.attr(['Alexis', 'Bill']); // ['Alexis', 'Bill'], 0 + * list.attr(2, 'Eve'); // Eve, 2 + * list.attr(0, 'Adam'); + * list.attr(['Alice', 'Bob']); + * + * list.removeAttr(1); + * @codeend + * + * ## The _remove_ event + * + * _remove_ events are fired when elements are removed from the list. + * + * The parameters of the event handler for the _remove_ event are: + * + * - _ev_ The event object. + * - _removedElements_ The removed elements. + * If more than one element was removed, _removedElements_ will be an array. + * Otherwise, it is simply the element itself. + * - _index_ Where the removal took place. + * + * Here is a concrete tour through the _remove_ event handler's arguments: + * + * @codestart + * var list = new can.List(); + * list.bind('remove', function(ev, removedElements, index) { + * console.log(removedElements + ', ' + index); + * }); + * + * list.attr(['Alexis', 'Bill']); + * list.attr(2, 'Eve'); + * list.attr(0, 'Adam'); + * list.attr(['Alice', 'Bob']); + * + * list.removeAttr(1); // Bob, 1 + * @codeend + * + * ## The _length_ event + * + * _length_ events are fired whenever the list changes. + * + * The parameters of the event handler for the _length_ event are: + * + * - _ev_ The event object. + *- _length_ The current length of the list. + * If events were batched when the _length_ event was triggered, _length_ + * will have the length of the list when `stopBatch` was called. Because + * of this, you may recieve multiple _length_ events with the same + * _length_ parameter. + * + * Here is a concrete tour through the _length_ event handler's arguments: + * + * @codestart + * var list = new can.List(); + * list.bind('length', function(ev, length) { + * console.log(length); + * }); + * + * list.attr(['Alexis', 'Bill']); // 2 + * list.attr(2, 'Eve'); // 3 + * list.attr(0, 'Adam'); + * list.attr(['Alice', 'Bob']); + * + * list.removeAttr(1); // 2 + * @codeend + */ + _attrs: function( items, remove ) { + if ( items === undefined ) { + return Map.helpers.serialize(this, 'attr', []); + } + + // Create a copy. + items = can.makeArray( items ); + + can.batch.start(); + this._updateAttrs(items, remove); + can.batch.stop() + }, + + _updateAttrs : function( items, remove ){ + var len = Math.min(items.length, this.length); + + for ( var prop = 0; prop < len; prop++ ) { + var curVal = this[prop], + newVal = items[prop]; + + if ( Map.helpers.canMakeObserve(curVal) && Map.helpers.canMakeObserve(newVal) ) { + curVal.attr(newVal, remove) + } else if ( curVal != newVal ) { + this._set(prop, newVal) + } else { + + } + } + if ( items.length > this.length ) { + // Add in the remaining props. + this.push.apply( this, items.slice( this.length ) ); + } else if ( items.length < this.length && remove ) { + this.splice(items.length) + } + } + }), + + + // Converts to an `array` of arguments. + getArgs = function( args ) { + return args[0] && can.isArray(args[0]) ? + args[0] : + can.makeArray(args); + }; + // Create `push`, `pop`, `shift`, and `unshift` + can.each({ + /** + * @function can.List.prototype.push push + * @description Add elements to the end of a list. + * @signature `list.push(...elements)` + * + * `push` adds elements onto the end of a List.] + * + * @param {*} elements the elements to add to the List + * + * @return {Number} the new length of the List + * + * @body + * `push` is fairly straightforward: + * + * @codestart + * var list = new can.List(['Alice']); + * + * list.push('Bob', 'Eve'); + * list.attr(); // ['Alice', 'Bob', 'Eve'] + * @codeend + * + * If you have an array you want to concatenate to the end + * of the List, you can use `apply`: + * + * @codestart + * var names = ['Bob', 'Eve'], + * list = new can.List(['Alice']); + * + * list.push.apply(list, names); + * list.attr(); // ['Alice', 'Bob', 'Eve'] + * @codeend + * + * ## Events + * + * `push` causes _change_, _add_, and _length_ events to be fired. + * + * ## See also + * + * `push` has a counterpart in [can.List.pop pop], or you may be + * looking for [can.List.unshift unshift] and its counterpart [can.List.shift shift]. + */ + push: "length", + /** + * @function can.List.prototype.unshift unshift + * @description Add elements to the beginning of a List. + * @signature `list.unshift(...elements)` + * + * `unshift` adds elements onto the beginning of a List. + * + * @param {*} elements the elements to add to the List + * + * @return {Number} the new length of the List + * + * @body + * `unshift` adds elements to the front of the list in bulk in the order specified: + * + * @codestart + * var list = new can.List(['Alice']); + * + * list.unshift('Bob', 'Eve'); + * list.attr(); // ['Bob', 'Eve', 'Alice'] + * @codeend + * + * If you have an array you want to concatenate to the beginning + * of the List, you can use `apply`: + * + * @codestart + * var names = ['Bob', 'Eve'], + * list = new can.List(['Alice']); + * + * list.push.apply(list, names); + * list.attr(); // ['Bob', 'Eve', 'Alice'] + * @codeend + * + * ## Events + * + * `unshift` causes _change_, _add_, and _length_ events to be fired. + * + * ## See also + * + * `unshift` has a counterpart in [can.List.shift shift], or you may be + * looking for [can.List.push push] and its counterpart [can.List.pop pop]. + */ + unshift: 0 + }, + // Adds a method + // `name` - The method name. + // `where` - Where items in the `array` should be added. + function( where, name ) { + var orig = [][name] + list.prototype[name] = function() { + // Get the items being added. + var args = [], + // Where we are going to add items. + len = where ? this.length : 0, + i = arguments.length, + res, + val, + constructor = this.constructor; + + // Go through and convert anything to an `map` that needs to be converted. + while(i--){ + val = arguments[i]; + args[i] = Map.helpers.canMakeObserve(val) ? + Map.helpers.hookupBubble(val, "*", this, this.constructor.Map, this.constructor) : + val; + } + + // Call the original method. + res = orig.apply(this, args); + + if ( !this.comparator || args.length ) { + + this._triggerChange(""+len, "add", args, undefined); + } + + return res; + } + }); + + can.each({ + /** + * @function can.List.prototype.pop pop + * @description Remove an element from the end of a List. + * @signature `list.pop()` + * + * `push` removes an element from the end of a List. + * + * @return {*} the element just popped off the List, or `undefined` if the List was empty + * + * @body + * `pop` is the opposite action from `[can.List.push push]`: + * + * @codestart + * var list = new can.List(['Alice']); + * + * list.push('Bob', 'Eve'); + * list.attr(); // ['Alice', 'Bob', 'Eve'] + * + * list.pop(); // 'Eve' + * list.pop(); // 'Bob' + * list.pop(); // 'Alice' + * list.pop(); // undefined + * @codeend + * + * ## Events + * + * `pop` causes _change_, _remove_, and _length_ events to be fired if the List is not empty + * when it is called. + * + * ## See also + * + * `pop` has its counterpart in [can.List.push push], or you may be + * looking for [can.List.unshift unshift] and its counterpart [can.List.shift shift]. + */ + pop: "length", + /** + * @function can.List.prototype.shift shift + * @description Remove en element from the front of a list. + * @signature `list.shift()` + * + * `shift` removes an element from the beginning of a List. + * + * @return {*} the element just shifted off the List, or `undefined` if the List is empty + * + * @body + * `shift` is the opposite action from `[can.List.unshift unshift]`: + * + * @codestart + * var list = new can.List(['Alice']); + * + * list.unshift('Bob', 'Eve'); + * list.attr(); // ['Bob', 'Eve', 'Alice'] + * + * list.shift(); // 'Bob' + * list.shift(); // 'Eve' + * list.shift(); // 'Alice' + * list.shift(); // undefined + * @codeend + * + * ## Events + * + * `pop` causes _change_, _remove_, and _length_ events to be fired if the List is not empty + * when it is called. + * + * ## See also + * + * `shift` has a counterpart in [can.List.unshift unshift], or you may be + * looking for [can.List.push push] and its counterpart [can.List.pop pop]. + */ + shift: 0 + }, + // Creates a `remove` type method + function( where, name ) { + list.prototype[name] = function() { + + var args = getArgs(arguments), + len = where && this.length ? this.length - 1 : 0; + + + var res = [][name].apply(this, args) + + // Create a change where the args are + // `len` - Where these items were removed. + // `remove` - Items removed. + // `undefined` - The new values (there are none). + // `res` - The old, removed values (should these be unbound). + this._triggerChange(""+len, "remove", undefined, [res]) + + if ( res && res.unbind ) { + res.unbind("change" + this._cid) + } + return res; + } + }); + + can.extend(list.prototype, { + /** + * @function can.List.prototype.indexOf indexOf + * @description Look for an item in a List. + * @signature `list.indexOf(item)` + * + * `indexOf` finds the position of a given item in the List. + * + * @param {*} item the item to find + * + * @return {Number} the position of the item in the List, or -1 if the item is not found. + * + * @body + * @codestart + * var list = new can.List(['Alice', 'Bob', 'Eve']); + * list.indexOf('Alice'); // 0 + * list.indexOf('Charlie'); // -1 + * @codeend + * + * It is trivial to make a `contains`-type function using `indexOf`: + * + * @codestart + * function(list, item) { + * return list.indexOf(item) >= 0; + * } + * @codeend + */ + indexOf: function(item) { + this.attr('length') + return can.inArray(item, this) + }, + + /** + * @function can.List.prototype.join join + * @description Join a List's elements into a string. + * @signature `list.join(separator)` + * + * `join` turns a List into a string by inserting _separator_ between the string representations + * of all the elements of the List. + * + * @param {String} separator the string to seperate elements with + * + * @return {String} the joined string + * + * @body + * @codestart + * var list = new can.List(['Alice', 'Bob', 'Eve']); + * list.join(', '); // 'Alice, Bob, Eve' + * + * var beatles = new can.List(['John', 'Paul', 'Ringo', 'George']); + * beatles.join('&'); // 'John&Paul&Ringo&George' + * @codeend + */ + join : [].join, + + /** + * @function can.List.prototype.reverse reverse + * @description Reverse the order of a List. + * @signature `list.reverse()` + * + * `reverse` reverses the elements of the List in place. + * + * @return {can.List} the List, for chaining + * + * @body + * @codestart + * var list = new can.List(['Alice', 'Bob', 'Eve']); + * var reversedList = list.reverse(); + * + * reversedList.attr(); // ['Eve', 'Bob', 'Alice']; + * list === reversedList; // true + * @codeend + */ + reverse: [].reverse, + + /** + * @function can.List.prototype.slice slice + * @description Make a copy of a part of a List. + * @signature `list.slice([start[, end]])` + * + * `slice` creates a copy of a portion of the List. + * + * @param {Number} [start=0] the index to start copying from + * + * @param {Number} [end] the first index not to include in the copy + * If _end_ is not supplied, `slice` will copy until the end of the list. + * + * @return {can.List} a new `can.List` with the extracted elements + * + * @body + * @codestart + * var list = new can.List(['Alice', 'Bob', 'Charlie', 'Daniel', 'Eve']); + * var newList = list.slice(1, 4); + * newList.attr(); // ['Bob', 'Charlie', 'Daniel'] + * @codeend + * + * `slice` is the simplest way to copy a List: + * + * @codestart + * var list = new can.List(['Alice', 'Bob', 'Eve']); + * var copy = list.slice(); + * + * copy.attr(); // ['Alice', 'Bob', 'Eve'] + * list === copy; // false + * @codeend + */ + slice : function() { + var temp = Array.prototype.slice.apply(this, arguments); + return new this.constructor( temp ); + }, + + /** + * @function can.List.prototype.concat concat + * @description Merge many collections together into a List. + * @signature `list.concat(...args)` + * @param {Array|can.List|*} args Any number of arrays, Lists, or values to add in + * For each parameter given, if it is an Array or a List, each of its elements will be added to + * the end of the concatenated List. Otherwise, the parameter itself will be added. + * + * @body + * `concat` makes a new List with the elements of the List followed by the elements of the parameters. + * + * @codestart + * var list = new can.List(); + * var newList = list.concat( + * 'Alice', + * ['Bob', 'Charlie']), + * new can.List(['Daniel', 'Eve']), + * {f: 'Francis'} + * ); + * newList.attr(); // ['Alice', 'Bob', 'Charlie', 'Daniel', 'Eve', {f: 'Francis'}] + * @codeend + */ + concat : function() { + var args = []; + can.each( can.makeArray( arguments ), function( arg, i ) { + args[i] = arg instanceof can.List ? arg.serialize() : arg ; + }); + return new this.constructor(Array.prototype.concat.apply(this.serialize(), args)); + }, + + /** + * @function can.List.prototype.forEach forEach + * @description Call a function for each element of a List. + * @signature `list.forEach(callback[, thisArg])` + * @param {function(element, index, list)} callback a function to call with each element of the List + * The three parameters that _callback_ gets passed are _element_, the element at _index_, _index_ the + * current element of the list, and _list_ the List the elements are coming from. + * @param {Object} [thisArg] the object to use as `this` inside the callback + * + * @body + * `forEach` calls a callback for each element in the List. + * + * @codestart + * var list = new can.List([1, 2, 3]); + * list.forEach(function(element, index, list) { + * list.attr(index, element * element); + * }); + * list.attr(); // [1, 4, 9] + * @codeend + */ + forEach : function( cb, thisarg ) { + can.each(this, cb, thisarg || this ); + }, + + /** + * @function can.List.prototype.replace replace + * @description Replace all the elements of a List. + * @signature `list.replace(collection)` + * @param {Array|can.List|can.Deferred} collection the collection of new elements to use + * If a [can.Deferred] is passed, it must resolve to an `Array` or `can.List`. + * The elements of the list are not actually removed until the Deferred resolves. + * + * @body + * `replace` replaces all the elements of this List with new ones. + * + * `replace` is especially useful when `can.List`s are live-bound into `[can.Control]`s, + * and you intend to populate them with the results of a `[can.Model]` call: + * + * @codestart + * can.Control({ + * init: function() { + * this.list = new Todo.List(); + * // live-bind the list into the DOM + * this.element.html(can.view('list.mustache', this.list)); + * // when this AJAX call returns, the live-bound DOM will be updated + * this.list.replace(Todo.findAll()); + * } + * }); + * @codeend + * + * Learn more about [can.Model.List making Lists of models]. + * + * ## Events + * + * A major difference between `replace` and `attr(newElements, true)` is that `replace` always emits + * an_add_ event and a _remove_ event, whereas `attr` will cause _set_ events along an _add_ or _remove_ + * event if needed. Corresponding _change_ and _length_ events will be fired as well. + * + * The differences in the events fired by `attr` and `replace` are demonstrated concretely by this example: + * @codestart + * var attrList = new can.List(['Alexis', 'Bill']); + * attrList.bind('change', function(ev, index, how, newVals, oldVals) { + * console.log(index + ', ' + how + ', ' + newVals + ', ' + oldVals); + * }); + * + * var replaceList = new can.List(['Alexis', 'Bill']); + * replaceList.bind('change', function(ev, index, how, newVals, oldVals) { + * console.log(index + ', ' + how + ', ' + newVals + ', ' + oldVals); + * }); + * + * attrList.attr(['Adam', 'Ben'], true); // 0, set, Adam, Alexis + * // 1, set, Ben, Bill + * replaceList.replace(['Adam', 'Ben']); // 0, remove, undefined, ['Alexis', 'Bill'] + * // 0, add, undefined, ['Adam', 'Ben'] + * + * attrList.attr(['Amber'], true); // 0, set, Amber, Adam + * // 1, remove, undefined, Ben + * replaceList.replace(['Amber']); // 0, remove, undefined, ['Adam', 'Ben'] + * // 0, add, Amber, ['Adam', 'Ben'] + * + * attrList.attr(['Alice', 'Bob', 'Eve'], true); // 0, set, Alice, Amber + * // 1, add, ['Bob', 'Eve'], undefined + * replaceList.replace(['Alice', 'Bob', 'Eve']); // 0, remove, undefined, Amber + * // 0, add, ['Alice', 'Bob', 'Eve'], Amber + * @codeend + */ + replace : function(newList) { + if(can.isDeferred(newList)) { + newList.then(can.proxy(this.replace, this)); + } else { + this.splice.apply(this, [0, this.length].concat(can.makeArray(newList || []))); + } + + return this; + } + }); + + can.List = Map.List = list; + return can.List; +}) diff --git a/list/list_test.js b/list/list_test.js new file mode 100644 index 00000000000..9d10bb1006e --- /dev/null +++ b/list/list_test.js @@ -0,0 +1,163 @@ +(function(undefined) { + +module('can/list') + + + +test("list attr changes length", function(){ + var l = new can.List([0,1,2]) + l.attr(3,3) + equal(l.length, 4); +}) + +test("list splice", function(){ + var l = new can.List([0,1,2,3]), + first = true; + + l.bind('change', function( ev, attr, how, newVals, oldVals ) { + equal(attr, "1") + // where comes from the attr ... + //equal(where, 1) + if(first){ + equal( how, "remove", "removing items" ) + equal( newVals, undefined, "no new Vals" ) + } else { + deepEqual( newVals, ["a","b"] , "got the right newVals") + equal( how, "add", "adding items" ) + } + + first = false; + }) + + l.splice(1,2, "a", "b"); + deepEqual(l.serialize(), [0,"a","b", 3], "serialized") +}); + + + +test("list pop", function(){ + var l = new can.List([0,1,2,3]); + + l.bind('change', function( ev, attr, how, newVals, oldVals ) { + equal(attr, "3") + + equal( how, "remove" ) + equal( newVals, undefined ) + deepEqual( oldVals, [3] ) + }) + + l.pop(); + deepEqual(l.serialize(), [0,1,2]) +}) + + + +test("remove nested property in item of array map", function(){ + var state = new can.List([{nested: true}]); + + state.bind("change", function(ev, attr, how, newVal, old){ + equal(attr, "0.nested"); + equal(how, "remove") + deepEqual(old, true); + }) + + state.removeAttr("0.nested"); + equal(undefined, state.attr("0.nested") ); +}); + + +test("pop unbinds", function(){ + var l = new can.List([{foo: 'bar'}]); + var o = l.attr(0), + count = 0; + l.bind('change', function(ev, attr, how, newVal, oldVal){ + count++; + if(count == 1){ + // the prop change + equal(attr, '0.foo', "count is set"); + } else if(count === 2 ){ + equal(how, "remove"); + equal(attr, "0") + } else { + ok(false, "called too many times") + } + + }) + + equal( o.attr('foo') , 'bar'); + + o.attr('foo','car') + l.pop(); + o.attr('foo','bad') +}) + +test("splice unbinds", function(){ + var l = new can.List([{foo: 'bar'}]); + var o = l.attr(0), + count = 0; + l.bind('change', function(ev, attr, how, newVal, oldVal){ + count++; + if(count == 1){ + // the prop change + equal(attr, '0.foo', "count is set"); + } else if(count === 2 ){ + equal(how, "remove"); + equal(attr, "0") + } else { + ok(false, "called too many times") + } + + }) + + equal( o.attr('foo') , 'bar'); + + o.attr('foo','car') + l.splice(0,1); + o.attr('foo','bad') +}); + + +test("always gets right attr even after moving array items", function(){ + var l = new can.List([{foo: 'bar'}]); + + // get the first item + var o = l.attr(0); + // add a new item + l.unshift("A new Value") + + // listen to change + l.bind('change', function(ev, attr, how){ + equal(attr, "1.foo") + }) + + // this should have bubbled right + o.attr('foo','led you') +}); + + + +test("Array accessor methods", 11, function() { + var l = new can.List([ 'a', 'b', 'c' ]), + sliced = l.slice(2), + joined = l.join(' | '), + concatenated = l.concat([ 2, 1 ], new can.List([ 0 ])); + + ok(sliced instanceof can.List, 'Slice is an Observable list'); + equal(sliced.length, 1, 'Sliced off two elements'); + equal(sliced[0], 'c', 'Single element as expected'); + equal(joined, 'a | b | c', 'Joined list properly'); + ok(concatenated instanceof can.List, 'Concatenated is an Observable list'); + deepEqual(concatenated.serialize(), [ 'a', 'b', 'c', 2, 1, 0 ], 'List concatenated properly'); + l.forEach(function(letter, index) { + ok(true, 'Iteration'); + if(index === 0) { + equal(letter, 'a', 'First letter right'); + } + if(index === 2) { + equal(letter, 'c', 'Last letter right'); + } + }); +}); + + +})(); diff --git a/list/test.html b/list/test.html new file mode 100644 index 00000000000..1d284dc8295 --- /dev/null +++ b/list/test.html @@ -0,0 +1,25 @@ + + +
+ + + +[can.Observe::delegate delegate]\(selector, event, handler(ev,newVal,oldVal,from)\)
:
+[can.Map::delegate delegate]\(selector, event, handler(ev,newVal,oldVal,from)\)
:
// create an observable
- var observe = new can.Observe({
+ var observe = new can.Map({
name : {
first : "Justin Meyer"
}
@@ -35,7 +35,7 @@ Listen to specific event changes with
observe.attr('name.first',"Justin")
Delegate will listen on the object until you
-call [can.Observe::undelegate undelegate]\(selector, event, handler\)
to remove the event handler.
+call [can.Map::undelegate undelegate]\(selector, event, handler\)
to remove the event handler.
observe.undelegate("name.first","set", handler );
diff --git a/observe/delegate/delegate_test.js b/map/delegate/delegate_test.js
similarity index 93%
rename from observe/delegate/delegate_test.js
rename to map/delegate/delegate_test.js
index f5793dc2cbe..20bdd2656e0 100644
--- a/observe/delegate/delegate_test.js
+++ b/map/delegate/delegate_test.js
@@ -1,9 +1,9 @@
(function() {
-module('can/observe/delegate')
+module('can/map/delegate')
-var matches = can.Observe.prototype.delegate.matches;
+var matches = can.Map.prototype.delegate.matches;
test("matches", function(){
equal( matches(['**'], ['foo','bar','0']) ,
@@ -31,7 +31,7 @@ test("matches", function(){
test("delegate", 4,function(){
- var state = new can.Observe({
+ var state = new can.Map({
properties : {
prices : []
}
@@ -53,7 +53,7 @@ test("delegate", 4,function(){
test("delegate on add", 2, function(){
- var state = new can.Observe({});
+ var state = new can.Map({});
state.delegate("foo","add", function(ev, newVal){
ok(true, "called");
@@ -67,7 +67,7 @@ test("delegate on add", 2, function(){
});
test("delegate set is called on add", 2, function(){
- var state = new can.Observe({});
+ var state = new can.Map({});
state.delegate("foo","set", function(ev, newVal){
ok(true, "called");
@@ -77,7 +77,7 @@ test("delegate set is called on add", 2, function(){
});
test("delegate's this", 5, function(){
- var state = new can.Observe({
+ var state = new can.Map({
person : {
name : {
first : "justin",
@@ -113,7 +113,7 @@ test("delegate's this", 5, function(){
test("delegate on deep properties with *", function(){
- var state = new can.Observe({
+ var state = new can.Map({
person : {
name : {
first : "justin",
@@ -131,7 +131,7 @@ test("delegate on deep properties with *", function(){
test("compound sets", function(){
- var state = new can.Observe({
+ var state = new can.Map({
type : "person",
id: "5"
});
@@ -179,7 +179,7 @@ test("compound sets", function(){
test("undelegate within event loop",1, function(){
- var state = new can.Observe({
+ var state = new can.Map({
type : "person",
id: "5"
});
@@ -205,7 +205,7 @@ test("undelegate within event loop",1, function(){
test("selector types", 5, function() {
- var state = new can.Observe({
+ var state = new can.Map({
foo: "a",
bar: "b",
baz: "c",
diff --git a/observe/delegate/test.html b/map/delegate/test.html
similarity index 82%
rename from observe/delegate/test.html
rename to map/delegate/test.html
index 252351e99eb..98609738158 100644
--- a/observe/delegate/test.html
+++ b/map/delegate/test.html
@@ -17,9 +17,9 @@
diff --git a/observe/elements/elements.js b/map/elements/elements.js
similarity index 95%
rename from observe/elements/elements.js
rename to map/elements/elements.js
index 261bd727634..977673fa9f8 100644
--- a/observe/elements/elements.js
+++ b/map/elements/elements.js
@@ -1,4 +1,4 @@
-steal('can/util', 'can/observe', function(can, Observe) {
+steal('can/util', 'can/map', function(can, Observe) {
var unique = function( items ) {
var collect = [];
@@ -15,7 +15,7 @@ var unique = function( items ) {
});
}
- can.extend(can.Observe.prototype,{
+ can.extend(can.Map.prototype,{
/**
* Returns a unique identifier for the observe instance. For example:
*
@@ -111,7 +111,7 @@ var unique = function( items ) {
});
});
- ret = kind ? new kind : new can.Observe.List;
+ ret = kind ? new kind : new can.List;
ret.push.apply(ret, unique(collection));
return ret;
@@ -139,7 +139,7 @@ var unique = function( items ) {
* it will add the instance to the element.
*/
$.fn.instance = function( type ) {
- if ( type && type instanceof can.Observe ) {
+ if ( type && type instanceof can.Map ) {
type.hookup(this[0]);
return this;
} else {
@@ -148,5 +148,5 @@ var unique = function( items ) {
};
- return can.Observe;
+ return can.Map;
})
diff --git a/observe/elements/elements_test.js b/map/elements/elements_test.js
similarity index 88%
rename from observe/elements/elements_test.js
rename to map/elements/elements_test.js
index 48c33db1f27..a5978ef632b 100644
--- a/observe/elements/elements_test.js
+++ b/map/elements/elements_test.js
@@ -1,6 +1,6 @@
(function() {
-module("can/observe/elements")
+module("can/map/elements")
test("identity uses the real id", function(){
var Person = can.Model.extend({
diff --git a/observe/elements/test.html b/map/elements/test.html
similarity index 83%
rename from observe/elements/test.html
rename to map/elements/test.html
index c0d751c4b7f..52c5c10cdd0 100644
--- a/observe/elements/test.html
+++ b/map/elements/test.html
@@ -17,7 +17,7 @@
diff --git a/observe/list/list.js b/map/list/list.js
similarity index 94%
rename from observe/list/list.js
rename to map/list/list.js
index 687682fd2ae..583fea6257a 100644
--- a/observe/list/list.js
+++ b/map/list/list.js
@@ -1,5 +1,5 @@
-steal('can/util', 'can/observe', 'can/observe/compute', function(can) {
- can.extend(can.Observe.List.prototype, {
+steal('can/util', 'can/map', 'can/compute', function(can) {
+ can.extend(can.List.prototype, {
filter : function(callback) {
// The filtered list
var filtered = new this.constructor();
@@ -57,7 +57,7 @@ steal('can/util', 'can/observe', 'can/observe/compute', function(can) {
},
map : function(callback) {
- var mapped = new can.Observe.List();
+ var mapped = new can.List();
var self = this;
// Again, lets run a generator function
var generator = function(element, index) {
@@ -122,5 +122,5 @@ steal('can/util', 'can/observe', 'can/observe/compute', function(can) {
*/
});
- return can.Observe.List;
+ return can.List;
})
diff --git a/observe/list/list_test.js b/map/list/list_test.js
similarity index 93%
rename from observe/list/list_test.js
rename to map/list/list_test.js
index ef38a71f2f2..ec086a636b7 100644
--- a/observe/list/list_test.js
+++ b/map/list/list_test.js
@@ -1,8 +1,8 @@
(function(){
- module("can/observe/list");
+ module("can/map/list");
test("filter", 8, function() {
- var original = new can.Observe.List([
+ var original = new can.List([
{
name : 'Test 1',
age : 20
@@ -21,7 +21,7 @@
}
]);
- var state = new can.Observe({ minAge : 20 });
+ var state = new can.Map({ minAge : 20 });
var filtered = original.filter(function(element) {
return element.attr('age') > state.attr('minAge');
@@ -54,7 +54,7 @@
});
test("attr updates items in position order", function(){
- var original = new can.Observe.List([
+ var original = new can.List([
{
id : 1,
name : 'Test 1',
@@ -101,7 +101,7 @@
});
test("map", function() {
- var original = new can.Observe.List([
+ var original = new can.List([
{
name : 'Test 1',
age : 20
diff --git a/observe/list/qunit.html b/map/list/qunit.html
similarity index 87%
rename from observe/list/qunit.html
rename to map/list/qunit.html
index 44f8986eaef..94d5ab7753b 100644
--- a/observe/list/qunit.html
+++ b/map/list/qunit.html
@@ -15,8 +15,8 @@