2 * angular-translate - v2.11.0 - 2016-03-20
4 * Copyright (c) 2016 The angular-translate team, Pascal Precht; Licensed MIT
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 () {
12 } else if (typeof exports === 'object') {
13 // Node. Does not work with strict CommonJS, but
14 // only CommonJS-like environments that support module.exports,
16 module.exports = factory();
22 angular.module('pascalprecht.translate')
25 * @name pascalprecht.translate.$translatePartialLoaderProvider
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.
33 .provider('$translatePartialLoader', $translatePartialLoader);
35 function $translatePartialLoader() {
44 * Represents Part object to add and set parts at runtime.
46 function Part(name, priority) {
50 this.priority = priority || 0;
58 * Returns a parsed url template string and replaces given target lang
61 * @param {string|function} urlTemplate - Either a string containing an url pattern (with
62 * '{part}' and '{lang}') or a function(part, lang)
64 * @param {string} targetLang - Language key for language to be used.
65 * @return {string} Parsed url template string
67 Part.prototype.parseUrl = function(urlTemplate, targetLang) {
68 if (angular.isFunction(urlTemplate)) {
69 return urlTemplate(this.name, targetLang);
71 return urlTemplate.replace(/\{part\}/g, this.name).replace(/\{lang\}/g, targetLang);
74 Part.prototype.getTable = function(lang, $q, $http, $httpOptions, urlTemplate, errorHandler) {
76 if (!this.tables[lang]) {
79 return $http(angular.extend({
81 url: this.parseUrl(urlTemplate, lang)
83 .then(function(result){
84 self.tables[lang] = result.data;
88 return errorHandler(self.name, lang)
89 .then(function(data) {
90 self.tables[lang] = data;
93 return $q.reject(self.name);
96 return $q.reject(self.name);
101 return $q.when(this.tables[lang]);
107 function hasPart(name) {
108 return Object.prototype.hasOwnProperty.call(parts, name);
111 function isStringValid(str) {
112 return angular.isString(str) && str !== '';
115 function isPartAvailable(name) {
116 if (!isStringValid(name)) {
117 throw new TypeError('Invalid type of a first argument, a non-empty string expected.');
120 return (hasPart(name) && parts[name].isActive);
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]);
130 dst[property] = src[property];
136 function getPrioritizedParts() {
137 var prioritizedParts = [];
138 for(var part in parts) {
139 if (parts[part].isActive) {
140 prioritizedParts.push(parts[part]);
143 prioritizedParts.sort(function (a, b) {
144 return a.priority - b.priority;
146 return prioritizedParts;
152 * @name pascalprecht.translate.$translatePartialLoaderProvider#addPart
153 * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
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.
160 * @param {string} name A name of the part to add
161 * @param {int} [priority=0] Sets the load priority of this part.
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**.
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!');
173 if (!hasPart(name)) {
174 parts[name] = new Part(name, priority);
176 parts[name].isActive = true;
183 * @name pascalprecht.translate.$translatePartialLoaderProvider#setPart
184 * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
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.
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
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.
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!');
203 if (!isStringValid(part)) {
204 throw new TypeError('Couldn\'t set part.`part` parameter has to be a string!');
206 if (typeof table !== 'object' || table === null) {
207 throw new TypeError('Couldn\'t set part. `table` parameter has to be an object!');
210 if (!hasPart(part)) {
211 parts[part] = new Part(part);
212 parts[part].isActive = false;
215 parts[part].tables[lang] = table;
221 * @name pascalprecht.translate.$translatePartialLoaderProvider#deletePart
222 * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
225 * Removes the previously added part of the translation data. So, `angular-translate` will not
226 * load it at the startup.
228 * @param {string} name A name of the part to delete
230 * @returns {object} $translatePartialLoaderProvider, so this method is chainable
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**.
235 this.deletePart = function (name) {
236 if (!isStringValid(name)) {
237 throw new TypeError('Couldn\'t delete part, first arg has to be string.');
241 parts[name].isActive = false;
250 * @name pascalprecht.translate.$translatePartialLoaderProvider#isPartAvailable
251 * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
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.
258 * @param {string} name A name of the part to check
260 * @returns {boolean} Returns **true** if the part is available now and **false** if not.
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**.
265 this.isPartAvailable = isPartAvailable;
269 * @name pascalprecht.translate.$translatePartialLoader
273 * @requires $injector
274 * @requires $rootScope
275 * @requires $translate
279 * @param {object} options Options object
281 * @throws {TypeError}
283 this.$get = ['$rootScope', '$injector', '$q', '$http',
284 function($rootScope, $injector, $q, $http) {
288 * @name pascalprecht.translate.$translatePartialLoader#$translatePartialLoaderStructureChanged
289 * @eventOf pascalprecht.translate.$translatePartialLoader
290 * @eventType broadcast on root scope
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.
297 * @param {string} name A name of the part which is a reason why the event was fired
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.');
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.');
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.');
314 errorHandler = $injector.get(errorHandler);
319 prioritizedParts = getPrioritizedParts();
321 angular.forEach(prioritizedParts, function(part) {
323 part.getTable(options.key, $q, $http, options.$http, options.urlTemplate, errorHandler)
325 part.urlTemplate = options.urlTemplate;
328 return $q.all(loaders)
331 prioritizedParts = getPrioritizedParts();
332 angular.forEach(prioritizedParts, function(part) {
333 deepExtend(table, part.tables[options.key]);
337 return $q.reject(options.key);
343 * @name pascalprecht.translate.$translatePartialLoader#addPart
344 * @methodOf pascalprecht.translate.$translatePartialLoader
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.
351 * @param {string} name A name of the part to add
352 * @param {int} [priority=0] Sets the load priority of this part.
354 * @returns {object} $translatePartialLoader, so this method is chainable
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
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**.
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');
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);
382 * @name pascalprecht.translate.$translatePartialLoader#deletePart
383 * @methodOf pascalprecht.translate.$translatePartialLoader
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.
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.
399 * @returns {object} $translatePartialLoader, so this method is chainable
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.
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**.
410 service.deletePart = function(name, removeData) {
411 if (!isStringValid(name)) {
412 throw new TypeError('Couldn\'t delete part, first arg has to be string');
415 if (removeData === undefined) {
417 } else if (typeof removeData !== 'boolean') {
418 throw new TypeError('Invalid type of a second argument, a boolean expected.');
422 var wasActive = parts[name].isActive;
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);
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));
438 parts[name].isActive = false;
441 $rootScope.$emit('$translatePartialLoaderStructureChanged', name);
450 * @name pascalprecht.translate.$translatePartialLoader#isPartLoaded
451 * @methodOf pascalprecht.translate.$translatePartialLoader
454 * Checks if the registered translation part is loaded into the translation table.
456 * @param {string} name A name of the part
457 * @param {string} lang A key of the language
459 * @returns {boolean} Returns **true** if the translation of the part is loaded to the translation table and **false** if not.
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**.
464 service.isPartLoaded = function(name, lang) {
465 return angular.isDefined(parts[name]) && angular.isDefined(parts[name].tables[lang]);
470 * @name pascalprecht.translate.$translatePartialLoader#getRegisteredParts
471 * @methodOf pascalprecht.translate.$translatePartialLoader
474 * Gets names of the parts that were added with the `addPart`.
476 * @returns {array} Returns array of registered parts, if none were registered then an empty array is returned.
478 service.getRegisteredParts = function() {
479 var registeredParts = [];
480 angular.forEach(parts, function(p){
482 registeredParts.push(p.name);
485 return registeredParts;
492 * @name pascalprecht.translate.$translatePartialLoader#isPartAvailable
493 * @methodOf pascalprecht.translate.$translatePartialLoader
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.
501 * @param {string} name A name of the part to delete
503 * @returns {boolean} Returns **true** if the part is available now and **false** if not.
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**.
508 service.isPartAvailable = isPartAvailable;
516 $translatePartialLoader.displayName = '$translatePartialLoader';
517 return 'pascalprecht.translate';