source: trunk/admin/themes/default/js/LocalStorageCache.js @ 29613

Last change on this file since 29613 was 29613, checked in by mistic100, 10 years ago

dynamically set selectize maxOptions to accommodate very long lists

File size: 11.1 KB
Line 
1(function($, exports) {
2  "use strict";
3 
4  /**
5   * Base LocalStorage cache
6   *
7   * @param options {object}
8   *    - key (required) identifier of the collection
9   *    - serverId (recommended) identifier of the Piwigo instance
10   *    - serverKey (required) state of collection server-side
11   *    - lifetime (optional) cache lifetime in seconds
12   *    - loader (required) function called to fetch data, takes a callback as first argument
13   *        which must be called with the loaded date
14   */
15  var LocalStorageCache = function(options) {
16    this._init(options);
17  };
18
19  /*
20   * Constructor (deported for easy inheritance)
21   */
22  LocalStorageCache.prototype._init = function(options) {
23    this.key = options.key + '_' + options.serverId;
24    this.serverKey = options.serverKey;
25    this.lifetime = options.lifetime ? options.lifetime*1000 : 3600*1000;
26    this.loader = options.loader;
27   
28    this.storage = window.localStorage;
29    this.ready = !!this.storage;
30  };
31
32  /*
33   * Get the cache content
34   * @param callback {function} called with the data as first parameter
35   */
36  LocalStorageCache.prototype.get = function(callback) {
37    var now = new Date().getTime(),
38        that = this;
39   
40    if (this.ready && this.storage[this.key] != undefined) {
41      var cache = JSON.parse(this.storage[this.key]);
42     
43      if (now - cache.timestamp <= this.lifetime && cache.key == this.serverKey) {
44        callback(cache.data);
45        return;
46      }
47    }
48   
49    this.loader(function(data) {
50      that.set.call(that, data);
51      callback(data);
52    });
53  };
54
55  /*
56   * Manually set the cache content
57   * @param data {mixed}
58   */
59  LocalStorageCache.prototype.set = function(data) {
60    if (this.ready) {
61      this.storage[this.key] = JSON.stringify({
62        timestamp: new Date().getTime(),
63        key: this.serverKey,
64        data: data
65      });
66    }
67  };
68
69  /*
70   * Manually clear the cache
71   */
72  LocalStorageCache.prototype.clear = function() {
73    if (this.ready) {
74      this.storage.removeItem(this.key);
75    }
76  };
77
78 
79  /**
80   * Abstract class containing common initialization code for selectize
81   */
82  var AbstractSelectizer = function(){};
83  AbstractSelectizer.prototype = new LocalStorageCache({});
84
85  /*
86   * Load Selectize with cache content
87   * @param $target {jQuery} may have some data attributes (create, default, value)
88   * @param options {object}
89   *    - value (optional) list of preselected items (ids, or objects with "id" attribute")
90   *    - default (optional) default value which will be forced if the select is emptyed
91   *    - create (optional) allow item user creation
92   *    - filter (optional) function called for each select before applying the data
93   *      takes two parameters: cache data, options
94   *      must return new data
95   */
96  AbstractSelectizer.prototype._selectize = function($target, globalOptions) {
97    this.get(function(data) {
98      $target.each(function() {
99        var filtered, value, defaultValue,
100            options = $.extend({}, globalOptions);
101       
102        // apply filter function
103        if (options.filter != undefined) {
104          filtered = options.filter.call(this, data, options);
105        }
106        else {
107          filtered = data;
108        }
109       
110        this.selectize.settings.maxOptions = filtered.length + 100;
111
112        // active creation mode
113        if (this.hasAttribute('data-create')) {
114          options.create = true;
115        }
116        this.selectize.settings.create = !!options.create;
117
118        // load options
119        this.selectize.load(function(callback) {
120          if ($.isEmptyObject(this.options)) {
121            callback(filtered);
122          }
123        });
124
125        // load items
126        if ((value = $(this).data('value'))) {
127          options.value = value;
128        }
129        if (options.value != undefined) {
130          $.each(value, $.proxy(function(i, cat) {
131            if ($.isNumeric(cat))
132              this.selectize.addItem(cat);
133            else
134              this.selectize.addItem(cat.id);
135          }, this));
136        }
137       
138        // set default
139        if ((defaultValue = $(this).data('default'))) {
140          options.default = defaultValue;
141        }
142        if (options.default == 'first') {
143          options.default = filtered[0] ? filtered[0].id : undefined;
144        }
145       
146        if (options.default != undefined) {
147          // add default item
148          if (this.selectize.getValue() == '') {
149            this.selectize.addItem(options.default);
150          }
151
152          // if multiple: prevent item deletion
153          if (this.multiple) {
154            this.selectize.getItem(options.default).find('.remove').hide();
155           
156            this.selectize.on('item_remove', function(id) {
157              if (id == options.default) {
158                this.addItem(id);
159                this.getItem(id).find('.remove').hide();
160              }
161            });
162          }
163          // if single: restore default on blur
164          else {
165            this.selectize.on('dropdown_close', function() {
166              if (this.getValue() == '') {
167                this.addItem(options.default);
168              }
169            });
170          }
171        }
172      });
173    });
174  };
175 
176  // redefine Selectize templates without escape
177  AbstractSelectizer.getRender = function(field_label, lang) {
178    lang = lang || { 'Add': 'Add' };
179
180        return {
181      'option': function(data, escape) {
182        return '<div class="option">' + data[field_label] + '</div>';
183      },
184      'item': function(data, escape) {
185        return '<div class="item">' + data[field_label] + '</div>';
186      },
187      'option_create': function(data, escape) {
188        return '<div class="create">' + lang['Add'] + ' <strong>' + data.input + '</strong>&hellip;</div>';
189      }
190    };
191  };
192
193
194  /**
195   * Special LocalStorage for admin categories list
196   *
197   * @param options {object}
198   *    - serverId (recommended) identifier of the Piwigo instance
199   *    - serverKey (required) state of collection server-side
200   *    - rootUrl (required) used for WS call
201   */
202  var CategoriesCache = function(options) {
203    options.key = 'categoriesAdminList';
204   
205    options.loader = function(callback) {
206      $.getJSON(options.rootUrl + 'ws.php?format=json&method=pwg.categories.getAdminList', function(data) {
207        var cats = data.result.categories.map(function(c, i) {
208          c.pos = i;
209          delete c['comment'];
210          delete c['uppercats'];
211          return c;
212        });
213
214        callback(cats);
215      });
216    };
217   
218    this._init(options);
219  };
220
221  CategoriesCache.prototype = new AbstractSelectizer();
222
223  /*
224   * Init Selectize with cache content
225   * @see AbstractSelectizer._selectize
226   */
227  CategoriesCache.prototype.selectize = function($target, options) {
228    options = options || {};
229
230    $target.selectize({
231      valueField: 'id',
232      labelField: 'fullname',
233      sortField: 'pos',
234      searchField: ['fullname'],
235      plugins: ['remove_button'],
236      render: AbstractSelectizer.getRender('fullname', options.lang)
237    });
238   
239    this._selectize($target, options);
240  };
241
242
243  /**
244   * Special LocalStorage for admin tags list
245   *
246   * @param options {object}
247   *    - serverId (recommended) identifier of the Piwigo instance
248   *    - serverKey (required) state of collection server-side
249   *    - rootUrl (required) used for WS call
250   */
251  var TagsCache = function(options) {
252    options.key = 'tagsAdminList';
253   
254    options.loader = function(callback) {
255      $.getJSON(options.rootUrl + 'ws.php?format=json&method=pwg.tags.getAdminList', function(data) {
256        var tags = data.result.tags.map(function(t) {
257          t.id = '~~' + t.id + '~~';
258          delete t['url_name'];
259          delete t['lastmodified'];
260          return t;
261        });
262
263        callback(tags);
264      });
265    };
266   
267    this._init(options);
268  };
269
270  TagsCache.prototype = new AbstractSelectizer();
271
272  /*
273   * Init Selectize with cache content
274   * @see AbstractSelectizer._selectize
275   */
276  TagsCache.prototype.selectize = function($target, options) {
277    options = options || {};
278
279    $target.selectize({
280      valueField: 'id',
281      labelField: 'name',
282      sortField: 'name',
283      searchField: ['name'],
284      plugins: ['remove_button'],
285      render: AbstractSelectizer.getRender('name', options.lang)
286    });
287   
288    this._selectize($target, options);
289  };
290 
291 
292  /**
293   * Special LocalStorage for admin groups list
294   *
295   * @param options {object}
296   *    - serverId (recommended) identifier of the Piwigo instance
297   *    - serverKey (required) state of collection server-side
298   *    - rootUrl (required) used for WS call
299   */
300  var GroupsCache = function(options) {
301    options.key = 'groupsAdminList';
302   
303    options.loader = function(callback) {
304      $.getJSON(options.rootUrl + 'ws.php?format=json&method=pwg.groups.getList&per_page=9999', function(data) {
305        var groups = data.result.groups.map(function(g) {
306          delete g['lastmodified'];
307          return g;
308        });
309
310        callback(groups);
311      });
312    };
313   
314    this._init(options);
315  };
316
317  GroupsCache.prototype = new AbstractSelectizer();
318
319  /*
320   * Init Selectize with cache content
321   * @see AbstractSelectizer._selectize
322   */
323  GroupsCache.prototype.selectize = function($target, options) {
324    options = options || {};
325
326    $target.selectize({
327      valueField: 'id',
328      labelField: 'name',
329      sortField: 'name',
330      searchField: ['name'],
331      plugins: ['remove_button'],
332      render: AbstractSelectizer.getRender('name', options.lang)
333    });
334   
335    this._selectize($target, options);
336  };
337 
338 
339  /**
340   * Special LocalStorage for admin users list
341   *
342   * @param options {object}
343   *    - serverId (recommended) identifier of the Piwigo instance
344   *    - serverKey (required) state of collection server-side
345   *    - rootUrl (required) used for WS call
346   */
347  var UsersCache = function(options) {
348    options.key = 'usersAdminList';
349   
350    options.loader = function(callback) {
351      var users = [];
352     
353      // recursive loader
354      (function load(page){
355        jQuery.getJSON(options.rootUrl + 'ws.php?format=json&method=pwg.users.getList&display=username&per_page=9999&page='+ page, function(data) {
356          users = users.concat(data.result.users);
357         
358          if (data.result.paging.count == data.result.paging.per_page) {
359            load(++page);
360          }
361          else {
362            callback(users);
363          }
364        });
365      }(0));
366    };
367   
368    this._init(options);
369  };
370
371  UsersCache.prototype = new AbstractSelectizer();
372
373  /*
374   * Init Selectize with cache content
375   * @see AbstractSelectizer._selectize
376   */
377  UsersCache.prototype.selectize = function($target, options) {
378    options = options || {};
379
380    $target.selectize({
381      valueField: 'id',
382      labelField: 'username',
383      sortField: 'username',
384      searchField: ['username'],
385      plugins: ['remove_button'],
386      render: AbstractSelectizer.getRender('username', options.lang)
387    });
388   
389    this._selectize($target, options);
390  };
391 
392 
393  /**
394   * Expose classes in global scope
395   */
396  exports.LocalStorageCache = LocalStorageCache;
397  exports.CategoriesCache = CategoriesCache;
398  exports.TagsCache = TagsCache;
399  exports.GroupsCache = GroupsCache;
400  exports.UsersCache = UsersCache;
401 
402}(jQuery, window));
Note: See TracBrowser for help on using the repository browser.