canjs-rails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 )
|