Built motion from commit 7e022ab.|2.0.15
[motion2.git] / public / bower_components / angular-translate-loader-partial / angular-translate-loader-partial.js
1 /*!
2  * angular-translate - v2.11.0 - 2016-03-20
3  * 
4  * Copyright (c) 2016 The angular-translate team, Pascal Precht; Licensed MIT
5  */
6 (function (root, factory) {
7   if (typeof define === 'function' && define.amd) {
8     // AMD. Register as an anonymous module unless amdModuleId is set
9     define([], function () {
10       return (factory());
11     });
12   } else if (typeof exports === 'object') {
13     // Node. Does not work with strict CommonJS, but
14     // only CommonJS-like environments that support module.exports,
15     // like Node.
16     module.exports = factory();
17   } else {
18     factory();
19   }
20 }(this, function () {
21
22 angular.module('pascalprecht.translate')
23 /**
24  * @ngdoc object
25  * @name pascalprecht.translate.$translatePartialLoaderProvider
26  *
27  * @description
28  * By using a $translatePartialLoaderProvider you can configure a list of a needed
29  * translation parts directly during the configuration phase of your application's
30  * lifetime. All parts you add by using this provider would be loaded by
31  * angular-translate at the startup as soon as possible.
32  */
33   .provider('$translatePartialLoader', $translatePartialLoader);
34
35 function $translatePartialLoader() {
36
37   'use strict';
38
39   /**
40    * @constructor
41    * @name Part
42    *
43    * @description
44    * Represents Part object to add and set parts at runtime.
45    */
46   function Part(name, priority) {
47     this.name = name;
48     this.isActive = true;
49     this.tables = {};
50     this.priority = priority || 0;
51   }
52
53   /**
54    * @name parseUrl
55    * @method
56    *
57    * @description
58    * Returns a parsed url template string and replaces given target lang
59    * and part name it.
60    *
61    * @param {string|function} urlTemplate - Either a string containing an url pattern (with
62    *                                        '{part}' and '{lang}') or a function(part, lang)
63    *                                        returning a string.
64    * @param {string} targetLang - Language key for language to be used.
65    * @return {string} Parsed url template string
66    */
67   Part.prototype.parseUrl = function(urlTemplate, targetLang) {
68     if (angular.isFunction(urlTemplate)) {
69       return urlTemplate(this.name, targetLang);
70     }
71     return urlTemplate.replace(/\{part\}/g, this.name).replace(/\{lang\}/g, targetLang);
72   };
73
74   Part.prototype.getTable = function(lang, $q, $http, $httpOptions, urlTemplate, errorHandler) {
75
76     if (!this.tables[lang]) {
77       var self = this;
78
79       return $http(angular.extend({
80         method : 'GET',
81         url: this.parseUrl(urlTemplate, lang)
82       }, $httpOptions))
83         .then(function(result){
84           self.tables[lang] = result.data;
85           return result.data;
86         }, function() {
87           if (errorHandler) {
88             return errorHandler(self.name, lang)
89               .then(function(data) {
90                 self.tables[lang] = data;
91                 return data;
92               }, function() {
93                 return $q.reject(self.name);
94               });
95           } else {
96             return $q.reject(self.name);
97           }
98         });
99
100     } else {
101       return $q.when(this.tables[lang]);
102     }
103   };
104
105   var parts = {};
106
107   function hasPart(name) {
108     return Object.prototype.hasOwnProperty.call(parts, name);
109   }
110
111   function isStringValid(str) {
112     return angular.isString(str) && str !== '';
113   }
114
115   function isPartAvailable(name) {
116     if (!isStringValid(name)) {
117       throw new TypeError('Invalid type of a first argument, a non-empty string expected.');
118     }
119
120     return (hasPart(name) && parts[name].isActive);
121   }
122
123   function deepExtend(dst, src) {
124     for (var property in src) {
125       if (src[property] && src[property].constructor &&
126        src[property].constructor === Object) {
127         dst[property] = dst[property] || {};
128         deepExtend(dst[property], src[property]);
129       } else {
130         dst[property] = src[property];
131       }
132     }
133     return dst;
134   }
135
136   function getPrioritizedParts() {
137     var prioritizedParts = [];
138     for(var part in parts) {
139       if (parts[part].isActive) {
140         prioritizedParts.push(parts[part]);
141       }
142     }
143     prioritizedParts.sort(function (a, b) {
144       return a.priority - b.priority;
145     });
146     return prioritizedParts;
147   }
148
149
150   /**
151    * @ngdoc function
152    * @name pascalprecht.translate.$translatePartialLoaderProvider#addPart
153    * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
154    *
155    * @description
156    * Registers a new part of the translation table to be loaded once the
157    * `angular-translate` gets into runtime phase. It does not actually load any
158    * translation data, but only registers a part to be loaded in the future.
159    *
160    * @param {string} name A name of the part to add
161    * @param {int} [priority=0] Sets the load priority of this part.
162    *
163    * @returns {object} $translatePartialLoaderProvider, so this method is chainable
164    * @throws {TypeError} The method could throw a **TypeError** if you pass the param
165    * of the wrong type. Please, note that the `name` param has to be a
166    * non-empty **string**.
167    */
168   this.addPart = function(name, priority) {
169     if (!isStringValid(name)) {
170       throw new TypeError('Couldn\'t add part, part name has to be a string!');
171     }
172
173     if (!hasPart(name)) {
174       parts[name] = new Part(name, priority);
175     }
176     parts[name].isActive = true;
177
178     return this;
179   };
180
181   /**
182    * @ngdocs function
183    * @name pascalprecht.translate.$translatePartialLoaderProvider#setPart
184    * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
185    *
186    * @description
187    * Sets a translation table to the specified part. This method does not make the
188    * specified part available, but only avoids loading this part from the server.
189    *
190    * @param {string} lang A language of the given translation table
191    * @param {string} part A name of the target part
192    * @param {object} table A translation table to set to the specified part
193    *
194    * @return {object} $translatePartialLoaderProvider, so this method is chainable
195    * @throws {TypeError} The method could throw a **TypeError** if you pass params
196    * of the wrong type. Please, note that the `lang` and `part` params have to be a
197    * non-empty **string**s and the `table` param has to be an object.
198    */
199   this.setPart = function (lang, part, table) {
200     if (!isStringValid(lang)) {
201       throw new TypeError('Couldn\'t set part.`lang` parameter has to be a string!');
202     }
203     if (!isStringValid(part)) {
204       throw new TypeError('Couldn\'t set part.`part` parameter has to be a string!');
205     }
206     if (typeof table !== 'object' || table === null) {
207       throw new TypeError('Couldn\'t set part. `table` parameter has to be an object!');
208     }
209
210     if (!hasPart(part)) {
211       parts[part] = new Part(part);
212       parts[part].isActive = false;
213     }
214
215     parts[part].tables[lang] = table;
216     return this;
217   };
218
219   /**
220    * @ngdoc function
221    * @name pascalprecht.translate.$translatePartialLoaderProvider#deletePart
222    * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
223    *
224    * @description
225    * Removes the previously added part of the translation data. So, `angular-translate` will not
226    * load it at the startup.
227    *
228    * @param {string} name A name of the part to delete
229    *
230    * @returns {object} $translatePartialLoaderProvider, so this method is chainable
231    *
232    * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong
233    * type. Please, note that the `name` param has to be a non-empty **string**.
234    */
235   this.deletePart = function (name) {
236     if (!isStringValid(name)) {
237       throw new TypeError('Couldn\'t delete part, first arg has to be string.');
238     }
239
240     if (hasPart(name)) {
241       parts[name].isActive = false;
242     }
243
244     return this;
245   };
246
247
248   /**
249    * @ngdoc function
250    * @name pascalprecht.translate.$translatePartialLoaderProvider#isPartAvailable
251    * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
252    *
253    * @description
254    * Checks if the specific part is available. A part becomes available after it was added by the
255    * `addPart` method. Available parts would be loaded from the server once the `angular-translate`
256    * asks the loader to that.
257    *
258    * @param {string} name A name of the part to check
259    *
260    * @returns {boolean} Returns **true** if the part is available now and **false** if not.
261    *
262    * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong
263    * type. Please, note that the `name` param has to be a non-empty **string**.
264    */
265   this.isPartAvailable = isPartAvailable;
266
267   /**
268    * @ngdoc object
269    * @name pascalprecht.translate.$translatePartialLoader
270    *
271    * @requires $q
272    * @requires $http
273    * @requires $injector
274    * @requires $rootScope
275    * @requires $translate
276    *
277    * @description
278    *
279    * @param {object} options Options object
280    *
281    * @throws {TypeError}
282    */
283   this.$get = ['$rootScope', '$injector', '$q', '$http',
284   function($rootScope, $injector, $q, $http) {
285
286     /**
287      * @ngdoc event
288      * @name pascalprecht.translate.$translatePartialLoader#$translatePartialLoaderStructureChanged
289      * @eventOf pascalprecht.translate.$translatePartialLoader
290      * @eventType broadcast on root scope
291      *
292      * @description
293      * A $translatePartialLoaderStructureChanged event is called when a state of the loader was
294      * changed somehow. It could mean either some part is added or some part is deleted. Anyway when
295      * you get this event the translation table is not longer current and has to be updated.
296      *
297      * @param {string} name A name of the part which is a reason why the event was fired
298      */
299
300     var service = function(options) {
301       if (!isStringValid(options.key)) {
302         throw new TypeError('Unable to load data, a key is not a non-empty string.');
303       }
304
305       if (!isStringValid(options.urlTemplate) && !angular.isFunction(options.urlTemplate)) {
306         throw new TypeError('Unable to load data, a urlTemplate is not a non-empty string or not a function.');
307       }
308
309       var errorHandler = options.loadFailureHandler;
310       if (errorHandler !== undefined) {
311         if (!angular.isString(errorHandler)) {
312           throw new Error('Unable to load data, a loadFailureHandler is not a string.');
313         } else {
314           errorHandler = $injector.get(errorHandler);
315         }
316       }
317
318       var loaders = [],
319           prioritizedParts = getPrioritizedParts();
320
321       angular.forEach(prioritizedParts, function(part) {
322         loaders.push(
323           part.getTable(options.key, $q, $http, options.$http, options.urlTemplate, errorHandler)
324         );
325         part.urlTemplate = options.urlTemplate;
326       });
327
328       return $q.all(loaders)
329         .then(function() {
330           var table = {};
331           prioritizedParts = getPrioritizedParts();
332           angular.forEach(prioritizedParts, function(part) {
333             deepExtend(table, part.tables[options.key]);
334           });
335           return table;
336         }, function() {
337           return $q.reject(options.key);
338         });
339     };
340
341     /**
342      * @ngdoc function
343      * @name pascalprecht.translate.$translatePartialLoader#addPart
344      * @methodOf pascalprecht.translate.$translatePartialLoader
345      *
346      * @description
347      * Registers a new part of the translation table. This method does not actually perform any xhr
348      * requests to get translation data. The new parts will be loaded in order of priority from the server next time
349      * `angular-translate` asks the loader to load translations.
350      *
351      * @param {string} name A name of the part to add
352      * @param {int} [priority=0] Sets the load priority of this part.
353      *
354      * @returns {object} $translatePartialLoader, so this method is chainable
355      *
356      * @fires {$translatePartialLoaderStructureChanged} The $translatePartialLoaderStructureChanged
357      * event would be fired by this method in case the new part affected somehow on the loaders
358      * state. This way it means that there are a new translation data available to be loaded from
359      * the server.
360      *
361      * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong
362      * type. Please, note that the `name` param has to be a non-empty **string**.
363      */
364     service.addPart = function(name, priority) {
365       if (!isStringValid(name)) {
366         throw new TypeError('Couldn\'t add part, first arg has to be a string');
367       }
368
369       if (!hasPart(name)) {
370         parts[name] = new Part(name, priority);
371         $rootScope.$emit('$translatePartialLoaderStructureChanged', name);
372       } else if (!parts[name].isActive) {
373         parts[name].isActive = true;
374         $rootScope.$emit('$translatePartialLoaderStructureChanged', name);
375       }
376
377       return service;
378     };
379
380     /**
381      * @ngdoc function
382      * @name pascalprecht.translate.$translatePartialLoader#deletePart
383      * @methodOf pascalprecht.translate.$translatePartialLoader
384      *
385      * @description
386      * Deletes the previously added part of the translation data. The target part could be deleted
387      * either logically or physically. When the data is deleted logically it is not actually deleted
388      * from the browser, but the loader marks it as not active and prevents it from affecting on the
389      * translations. If the deleted in such way part is added again, the loader will use the
390      * previously loaded data rather than loading it from the server once more time. But if the data
391      * is deleted physically, the loader will completely remove all information about it. So in case
392      * of recycling this part will be loaded from the server again.
393      *
394      * @param {string} name A name of the part to delete
395      * @param {boolean=} [removeData=false] An indicator if the loader has to remove a loaded
396      * translation data physically. If the `removeData` if set to **false** the loaded data will not be
397      * deleted physically and might be reused in the future to prevent an additional xhr requests.
398      *
399      * @returns {object} $translatePartialLoader, so this method is chainable
400      *
401      * @fires {$translatePartialLoaderStructureChanged} The $translatePartialLoaderStructureChanged
402      * event would be fired by this method in case a part deletion process affects somehow on the
403      * loaders state. This way it means that some part of the translation data is now deprecated and
404      * the translation table has to be recompiled with the remaining translation parts.
405      *
406      * @throws {TypeError} The method could throw a **TypeError** if you pass some param of the
407      * wrong type. Please, note that the `name` param has to be a non-empty **string** and
408      * the `removeData` param has to be either **undefined** or **boolean**.
409      */
410     service.deletePart = function(name, removeData) {
411       if (!isStringValid(name)) {
412         throw new TypeError('Couldn\'t delete part, first arg has to be string');
413       }
414
415       if (removeData === undefined) {
416         removeData = false;
417       } else if (typeof removeData !== 'boolean') {
418         throw new TypeError('Invalid type of a second argument, a boolean expected.');
419       }
420
421       if (hasPart(name)) {
422         var wasActive = parts[name].isActive;
423         if (removeData) {
424           var $translate = $injector.get('$translate');
425           var cache = $translate.loaderCache();
426           if (typeof(cache) === 'string') {
427             // getting on-demand instance of loader
428             cache = $injector.get(cache);
429           }
430           // Purging items from cache...
431           if (typeof(cache) === 'object') {
432             angular.forEach(parts[name].tables, function(value, key) {
433                 cache.remove(parts[name].parseUrl(parts[name].urlTemplate, key));
434             });
435           }
436           delete parts[name];
437         } else {
438           parts[name].isActive = false;
439         }
440         if (wasActive) {
441           $rootScope.$emit('$translatePartialLoaderStructureChanged', name);
442         }
443       }
444
445       return service;
446     };
447
448     /**
449      * @ngdoc function
450      * @name pascalprecht.translate.$translatePartialLoader#isPartLoaded
451      * @methodOf pascalprecht.translate.$translatePartialLoader
452      *
453      * @description
454      * Checks if the registered translation part is loaded into the translation table.
455      *
456      * @param {string} name A name of the part
457      * @param {string} lang A key of the language
458      *
459      * @returns {boolean} Returns **true** if the translation of the part is loaded to the translation table and **false** if not.
460      *
461      * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong
462      * type. Please, note that the `name` and `lang` params have to be non-empty **string**.
463      */
464     service.isPartLoaded = function(name, lang) {
465       return angular.isDefined(parts[name]) && angular.isDefined(parts[name].tables[lang]);
466     };
467
468     /**
469      * @ngdoc function
470      * @name pascalprecht.translate.$translatePartialLoader#getRegisteredParts
471      * @methodOf pascalprecht.translate.$translatePartialLoader
472      *
473      * @description
474      * Gets names of the parts that were added with the `addPart`.
475      *
476      * @returns {array} Returns array of registered parts, if none were registered then an empty array is returned.
477      */
478     service.getRegisteredParts = function() {
479       var registeredParts = [];
480       angular.forEach(parts, function(p){
481         if(p.isActive) {
482           registeredParts.push(p.name);
483         }
484       });
485       return registeredParts;
486     };
487
488
489
490     /**
491      * @ngdoc function
492      * @name pascalprecht.translate.$translatePartialLoader#isPartAvailable
493      * @methodOf pascalprecht.translate.$translatePartialLoader
494      *
495      * @description
496      * Checks if a target translation part is available. The part becomes available just after it was
497      * added by the `addPart` method. Part's availability does not mean that it was loaded from the
498      * server, but only that it was added to the loader. The available part might be loaded next
499      * time the loader is called.
500      *
501      * @param {string} name A name of the part to delete
502      *
503      * @returns {boolean} Returns **true** if the part is available now and **false** if not.
504      *
505      * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong
506      * type. Please, note that the `name` param has to be a non-empty **string**.
507      */
508     service.isPartAvailable = isPartAvailable;
509
510     return service;
511
512   }];
513
514 }
515
516 $translatePartialLoader.displayName = '$translatePartialLoader';
517 return 'pascalprecht.translate';
518
519 }));