From 4e7e1aae85afede78f2e4fb18ae562e8095f8b60 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 17 Aug 2011 20:19:58 +0200 Subject: [PATCH 01/21] colloection select and include_blank option to select_tag --- view/helpers/helpers.js | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/view/helpers/helpers.js b/view/helpers/helpers.js index 75b3f91b..50b8fd5f 100644 --- a/view/helpers/helpers.js +++ b/view/helpers/helpers.js @@ -1,4 +1,4 @@ -steal('jquery/view/ejs').then(function($){ +steal.plugins('jquery/view/ejs').then(function($){ /** * @add jQuery.EJS.Helpers.prototype @@ -214,11 +214,28 @@ $.extend($.EJS.Helpers.prototype, { html_options.id = html_options.id || name; //html_options.value = value; html_options.name = name; + if (html_options.hasOwnProperty('include_blank')){ + choices.unshift({text: (html_options.include_blank || ''), value: ''}); + delete html_options['include_blank']; + } var txt = ''; txt += this.start_tag_for('select', html_options); - for(var i = 0; i < choices.length; i++) + var prevGroupOn = null; + + if (choices[0] && choices[0].hasOwnProperty('groupOn')){ + txt += this.start_tag_for('optgroup', {label: choices[0].groupLabel}); + prevGroupOn = choices[0].groupOn + } + for(var i = 0; i < choices.length; i++) { - var choice = choices[i]; + var choice = choices[i]; + + if(choice.hasOwnProperty('groupOn') && choice.groupOn != prevGroupOn){ + txt += this.tag_end('optgroup'); + txt += this.start_tag_for('optgroup', {label: choice.groupLabel}); + prevGroupOn = choice.groupOn + } + if(typeof choice == 'string') choice = {value: choice}; if(!choice.text) choice.text = choice.value; if(!choice.value) choice.text = choice.text; @@ -317,8 +334,8 @@ $.extend($.EJS.Helpers.prototype, { options = options || {}; options.src = steal.root.join("resources/images/"+image_location); return this.single_tag_for('img', options); - } - + }, + }); $.EJS.Helpers.prototype.text_tag = $.EJS.Helpers.prototype.text_area_tag; From 5ffcae76ab7db2ac43c7cc31931f0b71feda93f2 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 18 Aug 2011 00:21:36 +0200 Subject: [PATCH 02/21] added collection select and include blank options to select_tag --- view/helpers/helpers.js | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/view/helpers/helpers.js b/view/helpers/helpers.js index f12e01f1..d1fe07b3 100644 --- a/view/helpers/helpers.js +++ b/view/helpers/helpers.js @@ -214,11 +214,28 @@ $.extend($.EJS.Helpers.prototype, { html_options.id = html_options.id || name; //html_options.value = value; html_options.name = name; + if (html_options.hasOwnProperty('include_blank')){ + choices.unshift({text: ((html_options.include_blank == true ) ? '' : html_options.include_blank), value: ''}); + delete html_options['include_blank']; + } var txt = ''; txt += this.start_tag_for('select', html_options); - for(var i = 0; i < choices.length; i++) + var prevGroupOn = null; + + if (choices[0] && choices[0].hasOwnProperty('groupOn')){ + txt += this.start_tag_for('optgroup', {label: choices[0].groupLabel}); + prevGroupOn = choices[0].groupOn + } + for(var i = 0; i < choices.length; i++) { - var choice = choices[i]; + var choice = choices[i]; + + if(choice.hasOwnProperty('groupOn') && choice.groupOn != prevGroupOn){ + txt += this.tag_end('optgroup'); + txt += this.start_tag_for('optgroup', {label: choice.groupLabel}); + prevGroupOn = choice.groupOn + } + if(typeof choice == 'string') choice = {value: choice}; if(!choice.text) choice.text = choice.value; if(!choice.value) choice.text = choice.text; @@ -317,8 +334,8 @@ $.extend($.EJS.Helpers.prototype, { options = options || {}; options.src = steal.root.join("resources/images/"+image_location); return this.single_tag_for('img', options); - } - + }, + }); $.EJS.Helpers.prototype.text_tag = $.EJS.Helpers.prototype.text_area_tag; From 1975c57b36bfd751f8a4054cce3ffe6f55fa5ddf Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 25 Aug 2011 20:42:56 +0200 Subject: [PATCH 03/21] refresh global list on model and prevent duplications --- model/model.js | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/model/model.js b/model/model.js index 0ff9e715..2afb6b69 100644 --- a/model/model.js +++ b/model/model.js @@ -761,12 +761,18 @@ steal.plugins('jquery/class', 'jquery/lang').then(function() { if (!attributes ) { return null; } - return new this( + + var instance = new this( // checks for properties in an object (like rails 2.0 gives); isObject(attributes[this._shortName]) || isObject(attributes.data) || isObject(attributes.attributes) || attributes); + if (this.list && instance[this.id]){ + this.list.remove(instance) + this.list.push(instance) + } + return instance }, /** * @function wrapMany @@ -1294,17 +1300,17 @@ steal.plugins('jquery/class', 'jquery/lang').then(function() { } - //if this class has a global list, add / remove from the list. - if ( property === Class.id && val !== null && Class.list ) { - // if we didn't have an old id, add ourselves - if (!old ) { - Class.list.push(this); - } else if ( old != val ) { - // if our id has changed ... well this should be ok - Class.list.remove(old); - Class.list.push(this); - } - } +// //if this class has a global list, add / remove from the list. +// if ( property === Class.id && val !== null && Class.list ) { +// // if we didn't have an old id, add ourselves +// if (!old ) { +// Class.list.push(this); +// } else if ( old != val ) { +// // if our id has changed ... well this should be ok +// Class.list.remove(old); +// Class.list.push(this); +// } +// } }, /** From 55e77c676786b2e0beb66e52f964215f16e2e6fe Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 24 Aug 2011 01:16:54 +0200 Subject: [PATCH 04/21] new steal stuff --- view/helpers/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/helpers/helpers.js b/view/helpers/helpers.js index 50b8fd5f..98384f9a 100644 --- a/view/helpers/helpers.js +++ b/view/helpers/helpers.js @@ -1,4 +1,4 @@ -steal.plugins('jquery/view/ejs').then(function($){ +steal('jquery/view/ejs').then(function($){ /** * @add jQuery.EJS.Helpers.prototype From 8c4be41f0c69d201a840e0aa19e649186d37d7ac Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 1 Sep 2011 11:04:11 +0200 Subject: [PATCH 05/21] custom serialization for back end requests --- model/model.js | 49 ++++++++++++++++++++-------------- model/qunit.html | 2 +- model/test/qunit/model_test.js | 17 ++++++++++++ 3 files changed, 47 insertions(+), 21 deletions(-) diff --git a/model/model.js b/model/model.js index 34d2a3a0..9db1c1fd 100644 --- a/model/model.js +++ b/model/model.js @@ -91,7 +91,7 @@ steal('jquery/class', 'jquery/lang').then(function() { reject = function(data){ deferred.rejectWith(self, [data]) }, - args = [self.serialize(), resolve, reject]; + args = [self.serialize(true), resolve, reject]; if(type == 'destroy'){ args.shift(); @@ -1033,7 +1033,8 @@ steal('jquery/class', 'jquery/lang').then(function() { } }, bind: bind, - unbind: unbind + unbind: unbind, + backendSerialize: {} }, /** * @Prototype @@ -1383,25 +1384,33 @@ steal('jquery/class', 'jquery/lang').then(function() { } return attributes; }, - serialize : function(){ - var Class = this.Class, - attrs = Class.attributes, - type, - converter, - data = {}, - attr; + serialize : function(backend){ + var Class = this.Class, + attrs = Class.attributes, + type, + converter, + data = {}, + attr, + instruction; - attributes = {}; - - for ( attr in attrs ) { - if ( attrs.hasOwnProperty(attr) ) { - type = attrs[attr]; - converter = Class.serialize[type] || Class.serialize['default']; - data[attr] = converter( this[attr] , type ); - } - } - return data; - }, + attributes = {}; + + for ( attr in attrs ) { + if ( attrs.hasOwnProperty(attr) ) { + type = attrs[attr]; + converter = Class.serialize[type] || Class.serialize['default']; + if (backend && Class.backendSerialize.hasOwnProperty(attr)){ + instruction = Class.backendSerialize[attr] + if (instruction != false || (instruction['if'] && instruction['if'].call(this, this[attr]))){ + data[instruction['key'] || attr] = converter( this[attr] , type ); + } + } else { + data[attr] = converter( this[attr] , type ); + } + } + } + return data; + }, /** * Returns if the instance is a new object. This is essentially if the * id is null or undefined. diff --git a/model/qunit.html b/model/qunit.html index abca295f..218735de 100644 --- a/model/qunit.html +++ b/model/qunit.html @@ -12,6 +12,6 @@

associations list - + \ No newline at end of file diff --git a/model/test/qunit/model_test.js b/model/test/qunit/model_test.js index 77b090cd..5e3dc633 100644 --- a/model/test/qunit/model_test.js +++ b/model/test/qunit/model_test.js @@ -405,3 +405,20 @@ test("serialize", function(){ }).serialize().createdAt, "feb", "serialized") }); +test("backendSerialize", function(){ + $.Model('User'); + $.Model("Task",{ + attributes: { + user: 'User.model' + }, + backendSerialize:{ + user: { + "if": function(user){ return this.isNew() || user.isNew() }, + "key": "user_attributes" + } + } + },{}); + equals(new Task({ + user: { name: "test" } + }).serialize(true).user_attributes.name, "test", "serialized") +}); From 42d0b8b3a62344ab5bb25eb066517a91f7b4b496 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 1 Sep 2011 11:09:43 +0200 Subject: [PATCH 06/21] Revert "updated model association to the new way of doing" This reverts commit 89b7b23bc2d9c06713dc8b62ec8c74643e636d1b. --- view/helpers/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/helpers/helpers.js b/view/helpers/helpers.js index 6cc8a666..9e2ed84d 100644 --- a/view/helpers/helpers.js +++ b/view/helpers/helpers.js @@ -1,4 +1,4 @@ -steal('jquery/view/ejs').then(function($){ +steal.plugins('jquery/view/ejs').then(function($){ /** * @add jQuery.EJS.Helpers.prototype From 6fe64fc499eeffbf22bf7bb3c4b6de7e57b0d49d Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 1 Sep 2011 11:20:13 +0200 Subject: [PATCH 07/21] steal stuff --- view/helpers/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/helpers/helpers.js b/view/helpers/helpers.js index 9e2ed84d..6cc8a666 100644 --- a/view/helpers/helpers.js +++ b/view/helpers/helpers.js @@ -1,4 +1,4 @@ -steal.plugins('jquery/view/ejs').then(function($){ +steal('jquery/view/ejs').then(function($){ /** * @add jQuery.EJS.Helpers.prototype From 239bc45fb74cb49b999aaecf0567f0b72f15e6cd Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 1 Sep 2011 12:03:19 +0200 Subject: [PATCH 08/21] make if optional for backend serialization --- model/model.js | 2 +- model/test/qunit/model_test.js | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/model/model.js b/model/model.js index 30e5bbae..734dde17 100644 --- a/model/model.js +++ b/model/model.js @@ -1401,7 +1401,7 @@ steal('jquery/class', 'jquery/lang/string', function() { converter = Class.serialize[type] || Class.serialize['default']; if (backend && Class.backendSerialize.hasOwnProperty(attr)){ instruction = Class.backendSerialize[attr] - if (instruction != false || (instruction['if'] && instruction['if'].call(this, this[attr]))){ + if (instruction != false && (!instruction['if'] || (instruction['if'].call(this, this[attr])))){ data[instruction['key'] || attr] = converter( this[attr] , type ); } } else { diff --git a/model/test/qunit/model_test.js b/model/test/qunit/model_test.js index 5e3dc633..0b79ca34 100644 --- a/model/test/qunit/model_test.js +++ b/model/test/qunit/model_test.js @@ -409,16 +409,28 @@ test("backendSerialize", function(){ $.Model('User'); $.Model("Task",{ attributes: { - user: 'User.model' + user1: 'User.model', + user2: 'User.model', + user3: 'User.model' }, backendSerialize:{ - user: { + user1: { "if": function(user){ return this.isNew() || user.isNew() }, "key": "user_attributes" - } + }, + user2: { + key: 'user_2_attributes' + }, + user3: false } },{}); - equals(new Task({ - user: { name: "test" } - }).serialize(true).user_attributes.name, "test", "serialized") + var obj = new Task({ + user1: { name: "test1" }, + user2: { name: "test2" }, + user3: { name: "test3" } + }).serialize(true) + equal(obj.user_attributes.name, "test1") + equal(obj.user_2_attributes.name, "test2") + ok(!obj.hasOwnProperty('user3')) + }); From 7a5d2d11184b196adda663a4cf1c419c2f65c71e Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 1 Sep 2011 18:25:18 +0200 Subject: [PATCH 09/21] revert custom serialization stuff for backend queries --- model/model.js | 14 +++----------- model/test/qunit/model_test.js | 29 ----------------------------- 2 files changed, 3 insertions(+), 40 deletions(-) diff --git a/model/model.js b/model/model.js index 734dde17..84e8313c 100644 --- a/model/model.js +++ b/model/model.js @@ -91,7 +91,7 @@ steal('jquery/class', 'jquery/lang/string', function() { reject = function(data){ deferred.rejectWith(self, [data]) }, - args = [self.serialize(true), resolve, reject]; + args = [self.serialize(), resolve, reject]; if(type == 'destroy'){ args.shift(); @@ -1033,8 +1033,7 @@ steal('jquery/class', 'jquery/lang/string', function() { } }, bind: bind, - unbind: unbind, - backendSerialize: {} + unbind: unbind }, /** * @Prototype @@ -1399,14 +1398,7 @@ steal('jquery/class', 'jquery/lang/string', function() { if ( attrs.hasOwnProperty(attr) ) { type = attrs[attr]; converter = Class.serialize[type] || Class.serialize['default']; - if (backend && Class.backendSerialize.hasOwnProperty(attr)){ - instruction = Class.backendSerialize[attr] - if (instruction != false && (!instruction['if'] || (instruction['if'].call(this, this[attr])))){ - data[instruction['key'] || attr] = converter( this[attr] , type ); - } - } else { - data[attr] = converter( this[attr] , type ); - } + data[attr] = converter( this[attr] , type ); } } return data; diff --git a/model/test/qunit/model_test.js b/model/test/qunit/model_test.js index 0b79ca34..77b090cd 100644 --- a/model/test/qunit/model_test.js +++ b/model/test/qunit/model_test.js @@ -405,32 +405,3 @@ test("serialize", function(){ }).serialize().createdAt, "feb", "serialized") }); -test("backendSerialize", function(){ - $.Model('User'); - $.Model("Task",{ - attributes: { - user1: 'User.model', - user2: 'User.model', - user3: 'User.model' - }, - backendSerialize:{ - user1: { - "if": function(user){ return this.isNew() || user.isNew() }, - "key": "user_attributes" - }, - user2: { - key: 'user_2_attributes' - }, - user3: false - } - },{}); - var obj = new Task({ - user1: { name: "test1" }, - user2: { name: "test2" }, - user3: { name: "test3" } - }).serialize(true) - equal(obj.user_attributes.name, "test1") - equal(obj.user_2_attributes.name, "test2") - ok(!obj.hasOwnProperty('user3')) - -}); From d1c4e38d764288e5c4b236c87cc714795654f830 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 1 Sep 2011 18:32:19 +0200 Subject: [PATCH 10/21] finally i ll keep it (for the moment..)! --- model/model.js | 14 +++++++++++--- model/test/qunit/model_test.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/model/model.js b/model/model.js index 84e8313c..734dde17 100644 --- a/model/model.js +++ b/model/model.js @@ -91,7 +91,7 @@ steal('jquery/class', 'jquery/lang/string', function() { reject = function(data){ deferred.rejectWith(self, [data]) }, - args = [self.serialize(), resolve, reject]; + args = [self.serialize(true), resolve, reject]; if(type == 'destroy'){ args.shift(); @@ -1033,7 +1033,8 @@ steal('jquery/class', 'jquery/lang/string', function() { } }, bind: bind, - unbind: unbind + unbind: unbind, + backendSerialize: {} }, /** * @Prototype @@ -1398,7 +1399,14 @@ steal('jquery/class', 'jquery/lang/string', function() { if ( attrs.hasOwnProperty(attr) ) { type = attrs[attr]; converter = Class.serialize[type] || Class.serialize['default']; - data[attr] = converter( this[attr] , type ); + if (backend && Class.backendSerialize.hasOwnProperty(attr)){ + instruction = Class.backendSerialize[attr] + if (instruction != false && (!instruction['if'] || (instruction['if'].call(this, this[attr])))){ + data[instruction['key'] || attr] = converter( this[attr] , type ); + } + } else { + data[attr] = converter( this[attr] , type ); + } } } return data; diff --git a/model/test/qunit/model_test.js b/model/test/qunit/model_test.js index 77b090cd..0b79ca34 100644 --- a/model/test/qunit/model_test.js +++ b/model/test/qunit/model_test.js @@ -405,3 +405,32 @@ test("serialize", function(){ }).serialize().createdAt, "feb", "serialized") }); +test("backendSerialize", function(){ + $.Model('User'); + $.Model("Task",{ + attributes: { + user1: 'User.model', + user2: 'User.model', + user3: 'User.model' + }, + backendSerialize:{ + user1: { + "if": function(user){ return this.isNew() || user.isNew() }, + "key": "user_attributes" + }, + user2: { + key: 'user_2_attributes' + }, + user3: false + } + },{}); + var obj = new Task({ + user1: { name: "test1" }, + user2: { name: "test2" }, + user3: { name: "test3" } + }).serialize(true) + equal(obj.user_attributes.name, "test1") + equal(obj.user_2_attributes.name, "test2") + ok(!obj.hasOwnProperty('user3')) + +}); From b3a0bf2256bc0835a23b372d737aa4360ec6626e Mon Sep 17 00:00:00 2001 From: Nicolas Date: Fri, 2 Sep 2011 17:44:34 +0200 Subject: [PATCH 11/21] i don t use anymore the common views application folder --- controller/view/view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/view/view.js b/controller/view/view.js index 83c4cdc4..a63ed55d 100644 --- a/controller/view/view.js +++ b/controller/view/view.js @@ -10,7 +10,7 @@ steal('jquery/controller', 'jquery/view').then(function( $ ) { classPartsWithoutPrefix.splice(0, 2); // Remove prefix (usually 2 elements) var classPartsWithoutPrefixSlashes = classPartsWithoutPrefix.join('/'), - hasControllers = (classParts.length > 2) && classParts[1] == 'Controllers', + hasControllers = false, //(classParts.length > 2) && classParts[1] == 'Controllers', path = hasControllers? jQuery.String.underscore(classParts[0]): jQuery.String.underscore(classParts.join("/")), controller_name = classPartsWithoutPrefix.join('/').toLowerCase(), suffix = (typeof view == "string" && /\.[\w\d]+$/.test(view)) ? "" : jQuery.View.ext; From 9b20e228aeab367f565908297cac811afc97096e Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 7 Sep 2011 14:29:20 +0200 Subject: [PATCH 12/21] ability to select multiple options for multi select --- view/helpers/helpers.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/view/helpers/helpers.js b/view/helpers/helpers.js index 6cc8a666..05aa09ef 100644 --- a/view/helpers/helpers.js +++ b/view/helpers/helpers.js @@ -209,7 +209,8 @@ $.extend($.EJS.Helpers.prototype, { * @param {Object} choices * @param {Object} html_options */ - select_tag: function( name, value, choices, html_options ) { + select_tag: function( name, value, choices, html_options ) { + var values = $.isArray(value) ? value : [value]; html_options = html_options || {}; html_options.id = html_options.id || name; //html_options.value = value; @@ -241,7 +242,7 @@ $.extend($.EJS.Helpers.prototype, { if(!choice.value) choice.text = choice.text; var optionOptions = {value: choice.value}; - if(choice.value == value) + if($.inArray(choice.value, values) != -1) optionOptions.selected ='selected'; txt += this.start_tag_for('option', optionOptions )+choice.text+this.tag_end('option'); } From 78eec4ffb27aa9d01ec94937a2b7df42e525c22f Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 7 Sep 2011 15:25:22 +0200 Subject: [PATCH 13/21] access to controllerInstance.view into its views --- controller/view/view.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/controller/view/view.js b/controller/view/view.js index a63ed55d..8c23247c 100644 --- a/controller/view/view.js +++ b/controller/view/view.js @@ -104,7 +104,8 @@ steal('jquery/controller', 'jquery/view').then(function( $ ) { view = jQuery.Controller._calculatePosition(this.Class, view, this.called); //calculate data - data = data || this; + var self = this; + data = data? $.extend({},data,{view: function(){ return self.view.apply(self, arguments)}}) : this; //calculate helpers var helpers = calculateHelpers.call(this, myhelpers); From 04f818a45852eb7286fbfdefb07a86d0466d1072 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 21 Sep 2011 15:57:33 +0200 Subject: [PATCH 14/21] getId was not parsing properly urls --- dom/fixture/fixture.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dom/fixture/fixture.js b/dom/fixture/fixture.js index 51e06496..579ca651 100644 --- a/dom/fixture/fixture.js +++ b/dom/fixture/fixture.js @@ -162,11 +162,12 @@ steal('jquery/dom', } if(id === undefined){ - id = settings.url.replace(/\/(\w+)(\/|$)/g, function(all, num){ - if(num != 'update'){ - id = num; - } - }) + id = settings.url.match(/\d+(?=(\.json))/) && settings.url.match(/\d+(?=(\.json))/)[0] +// id = settings.url.replace(/\/(\w+)(\/|$)/g, function(all, num){ +// if(num != 'update'){ +// id = num; +// } +// }) } if(id === undefined){ // if still not set, guess a random number From 9e0ae391d20e533e7f524856494a676f1f1e68e4 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 6 Oct 2011 10:10:58 +0200 Subject: [PATCH 15/21] prevent resizing hidden element --- event/resize/resize.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/event/resize/resize.js b/event/resize/resize.js index db36d1b1..daa11a64 100644 --- a/event/resize/resize.js +++ b/event/resize/resize.js @@ -129,8 +129,13 @@ steal('jquery/event').then(function( $ ) { // if index == -1 it's the window while (++index < length && (child = resizers[index]) && (isWindow || $.contains(where, child)) ) { - // call the event - $.event.handle.call(child, ev); + if($(child).is(':visible')){ + // call the event + $.event.handle.call(child, ev); + } else { + ev.stopPropagation() + } + if ( ev.isPropagationStopped() ) { // move index until the item is not in the current child From 6adb1bb5631af2512e4c12e62eee2c9f2023077c Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 6 Oct 2011 10:13:51 +0200 Subject: [PATCH 16/21] allow building association with plain object as in rails --- model/model.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/model/model.js b/model/model.js index 858145f3..d82b9494 100644 --- a/model/model.js +++ b/model/model.js @@ -1036,7 +1036,8 @@ steal('jquery/class', 'jquery/lang/string', function() { var res = getList(this.List), arr = isArray(instancesRawData), ml = ($.Model.List && instancesRawData instanceof $.Model.List), - raw = arr ? instancesRawData : (ml ? instancesRawData.serialize() : instancesRawData.data), + po = $.isPlainObject(instancesRawData) && !instancesRawData.hasOwnProperty('data'), // po as plain object with index as iterator ( allow {0=>item_attrs,1=>item_attrs, etc..} ) + raw = arr ? instancesRawData : (ml ? instancesRawData.serialize() : ( po ? $.map(instancesRawData, function(v){return v}) : instancesRawData.data )), length = raw.length, i = 0; //@steal-remove-start @@ -1048,7 +1049,7 @@ steal('jquery/class', 'jquery/lang/string', function() { for (; i < length; i++ ) { res.push(this.model(raw[i])); } - if (!arr ) { //push other stuff onto array + if (!arr && !po ) { //push other stuff onto array for ( var prop in instancesRawData ) { if ( prop !== 'data' ) { res[prop] = instancesRawData[prop]; From 19ab27bed1321542d5d9bd04904341ce4cdced43 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 6 Oct 2011 10:16:47 +0200 Subject: [PATCH 17/21] controller view helper --- controller/view/view.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controller/view/view.js b/controller/view/view.js index e744f7d8..85f2a54b 100644 --- a/controller/view/view.js +++ b/controller/view/view.js @@ -105,11 +105,11 @@ steal('jquery/controller', 'jquery/view').then(function( $ ) { //calculate data var self = this; - data = data? $.extend({},data,{view: function(){ return self.view.apply(self, arguments)}}) : this; + data = data? data : this; //calculate helpers var helpers = calculateHelpers.call(this, myhelpers); - + $.extend(helpers,{view: function(){ return self.view.apply(self, arguments)}}) return jQuery.View(view, data, helpers); //what about controllers in other folders? }; From 68066f2f09fa4b62c1a013dd859f392251f0b386 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 6 Oct 2011 10:17:39 +0200 Subject: [PATCH 18/21] allow passing an arg to prevent individually automatic filtering on filter attributes --- dom/fixture/fixture.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dom/fixture/fixture.js b/dom/fixture/fixture.js index 579ca651..9fc2106c 100644 --- a/dom/fixture/fixture.js +++ b/dom/fixture/fixture.js @@ -522,7 +522,8 @@ steal('jquery/dom', * } * */ - make: function( types, count, make, filter ) { + make: function( types, count, make, filter, preventAutoFiltering ) { + preventAutoFiltering = $.makeArray(preventAutoFiltering) if(typeof types === "string"){ types = [types+"s",types ] } @@ -593,7 +594,8 @@ steal('jquery/dom', for ( var param in settings.data ) { i=0; if ( settings.data[param] !== undefined && // don't do this if the value of the param is null (ignore it) - (param.indexOf("Id") != -1 || param.indexOf("_id") != -1) ) { + $.inArray(param, preventAutoFiltering) == -1 && + (param.indexOf("Id") != -1 || param.indexOf("_id") != -1) ) { while ( i < retArr.length ) { if ( settings.data[param] != retArr[i][param] ) { retArr.splice(i, 1); From 3f36b2750d87485f0f0169d4733addb620dea618 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Fri, 7 Oct 2011 11:55:59 +0200 Subject: [PATCH 19/21] first attempt to decouple serialize and param building --- model/backup/backup.js | 32 ++++++- model/backup/qunit/qunit.js | 103 +++++++++++++++-------- model/list/list.js | 9 +- model/model.js | 72 +++++++++++++--- model/test/qunit/associations_test.js | 18 ++-- model/test/qunit/model_test.js | 117 ++++++++++++++++---------- model/test/qunit/qunit.js | 4 +- 7 files changed, 251 insertions(+), 104 deletions(-) diff --git a/model/backup/backup.js b/model/backup/backup.js index e4c8c481..f35dcc7a 100644 --- a/model/backup/backup.js +++ b/model/backup/backup.js @@ -115,6 +115,7 @@ See this in action: return !same(this.serialize(), this._backupStore, !!checkAssociations); } }, + /** * @function jQuery.Model.prototype.restore * @parent jquery.model.backup @@ -126,7 +127,36 @@ See this in action: this.attrs(props); return this; - } + }, + + changed: function(attr) { + var self= this, + attrs = this.Class.attributes, + changes = [], + changed = function(attr){ + // consider all attr as changed when instance is new + if (self.isNew()){ + return true + } + // check assoc by calling their changed function individually + if (typeof self[attr] == 'object' && self[attr] !== null && self[attr].changed && self[attr].changed().length){ + return true + } + // check attributes and plural assoc + if ( (self._backupStore && !same(self.serialize(attr), self._backupStore[attr]))){ + return true + } + } + if (attr){ + return changed(attr) + } + for (attr in attrs) { + if ( changed(attr) ){ + changes.push(attr) + } + } + return changes + } }) }) diff --git a/model/backup/qunit/qunit.js b/model/backup/qunit/qunit.js index 4869d98d..a4954076 100644 --- a/model/backup/qunit/qunit.js +++ b/model/backup/qunit/qunit.js @@ -11,36 +11,36 @@ module("jquery/model/backup",{ test("backing up", function(){ var recipe = new Recipe({name: "cheese"}); ok(!recipe.isDirty(), "not backedup, but clean") - + recipe.backup(); ok(!recipe.isDirty(), "backedup, but clean"); - + recipe.name = 'blah' - + ok(recipe.isDirty(), "dirty"); - + recipe.restore(); - + ok(!recipe.isDirty(), "restored, clean"); - + equals(recipe.name, "cheese" ,"name back"); - + }); test("backup / restore with associations", function(){ $.Model("Instruction"); $.Model("Cookbook"); - + $.Model("Recipe",{ attributes : { instructions : "Instruction.models", cookbook: "Cookbook.model" } },{}); - - - + + + var recipe = new Recipe({ name: "cheese burger", instructions : [ @@ -55,53 +55,90 @@ test("backup / restore with associations", function(){ title : "Justin's Grillin Times" } }); - + //test basic is dirty - + ok(!recipe.isDirty(), "not backedup, but clean") - + recipe.backup(); ok(!recipe.isDirty(), "backedup, but clean"); - + recipe.name = 'blah' - + ok(recipe.isDirty(), "dirty"); - + recipe.restore(); - + ok(!recipe.isDirty(), "restored, clean"); - + equals(recipe.name, "cheese burger" ,"name back"); - + // test belongs too - + ok(!recipe.cookbook.isDirty(), "cookbook not backedup, but clean"); - + recipe.cookbook.backup(); - + recipe.cookbook.attr("title","Brian's Burgers"); - + ok(!recipe.isDirty(), "recipe itself is clean"); - + ok(recipe.isDirty(true), "recipe is dirty if checking associations"); - + recipe.cookbook.restore() - + ok(!recipe.isDirty(true), "recipe is now clean with checking associations"); - + equals(recipe.cookbook.title, "Justin's Grillin Times" ,"cookbook title back"); - + //try belongs to recursive restore - + recipe.cookbook.attr("title","Brian's Burgers"); recipe.restore(); ok(recipe.isDirty(true), "recipe is dirty if checking associations, after a restore"); - + recipe.restore(true); ok(!recipe.isDirty(true), "cleaned all of recipe and its associations"); - - + + }) +test('makeParams only save changed attributes and associations', function(){ + $.Model('User',{ + attributes: { + name: 'string', + category: 'Category.model', + loans: 'Loans.model' + } + },{}) + $.Model('Category',{},{}) + $.Model('Loan',{},{}) + + u = new User({name: 'test', category: {name: 'category 1'}, loans:[{id:2, name: 'test'}]}) + data = u.makeParams() + equals(data['name'], 'test', "attributes") + equals(data['category']['name'], 'category 1', "single association") + equals(data['loans'][0]["name"], 'test', 'plural association') + + u = new User({id: 1, name: 'test', category: {id:2, name: 'category 1'}, loans:[{id:2, name: 'test'}]}) + data = u.makeParams() + delete data["id"] + ok($.isEmptyObject(data), 'nothing should change since its not a new object') + + u.category.attr('name', 'cat name has changed') + data = u.makeParams() + equals(data['name'], undefined) + equals(data['category']['name'], 'cat name has changed') + equals(data['category']['id'], 2) + equals(data['loans'], undefined) + + u.attr('loans',[{id:2, name: 'test'},{name: 'added loan'}]) + data = u.makeParams() + equals(data['loans'].length, 2) +}) + + + + }) \ No newline at end of file diff --git a/model/list/list.js b/model/list/list.js index 8f878e8a..b24946aa 100644 --- a/model/list/list.js +++ b/model/list/list.js @@ -363,7 +363,14 @@ $.Class("jQuery.Model.List", return this.map(function(item){ return item.serialize() }); - } + }, + makeParams : function(nested){ + return this.map(function(item){ + //if (item.isDirty()){ // don t send back unchanged item + return item.makeParams(nested) + //} + }); + } }); var push = [].push, diff --git a/model/model.js b/model/model.js index 57ec4931..7d6ecc3d 100644 --- a/model/model.js +++ b/model/model.js @@ -91,7 +91,7 @@ steal('jquery/class', 'jquery/lang/string', function() { reject = function(data){ deferred.rejectWith(self, [data]) }, - args = [self.serialize(), resolve, reject], + args = [self.makeParams(), resolve, reject], constructor = self.constructor; if(type == 'destroy'){ @@ -1056,6 +1056,8 @@ steal('jquery/class', 'jquery/lang/string', function() { return isObject(val) && val.serialize ? val.serialize() : val; } }, + params: { + }, bind: bind, unbind: unbind }, @@ -1335,6 +1337,11 @@ steal('jquery/class', 'jquery/lang/string', function() { callback = success, list = Class.list; +// // backup the initial state of the instance + if (!this._init && this.backup && !this._backupStore ){ + this.backup() + } + val = this[property] = (value === null ? //if the value is null or undefined null : // it should be null converter.call(Class, value, function(){}, type) //convert it to something useful @@ -1445,25 +1452,62 @@ steal('jquery/class', 'jquery/lang/string', function() { } return attributes; }, - serialize : function(){ - var Class = this.constructor, + serialize : function(attr){ + var self= this, + Class = this.constructor, attrs = Class.attributes, type, converter, data = {}, - attr; - attributes = {}; - - for ( attr in attrs ) { - if ( attrs.hasOwnProperty(attr) ) { - type = attrs[attr]; - converter = Class.serialize[type] || Class.serialize['default']; - data[attr] = converter( this[attr] , type ); - } - } - return data; + serialize = function(attr){ + type = attrs[attr]; + converter = Class.serialize[type] || Class.serialize['default']; + return converter( self[attr] , type ); + }; + + if (attr){ + return serialize(attr) + } + + for ( attr in attrs ) { + if ( attrs.hasOwnProperty(attr) ) { + data[attr] = serialize(attr) + } + } + return data; + }, + makeParams: function(nested){ + var Class = this.constructor, + attrs = Class.attributes, + params = Class.params, + nested = nested || false, + type, + attr, + changed, + param, + data = {}; + + if (nested){ + data[Class.id] = getId(this); //ensure id is set for nested assoc + } + + for ( attr in attrs ){ + param = params[attr] == undefined ? {} : params[attr]; + type = attrs[attr]; + changed = !this.changed || this.changed(attr) ; + if ( (param != false && changed) + && (!param['if'] || param['if'].call(this, this[attr],type) == true) + && (!param['unless'] || param['unless'].call(this, this[attr],type) == false) ) + { + data[param['as'] || attr] = isObject(this[attr]) && this[attr].makeParams ? this[attr].makeParams(true) : this.serialize(attr) ; + } + } + + return data + }, + /** * Returns if the instance is a new object. This is essentially if the * id is null or undefined. diff --git a/model/test/qunit/associations_test.js b/model/test/qunit/associations_test.js index 9e402874..42e0425b 100644 --- a/model/test/qunit/associations_test.js +++ b/model/test/qunit/associations_test.js @@ -58,11 +58,11 @@ test("associations work", function(){ }) equals(c.person.name, "Justin", "association present"); equals(c.person.Class, MyTest.Person, "belongs to association typed"); - + equals(c.issues.length, 0); - + equals(c.loans.length, 2); - + equals(c.loans[0].Class, MyTest.Loan); }); @@ -77,14 +77,14 @@ test("Model association serialize on save", function(){ loans : [] }), cSave = c.save(); - + stop(); cSave.then(function(customer){ start() equals(customer.personAttr, "My name is thecountofzero", "serialization works"); - + }); - + }); test("Model.List association serialize on save", function(){ @@ -107,7 +107,7 @@ test("Model.List association serialize on save", function(){ ] }), cSave = c.save(); - + stop(); cSave.then(function(customer){ start() @@ -115,7 +115,7 @@ test("Model.List association serialize on save", function(){ ok(customer.loansAttr._data === undefined, "_data does not exist"); ok(customer.loansAttr._use_call === undefined, "_use_call does not exist"); ok(customer.loansAttr._changed === undefined, "_changed does not exist"); - + }); - + }); \ No newline at end of file diff --git a/model/test/qunit/model_test.js b/model/test/qunit/model_test.js index 5b5dbda9..45f75463 100644 --- a/model/test/qunit/model_test.js +++ b/model/test/qunit/model_test.js @@ -27,7 +27,7 @@ module("jquery/model", { test("CRUD", function(){ - + Person.findAll({}, function(response){ equals("findAll", response) }) @@ -90,7 +90,7 @@ test("findOne deferred", function(){ }); test("save deferred", function(){ - + $.Model("Person",{ create : function(attrs, success, error){ return $.ajax({ @@ -105,21 +105,21 @@ test("save deferred", function(){ }) } },{}); - + var person = new Person({name: "Justin"}), personD = person.save(); - + stop(); personD.then(function(person){ start() equals(person.id, 5, "we got an id") - + }); - + }); test("update deferred", function(){ - + $.Model("Person",{ update : function(id, attrs, success, error){ return $.ajax({ @@ -134,21 +134,21 @@ test("update deferred", function(){ }) } },{}); - + var person = new Person({name: "Justin", id:5}), personD = person.save(); - + stop(); personD.then(function(person){ start() equals(person.thing, "er", "we got updated") - + }); - + }); test("destroy deferred", function(){ - + $.Model("Person",{ destroy : function(id, success, error){ return $.ajax({ @@ -162,15 +162,15 @@ test("destroy deferred", function(){ }) } },{}); - + var person = new Person({name: "Justin", id:5}), personD = person.destroy(); - + stop(); personD.then(function(person){ start() equals(person.thing, "er", "we got destroyed") - + }); }); @@ -210,28 +210,28 @@ test("models", function(){ test("async setters", function(){ - + /* $.Model("Test.AsyncModel",{ setName : function(newVal, success, error){ - - + + setTimeout(function(){ success(newVal) }, 100) } }); - + var model = new Test.AsyncModel({ name : "justin" }); equals(model.name, "justin","property set right away") - + //makes model think it is no longer new model.id = 1; - + var count = 0; - + model.bind('name', function(ev, newName){ equals(newName, "Brian",'new name'); equals(++count, 1, "called once"); @@ -245,14 +245,14 @@ test("async setters", function(){ test("binding", 2,function(){ var inst = new Person({foo: "bar"}); - + inst.bind("foo", function(ev, val){ - ok(true,"updated") + ok(true,"updated") equals(val, "baz", "values match") }); - + inst.attr("foo","baz"); - + }); test("error binding", 1, function(){ @@ -269,8 +269,8 @@ test("error binding", 1, function(){ equals(error, "no name", "error message provided") }) school.attr("name",""); - - + + }) test("auto methods",function(){ @@ -286,24 +286,24 @@ test("auto methods",function(){ School.findAll({type:"schools"}, function(schools){ ok(schools,"findAll Got some data back"); equals(schools[0].constructor.shortName,"School","there are schools") - + School.findOne({id : "4"}, function(school){ ok(school,"findOne Got some data back"); equals(school.constructor.shortName,"School","a single school"); - - + + new School({name: "Highland"}).save(function(){ equals(this.name,"Highland","create gets the right name") this.update({name: "LHS"}, function(){ start(); equals(this.name,"LHS","create gets the right name") - + $.fixture.on = true; }) }) - + }) - + }) }) @@ -355,34 +355,34 @@ test("Model events" , function(){ success() } },{}); - + stop(); $([Test.Event]).bind('created',function(ev, passedItem){ - + ok(this === Test.Event, "got model") ok(passedItem === item, "got instance") equals(++order, 1, "order"); passedItem.update({}); - + }).bind('updated', function(ev, passedItem){ equals(++order, 2, "order"); ok(this === Test.Event, "got model") ok(passedItem === item, "got instance") - + passedItem.destroy({}); - + }).bind('destroyed', function(ev, passedItem){ equals(++order, 3, "order"); ok(this === Test.Event, "got model") ok(passedItem === item, "got instance") - + start(); - + }) - + var item = new Test.Event(); item.save(); - + }); @@ -444,7 +444,7 @@ test("removeAttr test", function(){ var person = new Person({foo: "bar"}) equals(person.foo, "bar", "property set"); person.removeAttr('foo') - + equals(person.foo, undefined, "property removed"); var attrs = person.attrs() equals(attrs.foo, undefined, "attrs removed"); @@ -458,3 +458,32 @@ test("identity should replace spaces with underscores", function(){ equals(t.identity(), "task_id_with_spaces") }); +test('makeParams allow attributes renaming and conditional saving', function(){ + $.Model('User',{ + attributes: { + name: 'string', + category: 'Category.model', + pictureUrl: 'string' + }, + params:{ + pictureUrl: false, + category: { + 'if': function(cat, type){ return cat.isNew() } + } + } + },{}) + $.Model('Category', { + attributes: { + name: 'string' + }, + params:{ + name: { as: 'catName' } + } + },{}) + u = new User({name: 'test', pictureUrl: 'http://pict.jpeg', category: {name: 'category 1'}}) + equals(u.makeParams()['name'], 'test') + equals(u.makeParams()['pictureUrl'], undefined) + equals(u.makeParams()['category']['catName'], 'category 1') + +}) + diff --git a/model/test/qunit/qunit.js b/model/test/qunit/qunit.js index b4fca3b7..71679bec 100644 --- a/model/test/qunit/qunit.js +++ b/model/test/qunit/qunit.js @@ -1,8 +1,8 @@ //we probably have to have this only describing where the tests are steal("jquery/model","jquery/dom/fixture") //load your app .then('funcunit/qunit') //load qunit - .then("./model_test.js","./associations_test.js") - .then( +.then("./model_test.js","./associations_test.js") +.then( "jquery/model/backup/qunit", "jquery/model/list/test/qunit" ) From c71a61b02399435513aa4d323848bf74935b8d81 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 20 Oct 2011 13:43:42 +0200 Subject: [PATCH 20/21] sned back model data behind model name --- model/model.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/model/model.js b/model/model.js index fef937e6..29bda4b8 100644 --- a/model/model.js +++ b/model/model.js @@ -1495,17 +1495,15 @@ steal('jquery/class', 'jquery/lang/string', function() { attr, changed, param, - data = {}; + data = {}, + prefixedData = {}; - if (nested){ - data[Class.id] = getId(this); //ensure id is set for nested assoc - } for ( attr in attrs ){ param = params[attr] == undefined ? {} : params[attr]; type = attrs[attr]; changed = !this.changed || this.changed(attr) ; - if ( (param != false && changed) + if ( (param != false && (changed || param == true)) && (!param['if'] || param['if'].call(this, this[attr],type) == true) && (!param['unless'] || param['unless'].call(this, this[attr],type) == false) ) { @@ -1513,6 +1511,9 @@ steal('jquery/class', 'jquery/lang/string', function() { } } + if (nested){ + data[Class.id] = getId(this); //ensure id is set for nested assoc + } return data }, /** From 51235cc464d8e0fc485df9b0d72dc85732783cc8 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 20 Oct 2011 13:44:02 +0200 Subject: [PATCH 21/21] fix resize --- event/resize/resize.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/event/resize/resize.js b/event/resize/resize.js index daa11a64..bde49d4a 100644 --- a/event/resize/resize.js +++ b/event/resize/resize.js @@ -133,7 +133,14 @@ steal('jquery/event').then(function( $ ) { // call the event $.event.handle.call(child, ev); } else { - ev.stopPropagation() + // move index until the item is not in the current child + while (++index < length && (sub = resizers[index]) ) { + if (!$.contains(child, sub) ) { + // set index back one + index--; + break + } + } }