Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('model-sync-local', function (Y, NAME) {
2
 
3
/*
4
An extension which provides a sync implementation through locally stored
5
key value pairs, either through the HTML localStorage API or falling back
6
onto an in-memory cache, that can be mixed into a Model or ModelList subclass.
7
 
8
@module app
9
@submodule model-sync-local
10
@since 3.13.0
11
**/
12
 
13
/**
14
An extension which provides a sync implementation through locally stored
15
key value pairs, either through the HTML localStorage API or falling back
16
onto an in-memory cache, that can be mixed into a Model or ModelList subclass.
17
 
18
A group of Models/ModelLists is serialized in localStorage by either its
19
class name, or a specified 'root' that is provided.
20
 
21
    var User = Y.Base.create('user', Y.Model, [Y.ModelSync.Local], {
22
        root: 'user'
23
    });
24
 
25
    var Users = Y.Base.create('users', Y.ModelList, [Y.ModelSync.Local], {
26
        model: User,
27
    });
28
 
29
@class ModelSync.Local
30
@extensionfor Model
31
@extensionfor ModelList
32
@since 3.13.0
33
**/
34
function LocalSync() {}
35
 
36
/**
37
Properties that shouldn't be turned into ad-hoc attributes when passed to a
38
Model or ModelList constructor.
39
 
40
@property _NON_ATTRS_CFG
41
@type Array
42
@default ['root']
43
@static
44
@protected
45
@since 3.13.0
46
**/
47
LocalSync._NON_ATTRS_CFG = ['root'];
48
 
49
/**
50
Feature testing for `localStorage` availability.
51
Will return falsey for browsers with `localStorage`, but that don't
52
actually work, such as iOS Safari in private browsing mode.
53
 
54
@property _hasLocalStorage
55
@type Boolean
56
@private
57
**/
58
LocalSync._hasLocalStorage = (function () {
59
    var LS   = Y.config.win.localStorage,
60
        test = Y.guid();
61
 
62
    try {
63
        LS.setItem(test, test);
64
        LS.removeItem(test);
65
        return true;
66
    } catch (e) {
67
        return false;
68
    }
69
})(),
70
 
71
/**
72
Object of key/value pairs to fall back on when localStorage is not available.
73
 
74
@property _data
75
@type Object
76
@private
77
**/
78
 
79
LocalSync._data = LocalSync._data || {};
80
 
81
/**
82
Cache to quickly access a specific object with a given ID.
83
 
84
@property _store
85
@type Array
86
@private
87
**/
88
 
89
LocalSync._store = LocalSync._store || {};
90
 
91
LocalSync.prototype = {
92
 
93
    // -- Public Methods -------------------------------------------------------
94
 
95
    /**
96
    Root used as the key inside of localStorage and/or the in-memory store.
97
 
98
    @property root
99
    @type String
100
    @default ""
101
    @since 3.13.0
102
    **/
103
    root: '',
104
 
105
    /**
106
    Shortcut for access to localStorage.
107
 
108
    @property storage
109
    @type Storage
110
    @default null
111
    @since 3.13.0
112
    **/
113
    storage: null,
114
 
115
    // -- Lifecycle Methods -----------------------------------------------------
116
    initializer: function (config) {
117
        var store, data;
118
 
119
        config || (config = {});
120
 
121
        if ('root' in config) {
122
            this.root = config.root || '';
123
        }
124
 
125
        // This is checking to see if the sync layer is being applied to
126
        // a ModelList, and if so, is looking for a `root` property on its
127
        // Model's prototype instead.
128
        if (!this.root && this.model && this.model.prototype.root) {
129
            this.root = this.model.prototype.root;
130
        }
131
 
132
        if (LocalSync._hasLocalStorage) {
133
            this.storage = Y.config.win.localStorage;
134
            store = this.storage.getItem(this.root);
135
        } else {
136
            Y.log("Could not access localStorage.", "warn");
137
        }
138
 
139
        // Pull in existing data from localStorage, if possible.
140
        // Otherwise, see if there's existing data on the local cache.
141
        if (store) {
142
            LocalSync._store[this.root] = store.split('|') || [];
143
 
144
            Y.Array.each(LocalSync._store[this.root], function (id) {
145
                LocalSync._data[id] = Y.JSON.parse(this.storage.getItem(id));
146
            }, this);
147
        } else {
148
            LocalSync._store[this.root] || (LocalSync._store[this.root] = []);
149
        }
150
    },
151
 
152
    // -- Public Methods -----------------------------------------------------------
153
 
154
    /**
155
    Creates a synchronization layer with the localStorage API, if available.
156
    Otherwise, falls back to a in-memory data store.
157
 
158
    This method is called internally by load(), save(), and destroy().
159
 
160
    @method sync
161
    @param {String} action Sync action to perform. May be one of the following:
162
 
163
      * **create**: Store a newly-created model for the first time.
164
      * **read**  : Load an existing model.
165
      * **update**: Update an existing model.
166
      * **delete**: Delete an existing model.
167
 
168
    @param {Object} [options] Sync options
169
    @param {Function} [callback] Called when the sync operation finishes.
170
      @param {Error|null} callback.err If an error occurred, this parameter will
171
        contain the error. If the sync operation succeeded, _err_ will be
172
        falsey.
173
      @param {Any} [callback.response] The response from our sync. This value will
174
        be passed to the parse() method, which is expected to parse it and
175
        return an attribute hash.
176
    **/
177
    sync: function (action, options, callback) {
178
        options || (options = {});
179
        var response, errorInfo;
180
 
181
        try {
182
            switch (action) {
183
                case 'read':
184
                    if (this._isYUIModelList) {
185
                        response = this._index(options);
186
                    } else {
187
                        response = this._show(options);
188
                    }
189
                    break;
190
                case 'create':
191
                    response = this._create(options);
192
                    break;
193
                case 'update':
194
                    response = this._update(options);
195
                    break;
196
                case 'delete':
197
                    response = this._destroy(options);
198
                    break;
199
            }
200
        } catch (error) {
201
            errorInfo = error.message;
202
        }
203
 
204
        if (response) {
205
            callback(null, response);
206
        } else if (errorInfo) {
207
            callback(errorInfo);
208
        } else {
209
            callback("Data not found in LocalStorage");
210
        }
211
    },
212
 
213
    /**
214
    Generate a random GUID for our Models. This can be overriden if you have
215
    another method of generating different IDs.
216
 
217
    @method generateID
218
    @protected
219
    @param {String} pre Optional GUID prefix
220
    **/
221
    generateID: function (pre) {
222
        return Y.guid(pre + '_');
223
    },
224
 
225
    // -- Protected Methods ----------------------------------------------------
226
 
227
    /**
228
    Sync method correlating to the "read" operation, for a Model List
229
 
230
    @method _index
231
    @return {Object[]} Array of objects found for that root key
232
    @protected
233
    @since 3.13.0
234
    **/
235
    _index: function () {
236
        var store = LocalSync._store[this.root],
237
            data  = Y.Array.map(store, function (id) {
238
                return LocalSync._data[id];
239
            });
240
 
241
        return data;
242
    },
243
 
244
    /**
245
    Sync method correlating to the "read" operation, for a Model
246
 
247
    @method _show
248
    @return {Object} Object found for that root key and model ID
249
    @protected
250
    @since 3.13.0
251
    **/
252
    _show: function () {
253
        return LocalSync._data[this.get('id')] || null;
254
    },
255
 
256
    /**
257
    Sync method correlating to the "create" operation
258
 
259
    @method _show
260
    @return {Object} The new object created.
261
    @protected
262
    @since 3.13.0
263
    **/
264
    _create: function () {
265
        var hash  = this.toJSON();
266
 
267
        hash.id = this.generateID(this.root);
268
 
269
        LocalSync._data[hash.id] = hash;
270
        if (this.storage) {
271
            this.storage.setItem(hash.id, Y.JSON.stringify(hash));
272
        }
273
 
274
        LocalSync._store[this.root].push(hash.id);
275
 
276
        this._save();
277
        return hash;
278
    },
279
 
280
    /**
281
    Sync method correlating to the "update" operation
282
 
283
    @method _update
284
    @return {Object} The updated object.
285
    @protected
286
    @since 3.13.0
287
    **/
288
    _update: function () {
289
        var hash = this.toJSON(),
290
            id = this.get('id');
291
 
292
        LocalSync._data[id] = hash;
293
 
294
        if (this.storage) {
295
            this.storage.setItem(id, hash);
296
        }
297
 
298
        if (Y.Array.indexOf(LocalSync._store[this.root], id) === -1) {
299
            LocalSync._store[this.root].push(id);
300
        }
301
 
302
        this._save();
303
 
304
        return hash;
305
    },
306
 
307
    /**
308
    Sync method correlating to the "delete" operation.  Deletes the data
309
    from the in-memory object, and saves into localStorage if available.
310
 
311
    @method _destroy
312
    @protected
313
    @since 3.13.0
314
    **/
315
    _destroy: function () {
316
        var id = this.get('id'),
317
            storage = this.storage;
318
 
319
        if (!LocalSync._data[id]) {
320
            return;
321
        }
322
 
323
        delete LocalSync._data[id];
324
 
325
        if (storage) {
326
            storage.removeItem(id);
327
        }
328
 
329
        LocalSync._store[this.root] = Y.Array.filter(LocalSync._store[this.root], function (item) {
330
            return item.id != id;
331
        });
332
 
333
        this._save();
334
        return this.toJSON();
335
    },
336
 
337
    /**
338
    Saves the current in-memory store into a localStorage key/value pair
339
    if localStorage is available; otherwise, does nothing.
340
 
341
    @method _save
342
    @protected
343
    @since 3.13.0
344
    **/
345
    _save: function () {
346
        if (LocalSync._hasLocalStorage && this.storage) {
347
            this.storage.setItem(
348
                this.root,
349
                LocalSync._store[this.root].join('|')
350
            );
351
        }
352
    }
353
};
354
 
355
// -- Namespace ---------------------------------------------------------------
356
 
357
Y.namespace('ModelSync').Local = LocalSync;
358
 
359
 
360
}, '3.18.1', {"requires": ["model", "json-stringify"]});