├── README.md ├── angular-translate-loader-partial.js ├── angular-translate-loader-partial.min.js ├── bower.json └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # angular-translate-loader-partial (bower shadow repository) 2 | 3 | This is the _Bower shadow_ repository for *angular-translate-loader-partial*. 4 | 5 | ## Bugs and issues 6 | 7 | Please file any issues and bugs in our main repository at [angular-translate/angular-translate](https://github.com/angular-translate/angular-translate/issues). 8 | 9 | ## Usage 10 | 11 | ### via Bower 12 | 13 | ```bash 14 | $ bower install angular-translate-loader-partial 15 | ``` 16 | 17 | ### via NPM 18 | 19 | ```bash 20 | $ npm install angular-translate-loader-partial 21 | ``` 22 | 23 | ### via cdnjs 24 | 25 | Please have a look at https://cdnjs.com/libraries/angular-translate-loader-partial for specific versions. 26 | 27 | ## License 28 | 29 | Licensed under MIT. See more details at [angular-translate/angular-translate](https://github.com/angular-translate/angular-translate). 30 | -------------------------------------------------------------------------------- /angular-translate-loader-partial.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * angular-translate - v2.19.1 - 2024-01-21 3 | * 4 | * Copyright (c) 2024 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 module === 'object' && module.exports) { 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, urlTemplate) { 47 | this.name = name; 48 | this.isActive = true; 49 | this.tables = {}; 50 | this.priority = priority || 0; 51 | this.langPromises = {}; 52 | this.urlTemplate = urlTemplate; 53 | } 54 | 55 | /** 56 | * @name parseUrl 57 | * @method 58 | * 59 | * @description 60 | * Returns a parsed url template string and replaces given target lang 61 | * and part name it. 62 | * 63 | * @param {string|function} urlTemplate - Either a string containing an url pattern (with 64 | * '{part}' and '{lang}') or a function(part, lang) 65 | * returning a string. 66 | * @param {string} targetLang - Language key for language to be used. 67 | * @return {string} Parsed url template string 68 | */ 69 | Part.prototype.parseUrl = function (urlTemplate, targetLang) { 70 | if (angular.isFunction(urlTemplate)) { 71 | return urlTemplate(this.name, targetLang); 72 | } 73 | return urlTemplate.replace(/\{part\}/g, this.name).replace(/\{lang\}/g, targetLang); 74 | }; 75 | 76 | Part.prototype.getTable = function (lang, $q, $http, $httpOptions, urlTemplate, errorHandler) { 77 | 78 | //locals 79 | var self = this; 80 | var lastLangPromise = this.langPromises[lang]; 81 | var deferred = $q.defer(); 82 | 83 | //private helper helpers 84 | var fetchData = function () { 85 | return $http( 86 | angular.extend({ 87 | method : 'GET', 88 | url : self.parseUrl(self.urlTemplate || urlTemplate, lang) 89 | }, 90 | $httpOptions) 91 | ); 92 | }; 93 | 94 | //private helper 95 | var handleNewData = function (data) { 96 | self.tables[lang] = data; 97 | deferred.resolve(data); 98 | }; 99 | 100 | //private helper 101 | var rejectDeferredWithPartName = function () { 102 | deferred.reject(self.name); 103 | }; 104 | 105 | //private helper 106 | var tryGettingThisTable = function () { 107 | //data fetching logic 108 | fetchData().then( 109 | function (result) { 110 | handleNewData(result.data); 111 | }, 112 | function (errorResponse) { 113 | if (errorHandler) { 114 | errorHandler(self.name, lang, errorResponse).then(handleNewData, rejectDeferredWithPartName); 115 | } else { 116 | rejectDeferredWithPartName(); 117 | } 118 | }); 119 | }; 120 | 121 | //loading logic 122 | if (!this.tables[lang]) { 123 | //let's try loading the data 124 | if (!lastLangPromise) { 125 | //this is the first request - just go ahead and hit the server 126 | tryGettingThisTable(); 127 | } else { 128 | //this is an additional request after one or more unfinished or failed requests 129 | //chain the deferred off the previous request's promise so that this request conditionally executes 130 | //if the previous request succeeds then the result will be passed through, but if it fails then this request will try again and hit the server 131 | lastLangPromise.then(deferred.resolve, tryGettingThisTable); 132 | } 133 | //retain a reference to the last promise so we can continue the chain if another request is made before any succeed 134 | //you can picture the promise chain as a singly-linked list (formed by the .then handler queues) that's traversed by the execution context 135 | this.langPromises[lang] = deferred.promise; 136 | } 137 | else { 138 | //the part has already been loaded - if lastLangPromise is also undefined then the table has been populated using setPart 139 | //this breaks the promise chain because we're not tying langDeferred's outcome to a previous call's promise handler queues, but we don't care because there's no asynchronous execution context to keep track of anymore 140 | deferred.resolve(this.tables[lang]); 141 | } 142 | return deferred.promise; 143 | }; 144 | 145 | var parts = {}; 146 | 147 | function hasPart(name) { 148 | return Object.prototype.hasOwnProperty.call(parts, name); 149 | } 150 | 151 | function isStringValid(str) { 152 | return angular.isString(str) && str !== ''; 153 | } 154 | 155 | function isPartAvailable(name) { 156 | if (!isStringValid(name)) { 157 | throw new TypeError('Invalid type of a first argument, a non-empty string expected.'); 158 | } 159 | 160 | return (hasPart(name) && parts[name].isActive); 161 | } 162 | 163 | function deepExtend(dst, src) { 164 | for (var property in src) { 165 | if (src[property] && src[property].constructor && 166 | src[property].constructor === Object) { 167 | dst[property] = dst[property] || {}; 168 | deepExtend(dst[property], src[property]); 169 | } else { 170 | dst[property] = src[property]; 171 | } 172 | } 173 | return dst; 174 | } 175 | 176 | function getPrioritizedParts() { 177 | var prioritizedParts = []; 178 | for (var part in parts) { 179 | if (parts[part].isActive) { 180 | prioritizedParts.push(parts[part]); 181 | } 182 | } 183 | prioritizedParts.sort(function (a, b) { 184 | return a.priority - b.priority; 185 | }); 186 | return prioritizedParts; 187 | } 188 | 189 | 190 | /** 191 | * @ngdoc function 192 | * @name pascalprecht.translate.$translatePartialLoaderProvider#addPart 193 | * @methodOf pascalprecht.translate.$translatePartialLoaderProvider 194 | * 195 | * @description 196 | * Registers a new part of the translation table to be loaded once the 197 | * `angular-translate` gets into runtime phase. It does not actually load any 198 | * translation data, but only registers a part to be loaded in the future. 199 | * 200 | * @param {string} name A name of the part to add 201 | * @param {int} [priority=0] Sets the load priority of this part. 202 | * @param {string|function} urlTemplate Either a string containing an url pattern (with 203 | * '{part}' and '{lang}') or a function(part, lang) 204 | * returning a string. 205 | * 206 | * @returns {object} $translatePartialLoaderProvider, so this method is chainable 207 | * @throws {TypeError} The method could throw a **TypeError** if you pass the param 208 | * of the wrong type. Please, note that the `name` param has to be a 209 | * non-empty **string**. 210 | */ 211 | this.addPart = function (name, priority, urlTemplate) { 212 | if (!isStringValid(name)) { 213 | throw new TypeError('Couldn\'t add part, part name has to be a string!'); 214 | } 215 | 216 | if (!hasPart(name)) { 217 | parts[name] = new Part(name, priority, urlTemplate); 218 | } 219 | parts[name].isActive = true; 220 | 221 | return this; 222 | }; 223 | 224 | /** 225 | * @ngdocs function 226 | * @name pascalprecht.translate.$translatePartialLoaderProvider#setPart 227 | * @methodOf pascalprecht.translate.$translatePartialLoaderProvider 228 | * 229 | * @description 230 | * Sets a translation table to the specified part. This method does not make the 231 | * specified part available, but only avoids loading this part from the server. 232 | * 233 | * @param {string} lang A language of the given translation table 234 | * @param {string} part A name of the target part 235 | * @param {object} table A translation table to set to the specified part 236 | * 237 | * @return {object} $translatePartialLoaderProvider, so this method is chainable 238 | * @throws {TypeError} The method could throw a **TypeError** if you pass params 239 | * of the wrong type. Please, note that the `lang` and `part` params have to be a 240 | * non-empty **string**s and the `table` param has to be an object. 241 | */ 242 | this.setPart = function (lang, part, table) { 243 | if (!isStringValid(lang)) { 244 | throw new TypeError('Couldn\'t set part.`lang` parameter has to be a string!'); 245 | } 246 | if (!isStringValid(part)) { 247 | throw new TypeError('Couldn\'t set part.`part` parameter has to be a string!'); 248 | } 249 | if (typeof table !== 'object' || table === null) { 250 | throw new TypeError('Couldn\'t set part. `table` parameter has to be an object!'); 251 | } 252 | 253 | if (!hasPart(part)) { 254 | parts[part] = new Part(part); 255 | parts[part].isActive = false; 256 | } 257 | 258 | parts[part].tables[lang] = table; 259 | return this; 260 | }; 261 | 262 | /** 263 | * @ngdoc function 264 | * @name pascalprecht.translate.$translatePartialLoaderProvider#deletePart 265 | * @methodOf pascalprecht.translate.$translatePartialLoaderProvider 266 | * 267 | * @description 268 | * Removes the previously added part of the translation data. So, `angular-translate` will not 269 | * load it at the startup. 270 | * 271 | * @param {string} name A name of the part to delete 272 | * 273 | * @returns {object} $translatePartialLoaderProvider, so this method is chainable 274 | * 275 | * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong 276 | * type. Please, note that the `name` param has to be a non-empty **string**. 277 | */ 278 | this.deletePart = function (name) { 279 | if (!isStringValid(name)) { 280 | throw new TypeError('Couldn\'t delete part, first arg has to be string.'); 281 | } 282 | 283 | if (hasPart(name)) { 284 | parts[name].isActive = false; 285 | } 286 | 287 | return this; 288 | }; 289 | 290 | 291 | /** 292 | * @ngdoc function 293 | * @name pascalprecht.translate.$translatePartialLoaderProvider#isPartAvailable 294 | * @methodOf pascalprecht.translate.$translatePartialLoaderProvider 295 | * 296 | * @description 297 | * Checks if the specific part is available. A part becomes available after it was added by the 298 | * `addPart` method. Available parts would be loaded from the server once the `angular-translate` 299 | * asks the loader to that. 300 | * 301 | * @param {string} name A name of the part to check 302 | * 303 | * @returns {boolean} Returns **true** if the part is available now and **false** if not. 304 | * 305 | * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong 306 | * type. Please, note that the `name` param has to be a non-empty **string**. 307 | */ 308 | this.isPartAvailable = isPartAvailable; 309 | 310 | /** 311 | * @ngdoc object 312 | * @name pascalprecht.translate.$translatePartialLoader 313 | * 314 | * @requires $q 315 | * @requires $http 316 | * @requires $injector 317 | * @requires $rootScope 318 | * @requires $translate 319 | * 320 | * @description 321 | * 322 | * @param {object} options Options object 323 | * 324 | * @throws {TypeError} 325 | */ 326 | this.$get = ['$rootScope', '$injector', '$q', '$http', '$log', 327 | function ($rootScope, $injector, $q, $http, $log) { 328 | 329 | /** 330 | * @ngdoc event 331 | * @name pascalprecht.translate.$translatePartialLoader#$translatePartialLoaderStructureChanged 332 | * @eventOf pascalprecht.translate.$translatePartialLoader 333 | * @eventType broadcast on root scope 334 | * 335 | * @description 336 | * A $translatePartialLoaderStructureChanged event is called when a state of the loader was 337 | * changed somehow. It could mean either some part is added or some part is deleted. Anyway when 338 | * you get this event the translation table is not longer current and has to be updated. 339 | * 340 | * @param {string} name A name of the part which is a reason why the event was fired 341 | */ 342 | 343 | var service = function (options) { 344 | if (!isStringValid(options.key)) { 345 | throw new TypeError('Unable to load data, a key is not a non-empty string.'); 346 | } 347 | 348 | if (!isStringValid(options.urlTemplate) && !angular.isFunction(options.urlTemplate)) { 349 | throw new TypeError('Unable to load data, a urlTemplate is not a non-empty string or not a function.'); 350 | } 351 | 352 | var errorHandler = options.loadFailureHandler; 353 | if (errorHandler !== undefined) { 354 | if (!angular.isString(errorHandler)) { 355 | throw new Error('Unable to load data, a loadFailureHandler is not a string.'); 356 | } else { 357 | errorHandler = $injector.get(errorHandler); 358 | } 359 | } 360 | 361 | var loaders = [], 362 | prioritizedParts = getPrioritizedParts(); 363 | 364 | angular.forEach(prioritizedParts, function (part) { 365 | loaders.push( 366 | part.getTable(options.key, $q, $http, options.$http, options.urlTemplate, errorHandler) 367 | ); 368 | part.urlTemplate = part.urlTemplate || options.urlTemplate; 369 | }); 370 | 371 | // workaround for #1781 372 | var structureHasBeenChangedWhileLoading = false; 373 | var dirtyCheckEventCloser = $rootScope.$on('$translatePartialLoaderStructureChanged', function () { 374 | structureHasBeenChangedWhileLoading = true; 375 | }); 376 | 377 | return $q.all(loaders) 378 | .then(function () { 379 | dirtyCheckEventCloser(); 380 | if (structureHasBeenChangedWhileLoading) { 381 | if (!options.__retries) { 382 | // the part structure has been changed while loading (the origin ones) 383 | // this can happen if an addPart/removePart has been invoked right after a $translate.use(lang) 384 | // TODO maybe we can optimize this with the actual list of missing parts 385 | options.__retries = (options.__retries || 0) + 1; 386 | return service(options); 387 | } else { 388 | // the part structure has been changed again while loading (retried one) 389 | // because this could an infinite loop, this will not load another one again 390 | $log.warn('The partial loader has detected a multiple structure change (with addPort/removePart) ' + 391 | 'while loading translations. You should consider using promises of $translate.use(lang) and ' + 392 | '$translate.refresh(). Also parts should be added/removed right before an explicit refresh ' + 393 | 'if possible.'); 394 | } 395 | } 396 | var table = {}; 397 | prioritizedParts = getPrioritizedParts(); 398 | angular.forEach(prioritizedParts, function (part) { 399 | deepExtend(table, part.tables[options.key]); 400 | }); 401 | return table; 402 | }, function () { 403 | dirtyCheckEventCloser(); 404 | return $q.reject(options.key); 405 | }); 406 | }; 407 | 408 | /** 409 | * @ngdoc function 410 | * @name pascalprecht.translate.$translatePartialLoader#addPart 411 | * @methodOf pascalprecht.translate.$translatePartialLoader 412 | * 413 | * @description 414 | * Registers a new part of the translation table. This method does not actually perform any xhr 415 | * requests to get translation data. The new parts will be loaded in order of priority from the server next time 416 | * `angular-translate` asks the loader to load translations. 417 | * 418 | * @param {string} name A name of the part to add 419 | * @param {int} [priority=0] Sets the load priority of this part. 420 | * 421 | * @returns {object} $translatePartialLoader, so this method is chainable 422 | * 423 | * @fires {$translatePartialLoaderStructureChanged} The $translatePartialLoaderStructureChanged 424 | * event would be fired by this method in case the new part affected somehow on the loaders 425 | * state. This way it means that there are a new translation data available to be loaded from 426 | * the server. 427 | * 428 | * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong 429 | * type. Please, note that the `name` param has to be a non-empty **string**. 430 | */ 431 | service.addPart = function (name, priority, urlTemplate) { 432 | if (!isStringValid(name)) { 433 | throw new TypeError('Couldn\'t add part, first arg has to be a string'); 434 | } 435 | 436 | if (!hasPart(name)) { 437 | parts[name] = new Part(name, priority, urlTemplate); 438 | $rootScope.$emit('$translatePartialLoaderStructureChanged', name); 439 | } else if (!parts[name].isActive) { 440 | parts[name].isActive = true; 441 | $rootScope.$emit('$translatePartialLoaderStructureChanged', name); 442 | } 443 | 444 | return service; 445 | }; 446 | 447 | /** 448 | * @ngdoc function 449 | * @name pascalprecht.translate.$translatePartialLoader#deletePart 450 | * @methodOf pascalprecht.translate.$translatePartialLoader 451 | * 452 | * @description 453 | * Deletes the previously added part of the translation data. The target part could be deleted 454 | * either logically or physically. When the data is deleted logically it is not actually deleted 455 | * from the browser, but the loader marks it as not active and prevents it from affecting on the 456 | * translations. If the deleted in such way part is added again, the loader will use the 457 | * previously loaded data rather than loading it from the server once more time. But if the data 458 | * is deleted physically, the loader will completely remove all information about it. So in case 459 | * of recycling this part will be loaded from the server again. 460 | * 461 | * @param {string} name A name of the part to delete 462 | * @param {boolean=} [removeData=false] An indicator if the loader has to remove a loaded 463 | * translation data physically. If the `removeData` if set to **false** the loaded data will not be 464 | * deleted physically and might be reused in the future to prevent an additional xhr requests. 465 | * 466 | * @returns {object} $translatePartialLoader, so this method is chainable 467 | * 468 | * @fires {$translatePartialLoaderStructureChanged} The $translatePartialLoaderStructureChanged 469 | * event would be fired by this method in case a part deletion process affects somehow on the 470 | * loaders state. This way it means that some part of the translation data is now deprecated and 471 | * the translation table has to be recompiled with the remaining translation parts. 472 | * 473 | * @throws {TypeError} The method could throw a **TypeError** if you pass some param of the 474 | * wrong type. Please, note that the `name` param has to be a non-empty **string** and 475 | * the `removeData` param has to be either **undefined** or **boolean**. 476 | */ 477 | service.deletePart = function (name, removeData) { 478 | if (!isStringValid(name)) { 479 | throw new TypeError('Couldn\'t delete part, first arg has to be string'); 480 | } 481 | 482 | if (removeData === undefined) { 483 | removeData = false; 484 | } else if (typeof removeData !== 'boolean') { 485 | throw new TypeError('Invalid type of a second argument, a boolean expected.'); 486 | } 487 | 488 | if (hasPart(name)) { 489 | var wasActive = parts[name].isActive; 490 | if (removeData) { 491 | var $translate = $injector.get('$translate'); 492 | var cache = $translate.loaderCache(); 493 | if (typeof(cache) === 'string') { 494 | // getting on-demand instance of loader 495 | cache = $injector.get(cache); 496 | } 497 | // Purging items from cache... 498 | if (typeof(cache) === 'object') { 499 | angular.forEach(parts[name].tables, function (value, key) { 500 | cache.remove(parts[name].parseUrl(parts[name].urlTemplate, key)); 501 | }); 502 | } 503 | delete parts[name]; 504 | } else { 505 | parts[name].isActive = false; 506 | } 507 | if (wasActive) { 508 | $rootScope.$emit('$translatePartialLoaderStructureChanged', name); 509 | } 510 | } 511 | 512 | return service; 513 | }; 514 | 515 | /** 516 | * @ngdoc function 517 | * @name pascalprecht.translate.$translatePartialLoader#isPartLoaded 518 | * @methodOf pascalprecht.translate.$translatePartialLoader 519 | * 520 | * @description 521 | * Checks if the registered translation part is loaded into the translation table. 522 | * 523 | * @param {string} name A name of the part 524 | * @param {string} lang A key of the language 525 | * 526 | * @returns {boolean} Returns **true** if the translation of the part is loaded to the translation table and **false** if not. 527 | * 528 | * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong 529 | * type. Please, note that the `name` and `lang` params have to be non-empty **string**. 530 | */ 531 | service.isPartLoaded = function (name, lang) { 532 | return angular.isDefined(parts[name]) && angular.isDefined(parts[name].tables[lang]); 533 | }; 534 | 535 | /** 536 | * @ngdoc function 537 | * @name pascalprecht.translate.$translatePartialLoader#getRegisteredParts 538 | * @methodOf pascalprecht.translate.$translatePartialLoader 539 | * 540 | * @description 541 | * Gets names of the parts that were added with the `addPart`. 542 | * 543 | * @returns {array} Returns array of registered parts, if none were registered then an empty array is returned. 544 | */ 545 | service.getRegisteredParts = function () { 546 | var registeredParts = []; 547 | angular.forEach(parts, function (p) { 548 | if (p.isActive) { 549 | registeredParts.push(p.name); 550 | } 551 | }); 552 | return registeredParts; 553 | }; 554 | 555 | 556 | /** 557 | * @ngdoc function 558 | * @name pascalprecht.translate.$translatePartialLoader#isPartAvailable 559 | * @methodOf pascalprecht.translate.$translatePartialLoader 560 | * 561 | * @description 562 | * Checks if a target translation part is available. The part becomes available just after it was 563 | * added by the `addPart` method. Part's availability does not mean that it was loaded from the 564 | * server, but only that it was added to the loader. The available part might be loaded next 565 | * time the loader is called. 566 | * 567 | * @param {string} name A name of the part to delete 568 | * 569 | * @returns {boolean} Returns **true** if the part is available now and **false** if not. 570 | * 571 | * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong 572 | * type. Please, note that the `name` param has to be a non-empty **string**. 573 | */ 574 | service.isPartAvailable = isPartAvailable; 575 | 576 | return service; 577 | 578 | }]; 579 | 580 | } 581 | 582 | $translatePartialLoader.displayName = '$translatePartialLoader'; 583 | return 'pascalprecht.translate'; 584 | 585 | })); 586 | -------------------------------------------------------------------------------- /angular-translate-loader-partial.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * angular-translate - v2.19.1 - 2024-01-21 3 | * 4 | * Copyright (c) 2024 The angular-translate team, Pascal Precht; Licensed MIT 5 | */ 6 | !function(t,e){"function"==typeof define&&define.amd?define([],function(){return e()}):"object"==typeof module&&module.exports?module.exports=e():e()}(0,function(){function t(){"use strict";function a(t,e,r){this.name=t,this.isActive=!0,this.tables={},this.priority=e||0,this.langPromises={},this.urlTemplate=r}a.prototype.parseUrl=function(t,e){return angular.isFunction(t)?t(this.name,e):t.replace(/\{part\}/g,this.name).replace(/\{lang\}/g,e)},a.prototype.getTable=function(e,t,r,a,n,i){var o=this,s=this.langPromises[e],l=t.defer(),u=function(t){o.tables[e]=t,l.resolve(t)},c=function(){l.reject(o.name)},p=function(){r(angular.extend({method:"GET",url:o.parseUrl(o.urlTemplate||n,e)},a)).then(function(t){u(t.data)},function(t){i?i(o.name,e,t).then(u,c):c()})};return this.tables[e]?l.resolve(this.tables[e]):(s?s.then(l.resolve,p):p(),this.langPromises[e]=l.promise),l.promise};var n={};function i(t){return Object.prototype.hasOwnProperty.call(n,t)}function f(t){return angular.isString(t)&&""!==t}function t(t){if(!f(t))throw new TypeError("Invalid type of a first argument, a non-empty string expected.");return i(t)&&n[t].isActive}function d(){var t=[];for(var e in n)n[e].isActive&&t.push(n[e]);return t.sort(function(t,e){return t.priority-e.priority}),t}this.addPart=function(t,e,r){if(!f(t))throw new TypeError("Couldn't add part, part name has to be a string!");return i(t)||(n[t]=new a(t,e,r)),n[t].isActive=!0,this},this.setPart=function(t,e,r){if(!f(t))throw new TypeError("Couldn't set part.`lang` parameter has to be a string!");if(!f(e))throw new TypeError("Couldn't set part.`part` parameter has to be a string!");if("object"!=typeof r||null===r)throw new TypeError("Couldn't set part. `table` parameter has to be an object!");return i(e)||(n[e]=new a(e),n[e].isActive=!1),n[e].tables[t]=r,this},this.deletePart=function(t){if(!f(t))throw new TypeError("Couldn't delete part, first arg has to be string.");return i(t)&&(n[t].isActive=!1),this},this.isPartAvailable=t,this.$get=["$rootScope","$injector","$q","$http","$log",function(o,s,l,u,c){var p=function(r){if(!f(r.key))throw new TypeError("Unable to load data, a key is not a non-empty string.");if(!f(r.urlTemplate)&&!angular.isFunction(r.urlTemplate))throw new TypeError("Unable to load data, a urlTemplate is not a non-empty string or not a function.");var e=r.loadFailureHandler;if(void 0!==e){if(!angular.isString(e))throw new Error("Unable to load data, a loadFailureHandler is not a string.");e=s.get(e)}var a=[],t=d();angular.forEach(t,function(t){a.push(t.getTable(r.key,l,u,r.$http,r.urlTemplate,e)),t.urlTemplate=t.urlTemplate||r.urlTemplate});var n=!1,i=o.$on("$translatePartialLoaderStructureChanged",function(){n=!0});return l.all(a).then(function(){if(i(),n){if(!r.__retries)return r.__retries=(r.__retries||0)+1,p(r);c.warn("The partial loader has detected a multiple structure change (with addPort/removePart) while loading translations. You should consider using promises of $translate.use(lang) and $translate.refresh(). Also parts should be added/removed right before an explicit refresh if possible.")}var e={};return t=d(),angular.forEach(t,function(t){!function t(e,r){for(var a in r)r[a]&&r[a].constructor&&r[a].constructor===Object?(e[a]=e[a]||{},t(e[a],r[a])):e[a]=r[a];return e}(e,t.tables[r.key])}),e},function(){return i(),l.reject(r.key)})};return p.addPart=function(t,e,r){if(!f(t))throw new TypeError("Couldn't add part, first arg has to be a string");return i(t)?n[t].isActive||(n[t].isActive=!0,o.$emit("$translatePartialLoaderStructureChanged",t)):(n[t]=new a(t,e,r),o.$emit("$translatePartialLoaderStructureChanged",t)),p},p.deletePart=function(r,t){if(!f(r))throw new TypeError("Couldn't delete part, first arg has to be string");if(void 0===t)t=!1;else if("boolean"!=typeof t)throw new TypeError("Invalid type of a second argument, a boolean expected.");if(i(r)){var e=n[r].isActive;if(t){var a=s.get("$translate").loaderCache();"string"==typeof a&&(a=s.get(a)),"object"==typeof a&&angular.forEach(n[r].tables,function(t,e){a.remove(n[r].parseUrl(n[r].urlTemplate,e))}),delete n[r]}else n[r].isActive=!1;e&&o.$emit("$translatePartialLoaderStructureChanged",r)}return p},p.isPartLoaded=function(t,e){return angular.isDefined(n[t])&&angular.isDefined(n[t].tables[e])},p.getRegisteredParts=function(){var e=[];return angular.forEach(n,function(t){t.isActive&&e.push(t.name)}),e},p.isPartAvailable=t,p}]}return angular.module("pascalprecht.translate").provider("$translatePartialLoader",t),t.displayName="$translatePartialLoader","pascalprecht.translate"}); -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-translate-loader-partial", 3 | "description": "A plugin for Angular Translate", 4 | "version": "2.19.1", 5 | "main": "./angular-translate-loader-partial.js", 6 | "ignore": [], 7 | "author": "Pascal Precht", 8 | "license": "MIT", 9 | "dependencies": { 10 | "angular-translate": "~2.19.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-translate-loader-partial", 3 | "version": "2.19.1", 4 | "description": "angular-translate-loader-partial", 5 | "main": "angular-translate-loader-partial.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/angular-translate/bower-angular-translate-loader-partial.git" 9 | }, 10 | "keywords": [ 11 | "angular", 12 | "translate", 13 | "loader" 14 | ], 15 | "author": "Pascal Precht", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/angular-translate/angular-translate/issues" 19 | }, 20 | "homepage": "https://angular-translate.github.io", 21 | "dependencies": { 22 | "angular-translate": "~2.19.1" 23 | } 24 | } 25 | --------------------------------------------------------------------------------