canjs-rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +36 -0
- data/Rakefile +2 -0
- data/canjs-rails.gemspec +23 -0
- data/lib/canjs-rails.rb +1 -0
- data/lib/canjs/rails.rb +7 -0
- data/lib/canjs/rails/engine.rb +6 -0
- data/lib/canjs/rails/version.rb +6 -0
- data/vendor/assets/javascripts/can.construct.proxy.js +60 -0
- data/vendor/assets/javascripts/can.construct.super.js +44 -0
- data/vendor/assets/javascripts/can.control.plugin.js +245 -0
- data/vendor/assets/javascripts/can.control.view.js +88 -0
- data/vendor/assets/javascripts/can.fixture.js +1020 -0
- data/vendor/assets/javascripts/can.jquery.js +2995 -0
- data/vendor/assets/javascripts/can.jquery.min.js +52 -0
- data/vendor/assets/javascripts/can.observe.attributes.js +293 -0
- data/vendor/assets/javascripts/can.observe.backup.js +368 -0
- data/vendor/assets/javascripts/can.observe.delegate.js +359 -0
- data/vendor/assets/javascripts/can.observe.setter.js +58 -0
- data/vendor/assets/javascripts/can.observe.validations.js +374 -0
- data/vendor/assets/javascripts/can.view.modifiers.js +292 -0
- data/vendor/assets/javascripts/download_canjs.sh +15 -0
- metadata +108 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
(function(can, window, undefined){
|
2
|
+
var URI = steal.URI || steal.File;
|
3
|
+
|
4
|
+
can.Control.getFolder = function() {
|
5
|
+
return can.underscore(this.fullName.replace(/\./g, "/")).replace("/Controllers", "");
|
6
|
+
};
|
7
|
+
|
8
|
+
can.Control._calculatePosition = function( Class, view ) {
|
9
|
+
var classParts = Class.fullName.split('.'),
|
10
|
+
classPartsWithoutPrefix = classParts.slice(0);
|
11
|
+
classPartsWithoutPrefix.splice(0, 2),
|
12
|
+
action_name = "init"; // Remove prefix (usually 2 elements)
|
13
|
+
|
14
|
+
var hasControllers = (classParts.length > 2) && classParts[1] == 'Controllers',
|
15
|
+
path = hasControllers? can.underscore(classParts[0]): can.underscore(classParts.join("/")),
|
16
|
+
controller_name = can.underscore(classPartsWithoutPrefix.join('/')).toLowerCase(),
|
17
|
+
suffix = (typeof view == "string" && /\.[\w\d]+$/.test(view)) ? "" : can.view.ext;
|
18
|
+
|
19
|
+
//calculate view
|
20
|
+
if ( typeof view == "string" ) {
|
21
|
+
if ( view.substr(0, 2) == "//" ) { //leave where it is
|
22
|
+
} else {
|
23
|
+
view = "//" + URI(path).join( 'views/' + (view.indexOf('/') !== -1 ? view : (hasControllers ? controller_name + '/' : "") + view)) + suffix;
|
24
|
+
}
|
25
|
+
} else if (!view ) {
|
26
|
+
view = "//" + URI(path).join('views/' + (hasControllers ? controller_name + '/' : "") + action_name.replace(/\.|#/g, '').replace(/ /g, '_'))+ suffix;
|
27
|
+
}
|
28
|
+
return view;
|
29
|
+
};
|
30
|
+
|
31
|
+
var calculateHelpers = function( myhelpers ) {
|
32
|
+
var helpers = {};
|
33
|
+
if ( myhelpers ) {
|
34
|
+
if ( can.isArray(myhelpers) ) {
|
35
|
+
for ( var h = 0; h < myhelpers.length; h++ ) {
|
36
|
+
can.extend(helpers, myhelpers[h]);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
else {
|
40
|
+
can.extend(helpers, myhelpers);
|
41
|
+
}
|
42
|
+
} else {
|
43
|
+
if ( this._default_helpers ) {
|
44
|
+
helpers = this._default_helpers;
|
45
|
+
}
|
46
|
+
|
47
|
+
//load from name
|
48
|
+
var current = window;
|
49
|
+
var parts = this.constructor.fullName.split(/\./);
|
50
|
+
for ( var i = 0; i < parts.length; i++ ) {
|
51
|
+
if(current){
|
52
|
+
if ( typeof current.Helpers == 'object' ) {
|
53
|
+
can.extend(helpers, current.Helpers);
|
54
|
+
}
|
55
|
+
current = current[parts[i]];
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
if (current && typeof current.Helpers == 'object' ) {
|
60
|
+
can.extend(helpers, current.Helpers);
|
61
|
+
}
|
62
|
+
|
63
|
+
this._default_helpers = helpers;
|
64
|
+
}
|
65
|
+
return helpers;
|
66
|
+
};
|
67
|
+
|
68
|
+
can.Control.prototype.view = function( view, data, myhelpers ) {
|
69
|
+
//shift args if no view is provided
|
70
|
+
if ( typeof view != "string" && !myhelpers ) {
|
71
|
+
myhelpers = data;
|
72
|
+
data = view;
|
73
|
+
view = null;
|
74
|
+
}
|
75
|
+
|
76
|
+
//guess from controller name
|
77
|
+
view = can.Control._calculatePosition(this.constructor, view, this.called);
|
78
|
+
|
79
|
+
//calculate data
|
80
|
+
data = data || this;
|
81
|
+
|
82
|
+
//calculate helpers
|
83
|
+
var helpers = calculateHelpers.call(this, myhelpers);
|
84
|
+
|
85
|
+
return can.view(view, data, helpers); //what about controllers in other folders?
|
86
|
+
};
|
87
|
+
|
88
|
+
})(this.can, this )
|
@@ -0,0 +1,1020 @@
|
|
1
|
+
(function(can, window, undefined){
|
2
|
+
|
3
|
+
var isArray = can.isArray,
|
4
|
+
// essentially returns an object that has all the must have comparisons ...
|
5
|
+
// must haves, do not return true when provided undefined
|
6
|
+
cleanSet = function(obj, compares){
|
7
|
+
var copy = can.extend({}, obj);
|
8
|
+
for(var prop in copy) {
|
9
|
+
var compare = compares[prop] === undefined ? compares["*"] : compares[prop];
|
10
|
+
if( same(copy[prop], undefined, compare ) ) {
|
11
|
+
delete copy[prop]
|
12
|
+
}
|
13
|
+
}
|
14
|
+
return copy;
|
15
|
+
},
|
16
|
+
propCount = function(obj){
|
17
|
+
var count = 0;
|
18
|
+
for(var prop in obj) count++;
|
19
|
+
return count;
|
20
|
+
};
|
21
|
+
|
22
|
+
/**
|
23
|
+
* @class can.Object
|
24
|
+
* @parent can.util
|
25
|
+
*
|
26
|
+
* Object contains several helper methods that
|
27
|
+
* help compare objects.
|
28
|
+
*
|
29
|
+
* ## same
|
30
|
+
*
|
31
|
+
* Returns true if two objects are similar.
|
32
|
+
*
|
33
|
+
* can.Object.same({foo: "bar"} , {bar: "foo"}) //-> false
|
34
|
+
*
|
35
|
+
* ## subset
|
36
|
+
*
|
37
|
+
* Returns true if an object is a set of another set.
|
38
|
+
*
|
39
|
+
* can.Object.subset({}, {foo: "bar"} ) //-> true
|
40
|
+
*
|
41
|
+
* ## subsets
|
42
|
+
*
|
43
|
+
* Returns the subsets of an object
|
44
|
+
*
|
45
|
+
* can.Object.subsets({userId: 20},
|
46
|
+
* [
|
47
|
+
* {userId: 20, limit: 30},
|
48
|
+
* {userId: 5},
|
49
|
+
* {}
|
50
|
+
* ])
|
51
|
+
* //-> [{userId: 20, limit: 30}]
|
52
|
+
*/
|
53
|
+
can.Object = {};
|
54
|
+
|
55
|
+
/**
|
56
|
+
* @function same
|
57
|
+
* Returns if two objects are the same. It takes an optional compares object that
|
58
|
+
* can be used to make comparisons.
|
59
|
+
*
|
60
|
+
* This function does not work with objects that create circular references.
|
61
|
+
*
|
62
|
+
* ## Examples
|
63
|
+
*
|
64
|
+
* can.Object.same({name: "Justin"},
|
65
|
+
* {name: "JUSTIN"}) //-> false
|
66
|
+
*
|
67
|
+
* // ignore the name property
|
68
|
+
* can.Object.same({name: "Brian"},
|
69
|
+
* {name: "JUSTIN"},
|
70
|
+
* {name: null}) //-> true
|
71
|
+
*
|
72
|
+
* // ignore case
|
73
|
+
* can.Object.same({name: "Justin"},
|
74
|
+
* {name: "JUSTIN"},
|
75
|
+
* {name: "i"}) //-> true
|
76
|
+
*
|
77
|
+
* // deep rule
|
78
|
+
* can.Object.same({ person : { name: "Justin" } },
|
79
|
+
* { person : { name: "JUSTIN" } },
|
80
|
+
* { person : { name: "i" } }) //-> true
|
81
|
+
*
|
82
|
+
* // supplied compare function
|
83
|
+
* can.Object.same({age: "Thirty"},
|
84
|
+
* {age: 30},
|
85
|
+
* {age: function( a, b ){
|
86
|
+
* if( a == "Thirty" ) {
|
87
|
+
* a = 30
|
88
|
+
* }
|
89
|
+
* if( b == "Thirty" ) {
|
90
|
+
* b = 30
|
91
|
+
* }
|
92
|
+
* return a === b;
|
93
|
+
* }}) //-> true
|
94
|
+
*
|
95
|
+
* @param {Object} a an object to compare
|
96
|
+
* @param {Object} b an object to compare
|
97
|
+
* @param {Object} [compares] an object that indicates how to
|
98
|
+
* compare specific properties.
|
99
|
+
* Typically this is a name / value pair
|
100
|
+
*
|
101
|
+
* can.Object.same({name: "Justin"},{name: "JUSTIN"},{name: "i"})
|
102
|
+
*
|
103
|
+
* There are two compare functions that you can specify with a string:
|
104
|
+
*
|
105
|
+
* - 'i' - ignores case
|
106
|
+
* - null - ignores this property
|
107
|
+
*
|
108
|
+
* @param {Object} [deep] used internally
|
109
|
+
*/
|
110
|
+
var same = can.Object.same = function(a, b, compares, aParent, bParent, deep){
|
111
|
+
var aType = typeof a,
|
112
|
+
aArray = isArray(a),
|
113
|
+
comparesType = typeof compares,
|
114
|
+
compare;
|
115
|
+
|
116
|
+
if(comparesType == 'string' || compares === null ){
|
117
|
+
compares = compareMethods[compares];
|
118
|
+
comparesType = 'function'
|
119
|
+
}
|
120
|
+
if(comparesType == 'function'){
|
121
|
+
return compares(a, b, aParent, bParent)
|
122
|
+
}
|
123
|
+
compares = compares || {};
|
124
|
+
|
125
|
+
if(a instanceof Date){
|
126
|
+
return a === b;
|
127
|
+
}
|
128
|
+
if(deep === -1){
|
129
|
+
return aType === 'object' || a === b;
|
130
|
+
}
|
131
|
+
if(aType !== typeof b || aArray !== isArray(b)){
|
132
|
+
return false;
|
133
|
+
}
|
134
|
+
if(a === b){
|
135
|
+
return true;
|
136
|
+
}
|
137
|
+
if(aArray){
|
138
|
+
if(a.length !== b.length){
|
139
|
+
return false;
|
140
|
+
}
|
141
|
+
for(var i =0; i < a.length; i ++){
|
142
|
+
compare = compares[i] === undefined ? compares["*"] : compares[i]
|
143
|
+
if(!same(a[i],b[i], a, b, compare )){
|
144
|
+
return false;
|
145
|
+
}
|
146
|
+
};
|
147
|
+
return true;
|
148
|
+
} else if(aType === "object" || aType === 'function'){
|
149
|
+
var bCopy = can.extend({}, b);
|
150
|
+
for(var prop in a){
|
151
|
+
compare = compares[prop] === undefined ? compares["*"] : compares[prop];
|
152
|
+
if(! same( a[prop], b[prop], compare , a, b, deep === false ? -1 : undefined )){
|
153
|
+
return false;
|
154
|
+
}
|
155
|
+
delete bCopy[prop];
|
156
|
+
}
|
157
|
+
// go through bCopy props ... if there is no compare .. return false
|
158
|
+
for(prop in bCopy){
|
159
|
+
if( compares[prop] === undefined ||
|
160
|
+
! same( undefined, b[prop], compares[prop] , a, b, deep === false ? -1 : undefined )){
|
161
|
+
return false;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
return true;
|
165
|
+
}
|
166
|
+
return false;
|
167
|
+
};
|
168
|
+
|
169
|
+
/**
|
170
|
+
* @function subsets
|
171
|
+
* Returns the sets in 'sets' that are a subset of checkSet
|
172
|
+
* @param {Object} checkSet
|
173
|
+
* @param {Object} sets
|
174
|
+
*/
|
175
|
+
can.Object.subsets = function(checkSet, sets, compares){
|
176
|
+
var len = sets.length,
|
177
|
+
subsets = [],
|
178
|
+
checkPropCount = propCount(checkSet),
|
179
|
+
setLength;
|
180
|
+
|
181
|
+
for(var i =0; i < len; i++){
|
182
|
+
//check this subset
|
183
|
+
var set = sets[i];
|
184
|
+
if( can.Object.subset(checkSet, set, compares) ){
|
185
|
+
subsets.push(set)
|
186
|
+
}
|
187
|
+
}
|
188
|
+
return subsets;
|
189
|
+
};
|
190
|
+
/**
|
191
|
+
* @function subset
|
192
|
+
* Compares if checkSet is a subset of set
|
193
|
+
* @param {Object} checkSet
|
194
|
+
* @param {Object} set
|
195
|
+
* @param {Object} [compares]
|
196
|
+
* @param {Object} [checkPropCount]
|
197
|
+
*/
|
198
|
+
can.Object.subset = function(subset, set, compares){
|
199
|
+
// go through set {type: 'folder'} and make sure every property
|
200
|
+
// is in subset {type: 'folder', parentId :5}
|
201
|
+
// then make sure that set has fewer properties
|
202
|
+
// make sure we are only checking 'important' properties
|
203
|
+
// in subset (ones that have to have a value)
|
204
|
+
|
205
|
+
var setPropCount =0,
|
206
|
+
compares = compares || {};
|
207
|
+
|
208
|
+
for(var prop in set){
|
209
|
+
|
210
|
+
if(! same(subset[prop], set[prop], compares[prop], subset, set ) ){
|
211
|
+
return false;
|
212
|
+
}
|
213
|
+
}
|
214
|
+
return true;
|
215
|
+
}
|
216
|
+
|
217
|
+
|
218
|
+
var compareMethods = {
|
219
|
+
"null" : function(){
|
220
|
+
return true;
|
221
|
+
},
|
222
|
+
i : function(a, b){
|
223
|
+
return (""+a).toLowerCase() == (""+b).toLowerCase()
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
|
228
|
+
;
|
229
|
+
|
230
|
+
|
231
|
+
var updateSettings = function (settings, originalOptions) {
|
232
|
+
if (!can.fixture.on) {
|
233
|
+
return;
|
234
|
+
}
|
235
|
+
|
236
|
+
//simple wrapper for logging
|
237
|
+
var log = function () {
|
238
|
+
if (window.console && console.log) {
|
239
|
+
console.log.apply(console, Array.prototype.slice.call(arguments));
|
240
|
+
}
|
241
|
+
}
|
242
|
+
|
243
|
+
// We always need the type which can also be called method, default to GET
|
244
|
+
settings.type = settings.type || settings.method || 'GET';
|
245
|
+
|
246
|
+
// add the fixture option if programmed in
|
247
|
+
var data = overwrite(settings);
|
248
|
+
|
249
|
+
// if we don't have a fixture, do nothing
|
250
|
+
if (!settings.fixture) {
|
251
|
+
if (window.location.protocol === "file:") {
|
252
|
+
log("ajax request to " + settings.url + ", no fixture found");
|
253
|
+
}
|
254
|
+
return;
|
255
|
+
}
|
256
|
+
|
257
|
+
//if referencing something else, update the fixture option
|
258
|
+
if (typeof settings.fixture === "string" && can.fixture[settings.fixture]) {
|
259
|
+
settings.fixture = can.fixture[settings.fixture];
|
260
|
+
}
|
261
|
+
|
262
|
+
// if a string, we just point to the right url
|
263
|
+
if (typeof settings.fixture == "string") {
|
264
|
+
var url = settings.fixture;
|
265
|
+
|
266
|
+
if (/^\/\//.test(url)) {
|
267
|
+
// this lets us use rootUrl w/o having steal...
|
268
|
+
url = can.fixture.rootUrl === steal.root ?
|
269
|
+
steal.root.mapJoin(settings.fixture.substr(2)) + '' :
|
270
|
+
can.fixture.rootUrl + settings.fixture.substr(2);
|
271
|
+
}
|
272
|
+
|
273
|
+
delete settings.fixture;
|
274
|
+
|
275
|
+
//@steal-remove-start
|
276
|
+
log("looking for fixture in " + url);
|
277
|
+
//@steal-remove-end
|
278
|
+
|
279
|
+
settings.url = url;
|
280
|
+
settings.data = null;
|
281
|
+
settings.type = "GET";
|
282
|
+
if (!settings.error) {
|
283
|
+
settings.error = function (xhr, error, message) {
|
284
|
+
throw "fixtures.js Error " + error + " " + message;
|
285
|
+
};
|
286
|
+
}
|
287
|
+
}
|
288
|
+
else {
|
289
|
+
//@steal-remove-start
|
290
|
+
log("using a dynamic fixture for " + settings.type + " " + settings.url);
|
291
|
+
//@steal-remove-end
|
292
|
+
|
293
|
+
//it's a function ... add the fixture datatype so our fixture transport handles it
|
294
|
+
// TODO: make everything go here for timing and other fun stuff
|
295
|
+
// add to settings data from fixture ...
|
296
|
+
settings.dataTypes && settings.dataTypes.splice(0, 0, "fixture");
|
297
|
+
|
298
|
+
if (data && originalOptions) {
|
299
|
+
can.extend(originalOptions.data, data)
|
300
|
+
}
|
301
|
+
}
|
302
|
+
},
|
303
|
+
// A helper function that takes what's called with response
|
304
|
+
// and moves some common args around to make it easier to call
|
305
|
+
extractResponse = function(status, statusText, responses, headers) {
|
306
|
+
// if we get response(RESPONSES, HEADERS)
|
307
|
+
if(typeof status != "number"){
|
308
|
+
headers = statusText;
|
309
|
+
responses = status;
|
310
|
+
statusText = "success"
|
311
|
+
status = 200;
|
312
|
+
}
|
313
|
+
// if we get response(200, RESPONSES, HEADERS)
|
314
|
+
if(typeof statusText != "string"){
|
315
|
+
headers = responses;
|
316
|
+
responses = statusText;
|
317
|
+
statusText = "success";
|
318
|
+
}
|
319
|
+
return [status, statusText, extractResponses(this, responses), headers];
|
320
|
+
},
|
321
|
+
// If we get data instead of responses,
|
322
|
+
// make sure we provide a response type that matches the first datatype (typically json)
|
323
|
+
extractResponses = function(settings, responses){
|
324
|
+
var next = settings.dataTypes ? settings.dataTypes[0] : (settings.dataType || 'json');
|
325
|
+
if (!responses || !responses[next]) {
|
326
|
+
var tmp = {}
|
327
|
+
tmp[next] = responses;
|
328
|
+
responses = tmp;
|
329
|
+
}
|
330
|
+
return responses;
|
331
|
+
};
|
332
|
+
|
333
|
+
//used to check urls
|
334
|
+
// check if jQuery
|
335
|
+
if (can.ajaxPrefilter && can.ajaxTransport) {
|
336
|
+
|
337
|
+
// the pre-filter needs to re-route the url
|
338
|
+
can.ajaxPrefilter(updateSettings);
|
339
|
+
|
340
|
+
can.ajaxTransport("fixture", function (s, original) {
|
341
|
+
// remove the fixture from the datatype
|
342
|
+
s.dataTypes.shift();
|
343
|
+
|
344
|
+
//we'll return the result of the next data type
|
345
|
+
var timeout, stopped = false;
|
346
|
+
|
347
|
+
return {
|
348
|
+
send: function (headers, callback) {
|
349
|
+
// we'll immediately wait the delay time for all fixtures
|
350
|
+
timeout = setTimeout(function () {
|
351
|
+
// if the user wants to call success on their own, we allow it ...
|
352
|
+
var success = function() {
|
353
|
+
if(stopped === false) {
|
354
|
+
callback.apply(null, extractResponse.apply(s, arguments) );
|
355
|
+
}
|
356
|
+
},
|
357
|
+
// get the result form the fixture
|
358
|
+
result = s.fixture(original, success, headers, s);
|
359
|
+
if(result !== undefined) {
|
360
|
+
// make sure the result has the right dataType
|
361
|
+
callback(200, "success", extractResponses(s, result), {});
|
362
|
+
}
|
363
|
+
}, can.fixture.delay);
|
364
|
+
},
|
365
|
+
abort: function () {
|
366
|
+
stopped = true;
|
367
|
+
clearTimeout(timeout)
|
368
|
+
}
|
369
|
+
};
|
370
|
+
});
|
371
|
+
} else {
|
372
|
+
var AJAX = can.ajax;
|
373
|
+
can.ajax = function (settings) {
|
374
|
+
updateSettings(settings, settings);
|
375
|
+
if (settings.fixture) {
|
376
|
+
var timeout, d = new can.Deferred(),
|
377
|
+
stopped = false;
|
378
|
+
|
379
|
+
//TODO this should work with response
|
380
|
+
d.getResponseHeader = function () {
|
381
|
+
}
|
382
|
+
|
383
|
+
// call success and fail
|
384
|
+
d.then(settings.success, settings.fail);
|
385
|
+
|
386
|
+
// abort should stop the timeout and calling success
|
387
|
+
d.abort = function () {
|
388
|
+
clearTimeout(timeout);
|
389
|
+
stopped = true;
|
390
|
+
d.reject(d)
|
391
|
+
}
|
392
|
+
// set a timeout that simulates making a request ....
|
393
|
+
timeout = setTimeout(function () {
|
394
|
+
// if the user wants to call success on their own, we allow it ...
|
395
|
+
var success = function() {
|
396
|
+
var response = extractResponse.apply(settings, arguments),
|
397
|
+
status = response[0];
|
398
|
+
|
399
|
+
if ( (status >= 200 && status < 300 || status === 304) && stopped === false) {
|
400
|
+
d.resolve(response[2][settings.dataType], "success", d)
|
401
|
+
} else {
|
402
|
+
// TODO probably resolve better
|
403
|
+
d.reject(d, 'error', response[1]);
|
404
|
+
}
|
405
|
+
},
|
406
|
+
// get the result form the fixture
|
407
|
+
result = settings.fixture(settings, success, settings.headers, settings);
|
408
|
+
if(result !== undefined) {
|
409
|
+
d.resolve(result, "success", d)
|
410
|
+
}
|
411
|
+
}, can.fixture.delay);
|
412
|
+
|
413
|
+
return d;
|
414
|
+
} else {
|
415
|
+
return AJAX(settings);
|
416
|
+
}
|
417
|
+
}
|
418
|
+
}
|
419
|
+
|
420
|
+
var typeTest = /^(script|json|test|jsonp)$/,
|
421
|
+
// a list of 'overwrite' settings object
|
422
|
+
overwrites = [],
|
423
|
+
// returns the index of an overwrite function
|
424
|
+
find = function (settings, exact) {
|
425
|
+
for (var i = 0; i < overwrites.length; i++) {
|
426
|
+
if ($fixture._similar(settings, overwrites[i], exact)) {
|
427
|
+
return i;
|
428
|
+
}
|
429
|
+
}
|
430
|
+
return -1;
|
431
|
+
},
|
432
|
+
// overwrites the settings fixture if an overwrite matches
|
433
|
+
overwrite = function (settings) {
|
434
|
+
var index = find(settings);
|
435
|
+
if (index > -1) {
|
436
|
+
settings.fixture = overwrites[index].fixture;
|
437
|
+
return $fixture._getData(overwrites[index].url, settings.url)
|
438
|
+
}
|
439
|
+
|
440
|
+
},
|
441
|
+
/**
|
442
|
+
* Makes an attempt to guess where the id is at in the url and returns it.
|
443
|
+
* @param {Object} settings
|
444
|
+
*/
|
445
|
+
getId = function (settings) {
|
446
|
+
var id = settings.data.id;
|
447
|
+
|
448
|
+
if (id === undefined && typeof settings.data === "number") {
|
449
|
+
id = settings.data;
|
450
|
+
}
|
451
|
+
|
452
|
+
/*
|
453
|
+
Check for id in params(if query string)
|
454
|
+
If this is just a string representation of an id, parse
|
455
|
+
if(id === undefined && typeof settings.data === "string") {
|
456
|
+
id = settings.data;
|
457
|
+
}
|
458
|
+
//*/
|
459
|
+
|
460
|
+
if (id === undefined) {
|
461
|
+
settings.url.replace(/\/(\d+)(\/|$|\.)/g, function (all, num) {
|
462
|
+
id = num;
|
463
|
+
});
|
464
|
+
}
|
465
|
+
|
466
|
+
if (id === undefined) {
|
467
|
+
id = settings.url.replace(/\/(\w+)(\/|$|\.)/g, function (all, num) {
|
468
|
+
if (num != 'update') {
|
469
|
+
id = num;
|
470
|
+
}
|
471
|
+
})
|
472
|
+
}
|
473
|
+
|
474
|
+
if (id === undefined) { // if still not set, guess a random number
|
475
|
+
id = Math.round(Math.random() * 1000)
|
476
|
+
}
|
477
|
+
|
478
|
+
return id;
|
479
|
+
};
|
480
|
+
|
481
|
+
/**
|
482
|
+
* @plugin can/util/fixture
|
483
|
+
* @test can/util/fixture/qunit.html
|
484
|
+
*
|
485
|
+
* `can.fixture` intercept an AJAX request and simulates the response with a file or function.
|
486
|
+
* Read more about the usage in the [overview can.fixture].
|
487
|
+
*
|
488
|
+
* @param {Object|String} settings Configures the AJAX requests the fixture should
|
489
|
+
* intercept. If an __object__ is passed, the object's properties and values
|
490
|
+
* are matched against the settings passed to can.ajax.
|
491
|
+
*
|
492
|
+
* If a __string__ is passed, it can be used to match the url and type. Urls
|
493
|
+
* can be templated, using <code>{NAME}</code> as wildcards.
|
494
|
+
*
|
495
|
+
* @param {Function|String} fixture The response to use for the AJAX
|
496
|
+
* request. If a __string__ url is passed, the ajax request is redirected
|
497
|
+
* to the url. If a __function__ is provided, it looks like:
|
498
|
+
*
|
499
|
+
* fixture( originalSettings, settings, callback, headers)
|
500
|
+
*
|
501
|
+
* where:
|
502
|
+
*
|
503
|
+
* - originalSettings - the orignal settings passed to can.ajax
|
504
|
+
* - settings - the settings after all filters have run
|
505
|
+
* - callback - a callback to call with the response if the fixture executes asynchronously
|
506
|
+
* - headers - request headers
|
507
|
+
*
|
508
|
+
* If __null__ is passed, and there is a fixture at settings, that fixture will be removed,
|
509
|
+
* allowing the AJAX request to behave normally.
|
510
|
+
*/
|
511
|
+
var $fixture = can.fixture = function (settings, fixture) {
|
512
|
+
// if we provide a fixture ...
|
513
|
+
if (fixture !== undefined) {
|
514
|
+
if (typeof settings == 'string') {
|
515
|
+
// handle url strings
|
516
|
+
var matches = settings.match(/(GET|POST|PUT|DELETE) (.+)/i);
|
517
|
+
if (!matches) {
|
518
|
+
settings = {
|
519
|
+
url : settings
|
520
|
+
};
|
521
|
+
} else {
|
522
|
+
settings = {
|
523
|
+
url : matches[2],
|
524
|
+
type : matches[1]
|
525
|
+
};
|
526
|
+
}
|
527
|
+
|
528
|
+
}
|
529
|
+
|
530
|
+
//handle removing. An exact match if fixture was provided, otherwise, anything similar
|
531
|
+
var index = find(settings, !!fixture);
|
532
|
+
if (index > -1) {
|
533
|
+
overwrites.splice(index, 1)
|
534
|
+
}
|
535
|
+
if (fixture == null) {
|
536
|
+
return
|
537
|
+
}
|
538
|
+
settings.fixture = fixture;
|
539
|
+
overwrites.push(settings)
|
540
|
+
} else {
|
541
|
+
can.each(settings, function(fixture, url){
|
542
|
+
$fixture(url, fixture);
|
543
|
+
})
|
544
|
+
}
|
545
|
+
};
|
546
|
+
var replacer = can.replacer;
|
547
|
+
|
548
|
+
can.extend(can.fixture, {
|
549
|
+
// given ajax settings, find an overwrite
|
550
|
+
_similar : function (settings, overwrite, exact) {
|
551
|
+
if (exact) {
|
552
|
+
return can.Object.same(settings, overwrite, {fixture : null})
|
553
|
+
} else {
|
554
|
+
return can.Object.subset(settings, overwrite, can.fixture._compare)
|
555
|
+
}
|
556
|
+
},
|
557
|
+
_compare : {
|
558
|
+
url : function (a, b) {
|
559
|
+
return !!$fixture._getData(b, a)
|
560
|
+
},
|
561
|
+
fixture : null,
|
562
|
+
type : "i"
|
563
|
+
},
|
564
|
+
// gets data from a url like "/todo/{id}" given "todo/5"
|
565
|
+
_getData : function (fixtureUrl, url) {
|
566
|
+
var order = [],
|
567
|
+
fixtureUrlAdjusted = fixtureUrl.replace('.', '\\.').replace('?', '\\?'),
|
568
|
+
res = new RegExp(fixtureUrlAdjusted.replace(replacer, function (whole, part) {
|
569
|
+
order.push(part)
|
570
|
+
return "([^\/]+)"
|
571
|
+
}) + "$").exec(url),
|
572
|
+
data = {};
|
573
|
+
|
574
|
+
if (!res) {
|
575
|
+
return null;
|
576
|
+
}
|
577
|
+
res.shift();
|
578
|
+
can.each(order, function (name) {
|
579
|
+
data[name] = res.shift()
|
580
|
+
})
|
581
|
+
return data;
|
582
|
+
},
|
583
|
+
|
584
|
+
make : function (types, count, make, filter) {
|
585
|
+
/**
|
586
|
+
* @function can.fixture.make
|
587
|
+
* @parent can.fixture
|
588
|
+
*
|
589
|
+
* `can.fixture.make` is used for findAll / findOne style requests.
|
590
|
+
*
|
591
|
+
* ## With can.ajax
|
592
|
+
*
|
593
|
+
* //makes a nested list of messages
|
594
|
+
* can.fixture.make(["messages","message"], 1000,
|
595
|
+
* function(i, messages){
|
596
|
+
* return {
|
597
|
+
* subject: "This is message "+i,
|
598
|
+
* body: "Here is some text for this message",
|
599
|
+
* date: Math.floor( new Date().getTime() ),
|
600
|
+
* parentId : i < 100 ? null : Math.floor(Math.random()*i)
|
601
|
+
* }
|
602
|
+
* })
|
603
|
+
* //uses the message fixture to return messages limited by
|
604
|
+
* // offset, limit, order, etc.
|
605
|
+
* can.ajax({
|
606
|
+
* url: "messages",
|
607
|
+
* data: {
|
608
|
+
* offset: 100,
|
609
|
+
* limit: 50,
|
610
|
+
* order: ["date ASC"],
|
611
|
+
* parentId: 5},
|
612
|
+
* },
|
613
|
+
* fixture: "-messages",
|
614
|
+
* success: function( messages ) { ... }
|
615
|
+
* });
|
616
|
+
*
|
617
|
+
* ## With can.Model
|
618
|
+
*
|
619
|
+
* `can.fixture.make` returns a model store that offers `findAll`, `findOne`, `create`,
|
620
|
+
* `update` and `destroy` fixture functions you can map to a [can.Model] Ajax request.
|
621
|
+
* Consider a model like this:
|
622
|
+
*
|
623
|
+
* var Todo = can.Model({
|
624
|
+
* findAll : 'GET /todos',
|
625
|
+
* findOne : 'GET /todos/{id}',
|
626
|
+
* create : 'POST /todos',
|
627
|
+
* update : 'PUT /todos/{id}',
|
628
|
+
* destroy : 'DELETE /todos/{id}'
|
629
|
+
* }, {});
|
630
|
+
*
|
631
|
+
* And an unnamed generated fixture like this:
|
632
|
+
*
|
633
|
+
* var store = can.fixture.make(100, function(i) {
|
634
|
+
* return {
|
635
|
+
* id : i,
|
636
|
+
* name : 'Todo ' + i
|
637
|
+
* }
|
638
|
+
* });
|
639
|
+
*
|
640
|
+
* You can map can.Model requests using the return value of `can.fixture.make`:
|
641
|
+
*
|
642
|
+
* can.fixture('GET /todos', store.findAll);
|
643
|
+
* can.fixture('GET /todos/{id}', store.findOne);
|
644
|
+
* can.fixture('POST /todos', store.create);
|
645
|
+
* can.fixture('PUT /todos/{id}', store.update);
|
646
|
+
* can.fixture('DELETE /todos/{id}', store.destroy);
|
647
|
+
*
|
648
|
+
* @param {Array|String} types An array of the fixture names or the singular fixture name.
|
649
|
+
* If an array, the first item is the plural fixture name (prefixed with -) and the second
|
650
|
+
* item is the singular name. If a string, it's assumed to be the singular fixture name. Make
|
651
|
+
* will simply add s to the end of it for the plural name. If this parameter is not an array
|
652
|
+
* or a String the fixture won't be added and only return the generator object.
|
653
|
+
* @param {Number} count the number of items to create
|
654
|
+
* @param {Function} make a function that will return the JavaScript object. The
|
655
|
+
* make function is called back with the id and the current array of items.
|
656
|
+
* @param {Function} filter (optional) a function used to further filter results. Used for to simulate
|
657
|
+
* server params like searchText or startDate.
|
658
|
+
* The function should return true if the item passes the filter,
|
659
|
+
* false otherwise. For example:
|
660
|
+
*
|
661
|
+
*
|
662
|
+
* function(item, settings){
|
663
|
+
* if(settings.data.searchText){
|
664
|
+
* var regex = new RegExp("^"+settings.data.searchText)
|
665
|
+
* return regex.test(item.name);
|
666
|
+
* }
|
667
|
+
* }
|
668
|
+
*
|
669
|
+
* @return {Object} A generator object providing fixture functions for *findAll*, *findOne*, *create*,
|
670
|
+
* *update* and *destroy*.
|
671
|
+
*/
|
672
|
+
var items = [], // TODO: change this to a hash
|
673
|
+
findOne = function (id) {
|
674
|
+
for (var i = 0; i < items.length; i++) {
|
675
|
+
if (id == items[i].id) {
|
676
|
+
return items[i];
|
677
|
+
}
|
678
|
+
}
|
679
|
+
},
|
680
|
+
methods = {};
|
681
|
+
|
682
|
+
if (typeof types === "string") {
|
683
|
+
types = [types + "s", types ]
|
684
|
+
} else if (!can.isArray(types)) {
|
685
|
+
filter = make;
|
686
|
+
make = count;
|
687
|
+
count = types;
|
688
|
+
}
|
689
|
+
|
690
|
+
// make all items
|
691
|
+
can.extend(methods, {
|
692
|
+
findAll : function (settings) {
|
693
|
+
//copy array of items
|
694
|
+
var retArr = items.slice(0);
|
695
|
+
settings.data = settings.data || {};
|
696
|
+
//sort using order
|
697
|
+
//order looks like ["age ASC","gender DESC"]
|
698
|
+
can.each((settings.data.order || []).slice(0).reverse(), function (name) {
|
699
|
+
var split = name.split(" ");
|
700
|
+
retArr = retArr.sort(function (a, b) {
|
701
|
+
if (split[1].toUpperCase() !== "ASC") {
|
702
|
+
if (a[split[0]] < b[split[0]]) {
|
703
|
+
return 1;
|
704
|
+
} else if (a[split[0]] == b[split[0]]) {
|
705
|
+
return 0
|
706
|
+
} else {
|
707
|
+
return -1;
|
708
|
+
}
|
709
|
+
}
|
710
|
+
else {
|
711
|
+
if (a[split[0]] < b[split[0]]) {
|
712
|
+
return -1;
|
713
|
+
} else if (a[split[0]] == b[split[0]]) {
|
714
|
+
return 0
|
715
|
+
} else {
|
716
|
+
return 1;
|
717
|
+
}
|
718
|
+
}
|
719
|
+
});
|
720
|
+
});
|
721
|
+
|
722
|
+
//group is just like a sort
|
723
|
+
can.each((settings.data.group || []).slice(0).reverse(), function (name) {
|
724
|
+
var split = name.split(" ");
|
725
|
+
retArr = retArr.sort(function (a, b) {
|
726
|
+
return a[split[0]] > b[split[0]];
|
727
|
+
});
|
728
|
+
});
|
729
|
+
|
730
|
+
|
731
|
+
var offset = parseInt(settings.data.offset, 10) || 0,
|
732
|
+
limit = parseInt(settings.data.limit, 10) || (items.length - offset),
|
733
|
+
i = 0;
|
734
|
+
|
735
|
+
//filter results if someone added an attr like parentId
|
736
|
+
for (var param in settings.data) {
|
737
|
+
i = 0;
|
738
|
+
if (settings.data[param] !== undefined && // don't do this if the value of the param is null (ignore it)
|
739
|
+
(param.indexOf("Id") != -1 || param.indexOf("_id") != -1)) {
|
740
|
+
while (i < retArr.length) {
|
741
|
+
if (settings.data[param] != retArr[i][param]) {
|
742
|
+
retArr.splice(i, 1);
|
743
|
+
} else {
|
744
|
+
i++;
|
745
|
+
}
|
746
|
+
}
|
747
|
+
}
|
748
|
+
}
|
749
|
+
|
750
|
+
if (filter) {
|
751
|
+
i = 0;
|
752
|
+
while (i < retArr.length) {
|
753
|
+
if (!filter(retArr[i], settings)) {
|
754
|
+
retArr.splice(i, 1);
|
755
|
+
} else {
|
756
|
+
i++;
|
757
|
+
}
|
758
|
+
}
|
759
|
+
}
|
760
|
+
|
761
|
+
//return data spliced with limit and offset
|
762
|
+
return {
|
763
|
+
"count" : retArr.length,
|
764
|
+
"limit" : settings.data.limit,
|
765
|
+
"offset" : settings.data.offset,
|
766
|
+
"data" : retArr.slice(offset, offset + limit)
|
767
|
+
};
|
768
|
+
},
|
769
|
+
findOne : function (orig, response) {
|
770
|
+
var item = findOne(getId(orig));
|
771
|
+
response(item ? item : undefined);
|
772
|
+
},
|
773
|
+
update : function (orig,response) {
|
774
|
+
var id = getId(orig);
|
775
|
+
|
776
|
+
// TODO: make it work with non-linear ids ..
|
777
|
+
can.extend(findOne(id), orig.data);
|
778
|
+
response({
|
779
|
+
id : getId(orig)
|
780
|
+
}, {
|
781
|
+
location : orig.url + "/" + getId(orig)
|
782
|
+
});
|
783
|
+
},
|
784
|
+
destroy : function (settings) {
|
785
|
+
var id = getId(settings);
|
786
|
+
for (var i = 0; i < items.length; i++) {
|
787
|
+
if (items[i].id == id) {
|
788
|
+
items.splice(i, 1);
|
789
|
+
break;
|
790
|
+
}
|
791
|
+
}
|
792
|
+
|
793
|
+
// TODO: make it work with non-linear ids ..
|
794
|
+
can.extend(findOne(id) || {}, settings.data);
|
795
|
+
return {};
|
796
|
+
},
|
797
|
+
create : function (settings, response) {
|
798
|
+
var item = make(items.length, items);
|
799
|
+
|
800
|
+
can.extend(item, settings.data);
|
801
|
+
|
802
|
+
if (!item.id) {
|
803
|
+
item.id = items.length;
|
804
|
+
}
|
805
|
+
|
806
|
+
items.push(item);
|
807
|
+
var id = item.id || parseInt(Math.random() * 100000, 10);
|
808
|
+
response({
|
809
|
+
id : id
|
810
|
+
}, {
|
811
|
+
location : settings.url + "/" + id
|
812
|
+
})
|
813
|
+
}
|
814
|
+
});
|
815
|
+
|
816
|
+
for (var i = 0; i < (count); i++) {
|
817
|
+
//call back provided make
|
818
|
+
var item = make(i, items);
|
819
|
+
|
820
|
+
if (!item.id) {
|
821
|
+
item.id = i;
|
822
|
+
}
|
823
|
+
items.push(item);
|
824
|
+
}
|
825
|
+
|
826
|
+
// if we have types given add them to can.fixture
|
827
|
+
if(can.isArray(types)) {
|
828
|
+
can.fixture["~" + types[0]] = items;
|
829
|
+
can.fixture["-" + types[0]] = methods.findAll;
|
830
|
+
can.fixture["-" + types[1]] = methods.findOne;
|
831
|
+
can.fixture["-" + types[1]+"Update"] = methods.update;
|
832
|
+
can.fixture["-" + types[1]+"Destroy"] = methods.destroy;
|
833
|
+
can.fixture["-" + types[1]+"Create"] = methods.create;
|
834
|
+
}
|
835
|
+
|
836
|
+
return can.extend({
|
837
|
+
getId: getId,
|
838
|
+
find : function(settings){
|
839
|
+
return findOne( getId(settings) );
|
840
|
+
}
|
841
|
+
}, methods);
|
842
|
+
},
|
843
|
+
/**
|
844
|
+
* @function can.fixture.rand
|
845
|
+
* @parent can.fixture
|
846
|
+
*
|
847
|
+
* `can.fixture.rand` creates random integers or random arrays of
|
848
|
+
* other arrays.
|
849
|
+
*
|
850
|
+
* ## Examples
|
851
|
+
*
|
852
|
+
* var rand = can.fixture.rand;
|
853
|
+
*
|
854
|
+
* // get a random integer between 0 and 10 (inclusive)
|
855
|
+
* rand(11);
|
856
|
+
*
|
857
|
+
* // get a random number between -5 and 5 (inclusive)
|
858
|
+
* rand(-5, 6);
|
859
|
+
*
|
860
|
+
* // pick a random item from an array
|
861
|
+
* rand(["j","m","v","c"],1)[0]
|
862
|
+
*
|
863
|
+
* // pick a random number of items from an array
|
864
|
+
* rand(["j","m","v","c"])
|
865
|
+
*
|
866
|
+
* // pick 2 items from an array
|
867
|
+
* rand(["j","m","v","c"],2)
|
868
|
+
*
|
869
|
+
* // pick between 2 and 3 items at random
|
870
|
+
* rand(["j","m","v","c"],2,3)
|
871
|
+
*
|
872
|
+
*
|
873
|
+
* @param {Array|Number} arr An array of items to select from.
|
874
|
+
* If a number is provided, a random number is returned.
|
875
|
+
* If min and max are not provided, a random number of items are selected
|
876
|
+
* from this array.
|
877
|
+
* @param {Number} [min] If only min is provided, min items
|
878
|
+
* are selected.
|
879
|
+
* @param {Number} [max] If min and max are provided, a random number of
|
880
|
+
* items between min and max (inclusive) is selected.
|
881
|
+
*/
|
882
|
+
rand : function (arr, min, max) {
|
883
|
+
if (typeof arr == 'number') {
|
884
|
+
if (typeof min == 'number') {
|
885
|
+
return arr + Math.floor(Math.random() * (min - arr));
|
886
|
+
} else {
|
887
|
+
return Math.floor(Math.random() * arr);
|
888
|
+
}
|
889
|
+
|
890
|
+
}
|
891
|
+
var rand = arguments.callee;
|
892
|
+
// get a random set
|
893
|
+
if (min === undefined) {
|
894
|
+
return rand(arr, rand(arr.length + 1))
|
895
|
+
}
|
896
|
+
// get a random selection of arr
|
897
|
+
var res = [];
|
898
|
+
arr = arr.slice(0);
|
899
|
+
// set max
|
900
|
+
if (!max) {
|
901
|
+
max = min;
|
902
|
+
}
|
903
|
+
//random max
|
904
|
+
max = min + Math.round(rand(max - min))
|
905
|
+
for (var i = 0; i < max; i++) {
|
906
|
+
res.push(arr.splice(rand(arr.length), 1)[0])
|
907
|
+
}
|
908
|
+
return res;
|
909
|
+
},
|
910
|
+
/**
|
911
|
+
* @hide
|
912
|
+
*
|
913
|
+
* Use can.fixture.xhr to create an object that looks like an xhr object.
|
914
|
+
*
|
915
|
+
* ## Example
|
916
|
+
*
|
917
|
+
* The following example shows how the -restCreate fixture uses xhr to return
|
918
|
+
* a simulated xhr object:
|
919
|
+
* @codestart
|
920
|
+
* "-restCreate" : function( settings, cbType ) {
|
921
|
+
* switch(cbType){
|
922
|
+
* case "success":
|
923
|
+
* return [
|
924
|
+
* {id: parseInt(Math.random()*1000)},
|
925
|
+
* "success",
|
926
|
+
* can.fixture.xhr()];
|
927
|
+
* case "complete":
|
928
|
+
* return [
|
929
|
+
* can.fixture.xhr({
|
930
|
+
* getResponseHeader: function() {
|
931
|
+
* return settings.url+"/"+parseInt(Math.random()*1000);
|
932
|
+
* }
|
933
|
+
* }),
|
934
|
+
* "success"];
|
935
|
+
* }
|
936
|
+
* }
|
937
|
+
* @codeend
|
938
|
+
* @param {Object} [xhr] properties that you want to overwrite
|
939
|
+
* @return {Object} an object that looks like a successful XHR object.
|
940
|
+
*/
|
941
|
+
xhr : function (xhr) {
|
942
|
+
return can.extend({}, {
|
943
|
+
abort : can.noop,
|
944
|
+
getAllResponseHeaders : function () {
|
945
|
+
return "";
|
946
|
+
},
|
947
|
+
getResponseHeader : function () {
|
948
|
+
return "";
|
949
|
+
},
|
950
|
+
open : can.noop,
|
951
|
+
overrideMimeType : can.noop,
|
952
|
+
readyState : 4,
|
953
|
+
responseText : "",
|
954
|
+
responseXML : null,
|
955
|
+
send : can.noop,
|
956
|
+
setRequestHeader : can.noop,
|
957
|
+
status : 200,
|
958
|
+
statusText : "OK"
|
959
|
+
}, xhr);
|
960
|
+
},
|
961
|
+
/**
|
962
|
+
* @attribute can.fixture.on
|
963
|
+
* @parent can.fixture
|
964
|
+
*
|
965
|
+
* `can.fixture.on` lets you programatically turn off fixtures. This is mostly used for testing.
|
966
|
+
*
|
967
|
+
* can.fixture.on = false
|
968
|
+
* Task.findAll({}, function(){
|
969
|
+
* can.fixture.on = true;
|
970
|
+
* })
|
971
|
+
*/
|
972
|
+
on : true
|
973
|
+
});
|
974
|
+
/**
|
975
|
+
* @attribute can.fixture.delay
|
976
|
+
* @parent can.fixture
|
977
|
+
*
|
978
|
+
* `can.fixture.delay` indicates the delay in milliseconds between an ajax request is made and
|
979
|
+
* the success and complete handlers are called. This only sets
|
980
|
+
* functional synchronous fixtures that return a result. By default, the delay is 200ms.
|
981
|
+
*
|
982
|
+
* @codestart
|
983
|
+
* steal('can/util/fixtures').then(function(){
|
984
|
+
* can.fixture.delay = 1000;
|
985
|
+
* })
|
986
|
+
* @codeend
|
987
|
+
*/
|
988
|
+
can.fixture.delay = 200;
|
989
|
+
|
990
|
+
/**
|
991
|
+
* @attribute can.fixture.rootUrl
|
992
|
+
* @parent can.fixture
|
993
|
+
*
|
994
|
+
* `can.fixture.rootUrl` contains the root URL for fixtures to use.
|
995
|
+
* If you are using StealJS it will use the Steal root
|
996
|
+
* URL by default.
|
997
|
+
*/
|
998
|
+
can.fixture.rootUrl = window.steal ? steal.root : undefined;
|
999
|
+
|
1000
|
+
can.fixture["-handleFunction"] = function (settings) {
|
1001
|
+
if (typeof settings.fixture === "string" && can.fixture[settings.fixture]) {
|
1002
|
+
settings.fixture = can.fixture[settings.fixture];
|
1003
|
+
}
|
1004
|
+
if (typeof settings.fixture == "function") {
|
1005
|
+
setTimeout(function () {
|
1006
|
+
if (settings.success) {
|
1007
|
+
settings.success.apply(null, settings.fixture(settings, "success"));
|
1008
|
+
}
|
1009
|
+
if (settings.complete) {
|
1010
|
+
settings.complete.apply(null, settings.fixture(settings, "complete"));
|
1011
|
+
}
|
1012
|
+
}, can.fixture.delay);
|
1013
|
+
return true;
|
1014
|
+
}
|
1015
|
+
return false;
|
1016
|
+
};
|
1017
|
+
|
1018
|
+
//Expose this for fixture debugging
|
1019
|
+
can.fixture.overwrites = overwrites;
|
1020
|
+
})(this.can, this )
|