(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define([], function () { return (factory()); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(); } else { factory(); } }(this, function () { 'use strict'; angular.module('tmh.dynamicLocale', []).config(['$provide', function($provide) { function makeStateful($delegate) { $delegate.$stateful = true; return $delegate; } $provide.decorator('dateFilter', ['$delegate', makeStateful]); $provide.decorator('numberFilter', ['$delegate', makeStateful]); $provide.decorator('currencyFilter', ['$delegate', makeStateful]); }]) .constant('tmhDynamicLocale.STORAGE_KEY', 'tmhDynamicLocale.locale') .provider('tmhDynamicLocale', ['tmhDynamicLocale.STORAGE_KEY', function(STORAGE_KEY) { var defaultLocale, localeLocationPattern = 'angular/i18n/angular-locale_{{locale}}.js', nodeToAppend, storageFactory = 'tmhDynamicLocaleStorageCache', storage, storageKey = STORAGE_KEY, storageGet = 'get', storagePut = 'put', promiseCache = {}, activeLocale, extraProperties = {}; /** * Loads a script asynchronously * * @param {string} url The url for the script @ @param {function} callback A function to be called once the script is loaded */ function loadScript(url, callback, errorCallback, $timeout) { var script = document.createElement('script'), element = nodeToAppend ? nodeToAppend : document.getElementsByTagName('body')[0], removed = false; script.type = 'text/javascript'; if (script.readyState) { // IE script.onreadystatechange = function () { if (script.readyState === 'complete' || script.readyState === 'loaded') { script.onreadystatechange = null; $timeout( function () { if (removed) return; removed = true; if (script.parentNode === element) { element.removeChild(script); } callback(); }, 30, false); } }; } else { // Others script.onload = function () { if (removed) return; removed = true; if (script.parentNode === element) { element.removeChild(script); } callback(); }; script.onerror = function () { if (removed) return; removed = true; if (script.parentNode === element) { element.removeChild(script); } errorCallback(); }; } script.src = url; script.async = true; element.appendChild(script); } /** * Loads a locale and replaces the properties from the current locale with the new locale information * * @param {string} localeUrl The path to the new locale * @param {Object} $locale The locale at the curent scope * @param {string} localeId The locale id to load * @param {Object} $rootScope The application $rootScope * @param {Object} $q The application $q * @param {Object} localeCache The current locale cache * @param {Object} $timeout The application $timeout */ function loadLocale(localeUrl, $locale, localeId, $rootScope, $q, localeCache, $timeout) { function overrideValues(oldObject, newObject) { if (activeLocale !== localeId) { return; } angular.forEach(oldObject, function(value, key) { if (!newObject[key]) { delete oldObject[key]; } else if (angular.isArray(newObject[key])) { oldObject[key].length = newObject[key].length; } }); angular.forEach(newObject, function(value, key) { if (angular.isArray(newObject[key]) || angular.isObject(newObject[key])) { if (!oldObject[key]) { oldObject[key] = angular.isArray(newObject[key]) ? [] : {}; } overrideValues(oldObject[key], newObject[key]); } else { oldObject[key] = newObject[key]; } }); } if (promiseCache[localeId]) { activeLocale = localeId; return promiseCache[localeId]; } var cachedLocale, deferred = $q.defer(); if (localeId === activeLocale) { deferred.resolve($locale); } else if ((cachedLocale = localeCache.get(localeId))) { activeLocale = localeId; $rootScope.$evalAsync(function() { overrideValues($locale, cachedLocale); storage[storagePut](storageKey, localeId); $rootScope.$broadcast('$localeChangeSuccess', localeId, $locale); deferred.resolve($locale); }); } else { activeLocale = localeId; promiseCache[localeId] = deferred.promise; loadScript(localeUrl, function() { // Create a new injector with the new locale var localInjector = angular.injector(['ngLocale']), externalLocale = localInjector.get('$locale'); overrideValues($locale, externalLocale); localeCache.put(localeId, externalLocale); delete promiseCache[localeId]; $rootScope.$applyAsync(function() { storage[storagePut](storageKey, localeId); $rootScope.$broadcast('$localeChangeSuccess', localeId, $locale); deferred.resolve($locale); }); }, function() { delete promiseCache[localeId]; $rootScope.$applyAsync(function() { if (activeLocale === localeId) { activeLocale = $locale.id; } $rootScope.$broadcast('$localeChangeError', localeId); deferred.reject(localeId); }); }, $timeout); } return deferred.promise; } this.localeLocationPattern = function(value) { if (value) { localeLocationPattern = value; return this; } else { return localeLocationPattern; } }; this.appendScriptTo = function(nodeElement) { nodeToAppend = nodeElement; }; this.useStorage = function(storageName) { storageFactory = storageName; storageGet = 'get'; storagePut = 'put'; }; this.useCookieStorage = function() { if (angular.version.minor < 7) { this.useStorage('$cookieStore'); } else { this.useStorage('$cookies'); storageGet = 'getObject'; storagePut = 'putObject'; } }; this.defaultLocale = function(value) { defaultLocale = value; }; this.storageKey = function(value) { if (value) { storageKey = value; return this; } else { return storageKey; } }; this.addLocalePatternValue = function(key, value) { extraProperties[key] = value; }; this.$get = ['$rootScope', '$injector', '$interpolate', '$locale', '$q', 'tmhDynamicLocaleCache', '$timeout', function($rootScope, $injector, interpolate, locale, $q, tmhDynamicLocaleCache, $timeout) { var localeLocation = interpolate(localeLocationPattern); storage = $injector.get(storageFactory); $rootScope.$evalAsync(function() { var initialLocale; if ((initialLocale = (storage[storageGet](storageKey) || defaultLocale))) { loadLocaleFn(initialLocale); } }); return { /** * @ngdoc method * @description * @param {string} value Sets the locale to the new locale. Changing the locale will trigger * a background task that will retrieve the new locale and configure the current $locale * instance with the information from the new locale */ set: loadLocaleFn, /** * @ngdoc method * @description Returns the configured locale */ get: function() { return activeLocale; } }; function loadLocaleFn(localeId) { var baseProperties = {locale: localeId, angularVersion: angular.version.full}; return loadLocale(localeLocation(angular.extend({}, extraProperties, baseProperties)), locale, localeId, $rootScope, $q, tmhDynamicLocaleCache, $timeout); } }]; }]).provider('tmhDynamicLocaleCache', function() { this.$get = ['$cacheFactory', function($cacheFactory) { return $cacheFactory('tmh.dynamicLocales'); }]; }).provider('tmhDynamicLocaleStorageCache', function() { this.$get = ['$cacheFactory', function($cacheFactory) { return $cacheFactory('tmh.dynamicLocales.store'); }]; }).run(['tmhDynamicLocale', angular.noop]); return 'tmh.dynamicLocale'; }));