~michael.nelson/ubuntu-webcatalog/1267731-import-sca-apps-error

« back to all changes in this revision

Viewing changes to src/webcatalog/static/yui/3.10.3/build/model-sync-rest/model-sync-rest-debug.js

  • Committer: Tarmac
  • Author(s): Stephen Stewart
  • Date: 2013-06-26 09:19:32 UTC
  • mfrom: (184.1.4 ubuntu-global-nav)
  • Revision ID: tarmac-20130626091932-8urtuli368k8p7ds
[r=beuno,jonas-drange] add ubuntu global nav to apps.ubuntu.com

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
YUI 3.10.3 (build 2fb5187)
 
3
Copyright 2013 Yahoo! Inc. All rights reserved.
 
4
Licensed under the BSD License.
 
5
http://yuilibrary.com/license/
 
6
*/
 
7
 
 
8
YUI.add('model-sync-rest', function (Y, NAME) {
 
9
 
 
10
/**
 
11
An extension which provides a RESTful XHR sync implementation that can be mixed
 
12
into a Model or ModelList subclass.
 
13
 
 
14
@module app
 
15
@submodule model-sync-rest
 
16
@since 3.6.0
 
17
**/
 
18
 
 
19
var Lang = Y.Lang;
 
20
 
 
21
/**
 
22
An extension which provides a RESTful XHR sync implementation that can be mixed
 
23
into a Model or ModelList subclass.
 
24
 
 
25
This makes it trivial for your Model or ModelList subclasses communicate and
 
26
transmit their data via RESTful XHRs. In most cases you'll only need to provide
 
27
a value for `root` when sub-classing `Y.Model`.
 
28
 
 
29
    Y.User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], {
 
30
        root: '/users'
 
31
    });
 
32
 
 
33
    Y.Users = Y.Base.create('users', Y.ModelList, [Y.ModelSync.REST], {
 
34
        // By convention `Y.User`'s `root` will be used for the lists' URL.
 
35
        model: Y.User
 
36
    });
 
37
 
 
38
    var users = new Y.Users();
 
39
 
 
40
    // GET users list from: "/users"
 
41
    users.load(function () {
 
42
        var firstUser = users.item(0);
 
43
 
 
44
        firstUser.get('id'); // => "1"
 
45
 
 
46
        // PUT updated user data at: "/users/1"
 
47
        firstUser.set('name', 'Eric').save();
 
48
    });
 
49
 
 
50
@class ModelSync.REST
 
51
@extensionfor Model
 
52
@extensionfor ModelList
 
53
@since 3.6.0
 
54
**/
 
55
function RESTSync() {}
 
56
 
 
57
/**
 
58
A request authenticity token to validate HTTP requests made by this extension
 
59
with the server when the request results in changing persistent state. This
 
60
allows you to protect your server from Cross-Site Request Forgery attacks.
 
61
 
 
62
A CSRF token provided by the server can be embedded in the HTML document and
 
63
assigned to `YUI.Env.CSRF_TOKEN` like this:
 
64
 
 
65
    <script>
 
66
        YUI.Env.CSRF_TOKEN = {{session.authenticityToken}};
 
67
    </script>
 
68
 
 
69
The above should come after YUI seed file so that `YUI.Env` will be defined.
 
70
 
 
71
**Note:** This can be overridden on a per-request basis. See `sync()` method.
 
72
 
 
73
When a value for the CSRF token is provided, either statically or via `options`
 
74
passed to the `save()` and `destroy()` methods, the applicable HTTP requests
 
75
will have a `X-CSRF-Token` header added with the token value.
 
76
 
 
77
@property CSRF_TOKEN
 
78
@type String
 
79
@default YUI.Env.CSRF_TOKEN
 
80
@static
 
81
@since 3.6.0
 
82
**/
 
83
RESTSync.CSRF_TOKEN = YUI.Env.CSRF_TOKEN;
 
84
 
 
85
/**
 
86
Static flag to use the HTTP POST method instead of PUT or DELETE.
 
87
 
 
88
If the server-side HTTP framework isn't RESTful, setting this flag to `true`
 
89
will cause all PUT and DELETE requests to instead use the POST HTTP method, and
 
90
add a `X-HTTP-Method-Override` HTTP header with the value of the method type
 
91
which was overridden.
 
92
 
 
93
@property EMULATE_HTTP
 
94
@type Boolean
 
95
@default false
 
96
@static
 
97
@since 3.6.0
 
98
**/
 
99
RESTSync.EMULATE_HTTP = false;
 
100
 
 
101
/**
 
102
Default headers used with all XHRs.
 
103
 
 
104
By default the `Accept` and `Content-Type` headers are set to
 
105
"application/json", this signals to the HTTP server to process the request
 
106
bodies as JSON and send JSON responses. If you're sending and receiving content
 
107
other than JSON, you can override these headers and the `parse()` and
 
108
`serialize()` methods.
 
109
 
 
110
**Note:** These headers will be merged with any request-specific headers, and
 
111
the request-specific headers will take precedence.
 
112
 
 
113
@property HTTP_HEADERS
 
114
@type Object
 
115
@default
 
116
    {
 
117
        "Accept"      : "application/json",
 
118
        "Content-Type": "application/json"
 
119
    }
 
120
@static
 
121
@since 3.6.0
 
122
**/
 
123
RESTSync.HTTP_HEADERS = {
 
124
    'Accept'      : 'application/json',
 
125
    'Content-Type': 'application/json'
 
126
};
 
127
 
 
128
/**
 
129
Static mapping of RESTful HTTP methods corresponding to CRUD actions.
 
130
 
 
131
@property HTTP_METHODS
 
132
@type Object
 
133
@default
 
134
    {
 
135
        "create": "POST",
 
136
        "read"  : "GET",
 
137
        "update": "PUT",
 
138
        "delete": "DELETE"
 
139
    }
 
140
@static
 
141
@since 3.6.0
 
142
**/
 
143
RESTSync.HTTP_METHODS = {
 
144
    'create': 'POST',
 
145
    'read'  : 'GET',
 
146
    'update': 'PUT',
 
147
    'delete': 'DELETE'
 
148
};
 
149
 
 
150
/**
 
151
The number of milliseconds before the XHRs will timeout/abort. This defaults to
 
152
30 seconds.
 
153
 
 
154
**Note:** This can be overridden on a per-request basis. See `sync()` method.
 
155
 
 
156
@property HTTP_TIMEOUT
 
157
@type Number
 
158
@default 30000
 
159
@static
 
160
@since 3.6.0
 
161
**/
 
162
RESTSync.HTTP_TIMEOUT = 30000;
 
163
 
 
164
/**
 
165
Properties that shouldn't be turned into ad-hoc attributes when passed to a
 
166
Model or ModelList constructor.
 
167
 
 
168
@property _NON_ATTRS_CFG
 
169
@type Array
 
170
@default ["root", "url"]
 
171
@static
 
172
@protected
 
173
@since 3.6.0
 
174
**/
 
175
RESTSync._NON_ATTRS_CFG = ['root', 'url'];
 
176
 
 
177
RESTSync.prototype = {
 
178
 
 
179
    // -- Public Properties ----------------------------------------------------
 
180
 
 
181
    /**
 
182
    A string which represents the root or collection part of the URL which
 
183
    relates to a Model or ModelList. Usually this value should be same for all
 
184
    instances of a specific Model/ModelList subclass.
 
185
 
 
186
    When sub-classing `Y.Model`, usually you'll only need to override this
 
187
    property, which lets the URLs for the XHRs be generated by convention. If
 
188
    the `root` string ends with a trailing-slash, XHR URLs will also end with a
 
189
    "/", and if the `root` does not end with a slash, neither will the XHR URLs.
 
190
 
 
191
    @example
 
192
        Y.User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], {
 
193
            root: '/users'
 
194
        });
 
195
 
 
196
        var currentUser, newUser;
 
197
 
 
198
        // GET the user data from: "/users/123"
 
199
        currentUser = new Y.User({id: '123'}).load();
 
200
 
 
201
        // POST the new user data to: "/users"
 
202
        newUser = new Y.User({name: 'Eric Ferraiuolo'}).save();
 
203
 
 
204
    When sub-classing `Y.ModelList`, usually you'll want to ignore configuring
 
205
    the `root` and simply rely on the build-in convention of the list's
 
206
    generated URLs defaulting to the `root` specified by the list's `model`.
 
207
 
 
208
    @property root
 
209
    @type String
 
210
    @default ""
 
211
    @since 3.6.0
 
212
    **/
 
213
    root: '',
 
214
 
 
215
    /**
 
216
    A string which specifies the URL to use when making XHRs, if not value is
 
217
    provided, the URLs used to make XHRs will be generated by convention.
 
218
 
 
219
    While a `url` can be provided for each Model/ModelList instance, usually
 
220
    you'll want to either rely on the default convention or provide a tokenized
 
221
    string on the prototype which can be used for all instances.
 
222
 
 
223
    When sub-classing `Y.Model`, you will probably be able to rely on the
 
224
    default convention of generating URLs in conjunction with the `root`
 
225
    property and whether the model is new or not (i.e. has an `id`). If the
 
226
    `root` property ends with a trailing-slash, the generated URL for the
 
227
    specific model will also end with a trailing-slash.
 
228
 
 
229
    @example
 
230
        Y.User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], {
 
231
            root: '/users/'
 
232
        });
 
233
 
 
234
        var currentUser, newUser;
 
235
 
 
236
        // GET the user data from: "/users/123/"
 
237
        currentUser = new Y.User({id: '123'}).load();
 
238
 
 
239
        // POST the new user data to: "/users/"
 
240
        newUser = new Y.User({name: 'Eric Ferraiuolo'}).save();
 
241
 
 
242
    If a `url` is specified, it will be processed by `Y.Lang.sub()`, which is
 
243
    useful when the URLs for a Model/ModelList subclass match a specific pattern
 
244
    and can use simple replacement tokens; e.g.:
 
245
 
 
246
    @example
 
247
        Y.User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], {
 
248
            root: '/users',
 
249
            url : '/users/{username}'
 
250
        });
 
251
 
 
252
    **Note:** String subsitituion of the `url` only use string an number values
 
253
    provided by this object's attribute and/or the `options` passed to the
 
254
    `getURL()` method. Do not expect something fancy to happen with Object,
 
255
    Array, or Boolean values, they will simply be ignored.
 
256
 
 
257
    If your URLs have plural roots or collection URLs, while the specific item
 
258
    resources are under a singular name, e.g. "/users" (plural) and "/user/123"
 
259
    (singular), you'll probably want to configure the `root` and `url`
 
260
    properties like this:
 
261
 
 
262
    @example
 
263
        Y.User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], {
 
264
            root: '/users',
 
265
            url : '/user/{id}'
 
266
        });
 
267
 
 
268
        var currentUser, newUser;
 
269
 
 
270
        // GET the user data from: "/user/123"
 
271
        currentUser = new Y.User({id: '123'}).load();
 
272
 
 
273
        // POST the new user data to: "/users"
 
274
        newUser = new Y.User({name: 'Eric Ferraiuolo'}).save();
 
275
 
 
276
    When sub-classing `Y.ModelList`, usually you'll be able to rely on the
 
277
    associated `model` to supply its `root` to be used as the model list's URL.
 
278
    If this needs to be customized, you can provide a simple string for the
 
279
    `url` property.
 
280
 
 
281
    @example
 
282
        Y.Users = Y.Base.create('users', Y.ModelList, [Y.ModelSync.REST], {
 
283
            // Leverages `Y.User`'s `root`, which is "/users".
 
284
            model: Y.User
 
285
        });
 
286
 
 
287
        // Or specified explicitly...
 
288
 
 
289
        Y.Users = Y.Base.create('users', Y.ModelList, [Y.ModelSync.REST], {
 
290
            model: Y.User,
 
291
            url  : '/users'
 
292
        });
 
293
 
 
294
    @property url
 
295
    @type String
 
296
    @default ""
 
297
    @since 3.6.0
 
298
    **/
 
299
    url: '',
 
300
 
 
301
    // -- Lifecycle Methods ----------------------------------------------------
 
302
 
 
303
    initializer: function (config) {
 
304
        config || (config = {});
 
305
 
 
306
        // Overrides `root` at the instance level.
 
307
        if ('root' in config) {
 
308
            this.root = config.root || '';
 
309
        }
 
310
 
 
311
        // Overrides `url` at the instance level.
 
312
        if ('url' in config) {
 
313
            this.url = config.url || '';
 
314
        }
 
315
    },
 
316
 
 
317
    // -- Public Methods -------------------------------------------------------
 
318
 
 
319
    /**
 
320
    Returns the URL for this model or model list for the given `action` and
 
321
    `options`, if specified.
 
322
 
 
323
    This method correctly handles the variations of `root` and `url` values and
 
324
    is called by the `sync()` method to get the URLs used to make the XHRs.
 
325
 
 
326
    You can override this method if you need to provide a specific
 
327
    implementation for how the URLs of your Model and ModelList subclasses need
 
328
    to be generated.
 
329
 
 
330
    @method getURL
 
331
    @param {String} [action] Optional `sync()` action for which to generate the
 
332
        URL.
 
333
    @param {Object} [options] Optional options which may be used to help
 
334
        generate the URL.
 
335
    @return {String} this model's or model list's URL for the the given
 
336
        `action` and `options`.
 
337
    @since 3.6.0
 
338
    **/
 
339
    getURL: function (action, options) {
 
340
        var root = this.root,
 
341
            url  = this.url;
 
342
 
 
343
        // If this is a model list, use its `url` and substitute placeholders,
 
344
        // but default to the `root` of its `model`. By convention a model's
 
345
        // `root` is the location to a collection resource.
 
346
        if (this._isYUIModelList) {
 
347
            if (!url) {
 
348
                return this.model.prototype.root;
 
349
            }
 
350
 
 
351
            return this._substituteURL(url, Y.merge(this.getAttrs(), options));
 
352
        }
 
353
 
 
354
        // Assume `this` is a model.
 
355
 
 
356
        // When a model is new, i.e. has no `id`, the `root` should be used. By
 
357
        // convention a model's `root` is the location to a collection resource.
 
358
        // The model's `url` will be used as a fallback if `root` isn't defined.
 
359
        if (root && (action === 'create' || this.isNew())) {
 
360
            return root;
 
361
        }
 
362
 
 
363
        // When a model's `url` is not provided, we'll generate a URL to use by
 
364
        // convention. This will combine the model's `id` with its configured
 
365
        // `root` and add a trailing-slash if the root ends with "/".
 
366
        if (!url) {
 
367
            return this._joinURL(this.getAsURL('id') || '');
 
368
        }
 
369
 
 
370
        // Substitute placeholders in the `url` with URL-encoded values from the
 
371
        // model's attribute values or the specified `options`.
 
372
        return this._substituteURL(url, Y.merge(this.getAttrs(), options));
 
373
    },
 
374
 
 
375
    /**
 
376
    Called to parse the response object returned from `Y.io()`. This method
 
377
    receives the full response object and is expected to "prep" a response which
 
378
    is suitable to pass to the `parse()` method.
 
379
 
 
380
    By default the response body is returned (`responseText`), because it
 
381
    usually represents the entire entity of this model on the server.
 
382
 
 
383
    If you need to parse data out of the response's headers you should do so by
 
384
    overriding this method. If you'd like the entire response object from the
 
385
    XHR to be passed to your `parse()` method, you can simply assign this
 
386
    property to `false`.
 
387
 
 
388
    @method parseIOResponse
 
389
    @param {Object} response Response object from `Y.io()`.
 
390
    @return {Any} The modified response to pass along to the `parse()` method.
 
391
    @since 3.7.0
 
392
    **/
 
393
    parseIOResponse: function (response) {
 
394
        return response.responseText;
 
395
    },
 
396
 
 
397
    /**
 
398
    Serializes `this` model to be used as the HTTP request entity body.
 
399
 
 
400
    By default this model will be serialized to a JSON string via its `toJSON()`
 
401
    method.
 
402
 
 
403
    You can override this method when the HTTP server expects a different
 
404
    representation of this model's data that is different from the default JSON
 
405
    serialization. If you're sending and receive content other than JSON, be
 
406
    sure change the `Accept` and `Content-Type` `HTTP_HEADERS` as well.
 
407
 
 
408
    **Note:** A model's `toJSON()` method can also be overridden. If you only
 
409
    need to modify which attributes are serialized to JSON, that's a better
 
410
    place to start.
 
411
 
 
412
    @method serialize
 
413
    @param {String} [action] Optional `sync()` action for which to generate the
 
414
        the serialized representation of this model.
 
415
    @return {String} serialized HTTP request entity body.
 
416
    @since 3.6.0
 
417
    **/
 
418
    serialize: function (action) {
 
419
        return Y.JSON.stringify(this);
 
420
    },
 
421
 
 
422
    /**
 
423
    Communicates with a RESTful HTTP server by sending and receiving data via
 
424
    XHRs. This method is called internally by load(), save(), and destroy().
 
425
 
 
426
    The URL used for each XHR will be retrieved by calling the `getURL()` method
 
427
    and passing it the specified `action` and `options`.
 
428
 
 
429
    This method relies heavily on standard RESTful HTTP conventions
 
430
 
 
431
    @method sync
 
432
    @param {String} action Sync action to perform. May be one of the following:
 
433
 
 
434
      * `create`: Store a newly-created model for the first time.
 
435
      * `delete`: Delete an existing model.
 
436
      * `read`  : Load an existing model.
 
437
      * `update`: Update an existing model.
 
438
 
 
439
    @param {Object} [options] Sync options:
 
440
      @param {String} [options.csrfToken] The authenticity token used by the
 
441
        server to verify the validity of this request and protected against CSRF
 
442
        attacks. This overrides the default value provided by the static
 
443
        `CSRF_TOKEN` property.
 
444
      @param {Object} [options.headers] The HTTP headers to mix with the default
 
445
        headers specified by the static `HTTP_HEADERS` property.
 
446
      @param {Number} [options.timeout] The number of milliseconds before the
 
447
        request will timeout and be aborted. This overrides the default provided
 
448
        by the static `HTTP_TIMEOUT` property.
 
449
    @param {Function} [callback] Called when the sync operation finishes.
 
450
      @param {Error|null} callback.err If an error occurred, this parameter will
 
451
        contain the error. If the sync operation succeeded, _err_ will be
 
452
        falsy.
 
453
      @param {Any} [callback.response] The server's response.
 
454
    **/
 
455
    sync: function (action, options, callback) {
 
456
        options || (options = {});
 
457
 
 
458
        var url       = this.getURL(action, options),
 
459
            method    = RESTSync.HTTP_METHODS[action],
 
460
            headers   = Y.merge(RESTSync.HTTP_HEADERS, options.headers),
 
461
            timeout   = options.timeout || RESTSync.HTTP_TIMEOUT,
 
462
            csrfToken = options.csrfToken || RESTSync.CSRF_TOKEN,
 
463
            entity;
 
464
 
 
465
        // Prepare the content if we are sending data to the server.
 
466
        if (method === 'POST' || method === 'PUT') {
 
467
            entity = this.serialize(action);
 
468
        } else {
 
469
            // Remove header, no content is being sent.
 
470
            delete headers['Content-Type'];
 
471
        }
 
472
 
 
473
        // Setup HTTP emulation for older servers if we need it.
 
474
        if (RESTSync.EMULATE_HTTP &&
 
475
                (method === 'PUT' || method === 'DELETE')) {
 
476
 
 
477
            // Pass along original method type in the headers.
 
478
            headers['X-HTTP-Method-Override'] = method;
 
479
 
 
480
            // Fall-back to using POST method type.
 
481
            method = 'POST';
 
482
        }
 
483
 
 
484
        // Add CSRF token to HTTP request headers if one is specified and the
 
485
        // request will cause side effects on the server.
 
486
        if (csrfToken &&
 
487
                (method === 'POST' || method === 'PUT' || method === 'DELETE')) {
 
488
 
 
489
            headers['X-CSRF-Token'] = csrfToken;
 
490
        }
 
491
 
 
492
        this._sendSyncIORequest({
 
493
            action  : action,
 
494
            callback: callback,
 
495
            entity  : entity,
 
496
            headers : headers,
 
497
            method  : method,
 
498
            timeout : timeout,
 
499
            url     : url
 
500
        });
 
501
    },
 
502
 
 
503
    // -- Protected Methods ----------------------------------------------------
 
504
 
 
505
    /**
 
506
    Joins the `root` URL to the specified `url`, normalizing leading/trailing
 
507
    "/" characters.
 
508
 
 
509
    @example
 
510
        model.root = '/foo'
 
511
        model._joinURL('bar');  // => '/foo/bar'
 
512
        model._joinURL('/bar'); // => '/foo/bar'
 
513
 
 
514
        model.root = '/foo/'
 
515
        model._joinURL('bar');  // => '/foo/bar/'
 
516
        model._joinURL('/bar'); // => '/foo/bar/'
 
517
 
 
518
    @method _joinURL
 
519
    @param {String} url URL to append to the `root` URL.
 
520
    @return {String} Joined URL.
 
521
    @protected
 
522
    @since 3.6.0
 
523
    **/
 
524
    _joinURL: function (url) {
 
525
        var root = this.root;
 
526
 
 
527
        if (!(root || url)) {
 
528
            return '';
 
529
        }
 
530
 
 
531
        if (url.charAt(0) === '/') {
 
532
            url = url.substring(1);
 
533
        }
 
534
 
 
535
        // Combines the `root` with the `url` and adds a trailing-slash if the
 
536
        // `root` has a trailing-slash.
 
537
        return root && root.charAt(root.length - 1) === '/' ?
 
538
                root + url + '/' :
 
539
                root + '/' + url;
 
540
    },
 
541
 
 
542
 
 
543
    /**
 
544
    Calls both public, overrideable methods: `parseIOResponse()`, then `parse()`
 
545
    and returns the result.
 
546
 
 
547
    This will call into `parseIOResponse()`, if it's defined as a method,
 
548
    passing it the full response object from the XHR and using its return value
 
549
    to pass along to the `parse()`. This enables developers to easily parse data
 
550
    out of the response headers which should be used by the `parse()` method.
 
551
 
 
552
    @method _parse
 
553
    @param {Object} response Response object from `Y.io()`.
 
554
    @return {Object|Object[]} Attribute hash or Array of model attribute hashes.
 
555
    @protected
 
556
    @since 3.7.0
 
557
    **/
 
558
    _parse: function (response) {
 
559
        // When `parseIOResponse` is defined as a method, it will be invoked and
 
560
        // the result will become the new response object that the `parse()`
 
561
        // will be invoked with.
 
562
        if (typeof this.parseIOResponse === 'function') {
 
563
            response = this.parseIOResponse(response);
 
564
        }
 
565
 
 
566
        return this.parse(response);
 
567
    },
 
568
 
 
569
    /**
 
570
    Performs the XHR and returns the resulting `Y.io()` request object.
 
571
 
 
572
    This method is called by `sync()`.
 
573
 
 
574
    @method _sendSyncIORequest
 
575
    @param {Object} config An object with the following properties:
 
576
      @param {String} config.action The `sync()` action being performed.
 
577
      @param {Function} [config.callback] Called when the sync operation
 
578
        finishes.
 
579
      @param {String} [config.entity] The HTTP request entity body.
 
580
      @param {Object} config.headers The HTTP request headers.
 
581
      @param {String} config.method The HTTP request method.
 
582
      @param {Number} [config.timeout] Time until the HTTP request is aborted.
 
583
      @param {String} config.url The URL of the HTTP resource.
 
584
    @return {Object} The resulting `Y.io()` request object.
 
585
    @protected
 
586
    @since 3.6.0
 
587
    **/
 
588
    _sendSyncIORequest: function (config) {
 
589
        return Y.io(config.url, {
 
590
            'arguments': {
 
591
                action  : config.action,
 
592
                callback: config.callback,
 
593
                url     : config.url
 
594
            },
 
595
 
 
596
            context: this,
 
597
            data   : config.entity,
 
598
            headers: config.headers,
 
599
            method : config.method,
 
600
            timeout: config.timeout,
 
601
 
 
602
            on: {
 
603
                start  : this._onSyncIOStart,
 
604
                failure: this._onSyncIOFailure,
 
605
                success: this._onSyncIOSuccess,
 
606
                end    : this._onSyncIOEnd
 
607
            }
 
608
        });
 
609
    },
 
610
 
 
611
    /**
 
612
    Utility which takes a tokenized `url` string and substitutes its
 
613
    placeholders using a specified `data` object.
 
614
 
 
615
    This method will property URL-encode any values before substituting them.
 
616
    Also, only expect it to work with String and Number values.
 
617
 
 
618
    @example
 
619
        var url = this._substituteURL('/users/{name}', {id: 'Eric F'});
 
620
        // => "/users/Eric%20F"
 
621
 
 
622
    @method _substituteURL
 
623
    @param {String} url Tokenized URL string to substitute placeholder values.
 
624
    @param {Object} data Set of data to fill in the `url`'s placeholders.
 
625
    @return {String} Substituted URL.
 
626
    @protected
 
627
    @since 3.6.0
 
628
    **/
 
629
    _substituteURL: function (url, data) {
 
630
        if (!url) {
 
631
            return '';
 
632
        }
 
633
 
 
634
        var values = {};
 
635
 
 
636
        // Creates a hash of the string and number values only to be used to
 
637
        // replace any placeholders in a tokenized `url`.
 
638
        Y.Object.each(data, function (v, k) {
 
639
            if (Lang.isString(v) || Lang.isNumber(v)) {
 
640
                // URL-encode any string or number values.
 
641
                values[k] = encodeURIComponent(v);
 
642
            }
 
643
        });
 
644
 
 
645
        return Lang.sub(url, values);
 
646
    },
 
647
 
 
648
    // -- Event Handlers -------------------------------------------------------
 
649
 
 
650
    /**
 
651
    Called when the `Y.io` request has finished, after "success" or "failure"
 
652
    has been determined.
 
653
 
 
654
    This is a no-op by default, but provides a hook for overriding.
 
655
 
 
656
    @method _onSyncIOEnd
 
657
    @param {String} txId The `Y.io` transaction id.
 
658
    @param {Object} details Extra details carried through from `sync()`:
 
659
      @param {String} details.action The sync action performed.
 
660
      @param {Function} [details.callback] The function to call after syncing.
 
661
      @param {String} details.url The URL of the requested resource.
 
662
    @protected
 
663
    @since 3.6.0
 
664
    **/
 
665
    _onSyncIOEnd: function (txId, details) {},
 
666
 
 
667
    /**
 
668
    Called when the `Y.io` request has finished unsuccessfully.
 
669
 
 
670
    By default this calls the `details.callback` function passing it the HTTP
 
671
    status code and message as an error object along with the response body.
 
672
 
 
673
    @method _onSyncIOFailure
 
674
    @param {String} txId The `Y.io` transaction id.
 
675
    @param {Object} res The `Y.io` response object.
 
676
    @param {Object} details Extra details carried through from `sync()`:
 
677
      @param {String} details.action The sync action performed.
 
678
      @param {Function} [details.callback] The function to call after syncing.
 
679
      @param {String} details.url The URL of the requested resource.
 
680
    @protected
 
681
    @since 3.6.0
 
682
    **/
 
683
    _onSyncIOFailure: function (txId, res, details) {
 
684
        var callback = details.callback;
 
685
 
 
686
        if (callback) {
 
687
            callback({
 
688
                code: res.status,
 
689
                msg : res.statusText
 
690
            }, res);
 
691
        }
 
692
    },
 
693
 
 
694
    /**
 
695
    Called when the `Y.io` request has finished successfully.
 
696
 
 
697
    By default this calls the `details.callback` function passing it the
 
698
    response body.
 
699
 
 
700
    @method _onSyncIOSuccess
 
701
    @param {String} txId The `Y.io` transaction id.
 
702
    @param {Object} res The `Y.io` response object.
 
703
    @param {Object} details Extra details carried through from `sync()`:
 
704
      @param {String} details.action The sync action performed.
 
705
      @param {Function} [details.callback] The function to call after syncing.
 
706
      @param {String} details.url The URL of the requested resource.
 
707
    @protected
 
708
    @since 3.6.0
 
709
    **/
 
710
    _onSyncIOSuccess: function (txId, res, details) {
 
711
        var callback = details.callback;
 
712
 
 
713
        if (callback) {
 
714
            callback(null, res);
 
715
        }
 
716
    },
 
717
 
 
718
    /**
 
719
    Called when the `Y.io` request is made.
 
720
 
 
721
    This is a no-op by default, but provides a hook for overriding.
 
722
 
 
723
    @method _onSyncIOStart
 
724
    @param {String} txId The `Y.io` transaction id.
 
725
    @param {Object} details Extra details carried through from `sync()`:
 
726
      @param {String} detials.action The sync action performed.
 
727
      @param {Function} [details.callback] The function to call after syncing.
 
728
      @param {String} details.url The URL of the requested resource.
 
729
    @protected
 
730
    @since 3.6.0
 
731
    **/
 
732
    _onSyncIOStart: function (txId, details) {}
 
733
};
 
734
 
 
735
// -- Namespace ----------------------------------------------------------------
 
736
 
 
737
Y.namespace('ModelSync').REST = RESTSync;
 
738
 
 
739
 
 
740
}, '3.10.3', {"requires": ["model", "io-base", "json-stringify"]});