Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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