/*! * angular-translate - v2.18.1 - 2018-05-19 * * Copyright (c) 2018 The angular-translate team, Pascal Precht; Licensed MIT */ //!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(e){"use strict";var n=e.storageKey(),a=e.storage(),t=function(){var t=e.preferredLanguage();angular.isString(t)?e.use(t):a.put(n,e.use())};t.displayName="fallbackFromIncorrectStorageValue",a?a.get(n)?e.use(a.get(n)).catch(t):t():angular.isString(e.preferredLanguage())&&e.use(e.preferredLanguage())}function e(t,r,e,i){"use strict";var z,c,T,x,F,I,_,n,V,R,D,K,U,M,H,G,q={},Y=[],B=t,J=[],Q="translate-cloak",W=!1,X=!1,Z=".",tt=!1,et=!1,nt=0,at=!0,a="default",s={default:function(t){return(t||"").split("-").join("_")},java:function(t){var e=(t||"").split("-").join("_"),n=e.split("_");return 1");return e.text(t),e.html()},i=function(t){if(!n)throw new Error("pascalprecht.translate.$translateSanitization: Error cannot find $sanitize service. Either include the ngSanitize module (https://docs.angularjs.org/api/ngSanitize) or use a sanitization strategy which does not depend on $sanitize, such as 'escape'.");return n(t)},s=function(t){if(!a)throw new Error("pascalprecht.translate.$translateSanitization: Error cannot find $sce service.");return a.trustAsHtml(t)},o=function(t,n,a){if(angular.isDate(t))return t;if(angular.isObject(t)){var r=angular.isArray(t)?[]:{};if(a){if(-1 *
sanitize
*
Sanitizes HTML in the translation text using $sanitize
*
escape
*
Escapes HTML in the translation
*
sanitizeParameters
*
Sanitizes HTML in the values of the interpolation parameters using $sanitize
*
escapeParameters
*
Escapes HTML in the values of the interpolation parameters
*
escaped
*
Support legacy strategy name 'escaped' for backwards compatibility (will be removed in 3.0)
* * */ strategies = { sanitize: function (value, mode/*, context*/) { if (mode === 'text') { value = htmlSanitizeValue(value); } return value; }, escape: function (value, mode/*, context*/) { if (mode === 'text') { value = htmlEscapeValue(value); } return value; }, sanitizeParameters: function (value, mode/*, context*/) { if (mode === 'params') { value = mapInterpolationParameters(value, htmlSanitizeValue); } return value; }, escapeParameters: function (value, mode/*, context*/) { if (mode === 'params') { value = mapInterpolationParameters(value, htmlEscapeValue); } return value; }, sce: function (value, mode, context) { if (mode === 'text') { value = htmlTrustValue(value); } else if (mode === 'params') { if (context !== 'filter') { // do html escape in filter context #1101 value = mapInterpolationParameters(value, htmlEscapeValue); } } return value; }, sceParameters: function (value, mode/*, context*/) { if (mode === 'params') { value = mapInterpolationParameters(value, htmlTrustValue); } return value; } }; // Support legacy strategy name 'escaped' for backwards compatibility. // TODO should be removed in 3.0 strategies.escaped = strategies.escapeParameters; /** * @ngdoc function * @name pascalprecht.translate.$translateSanitizationProvider#addStrategy * @methodOf pascalprecht.translate.$translateSanitizationProvider * * @description * Adds a sanitization strategy to the list of known strategies. * * @param {string} strategyName - unique key for a strategy * @param {StrategyFunction} strategyFunction - strategy function * @returns {object} this */ this.addStrategy = function (strategyName, strategyFunction) { strategies[strategyName] = strategyFunction; return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateSanitizationProvider#removeStrategy * @methodOf pascalprecht.translate.$translateSanitizationProvider * * @description * Removes a sanitization strategy from the list of known strategies. * * @param {string} strategyName - unique key for a strategy * @returns {object} this */ this.removeStrategy = function (strategyName) { delete strategies[strategyName]; return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateSanitizationProvider#useStrategy * @methodOf pascalprecht.translate.$translateSanitizationProvider * * @description * Selects a sanitization strategy. When an array is provided the strategies will be executed in order. * * @param {string|StrategyFunction|array} strategy The sanitization strategy / strategies which should be used. Either a name of an existing strategy, a custom strategy function, or an array consisting of multiple names and / or custom functions. * @returns {object} this */ this.useStrategy = function (strategy) { hasConfiguredStrategy = true; currentStrategy = strategy; return this; }; /** * @ngdoc object * @name pascalprecht.translate.$translateSanitization * @requires $injector * @requires $log * * @description * Sanitizes interpolation parameters and translated texts. * */ this.$get = ['$injector', '$log', function ($injector, $log) { var cachedStrategyMap = {}; var applyStrategies = function (value, mode, context, selectedStrategies) { angular.forEach(selectedStrategies, function (selectedStrategy) { if (angular.isFunction(selectedStrategy)) { value = selectedStrategy(value, mode, context); } else if (angular.isFunction(strategies[selectedStrategy])) { value = strategies[selectedStrategy](value, mode, context); } else if (angular.isString(strategies[selectedStrategy])) { if (!cachedStrategyMap[strategies[selectedStrategy]]) { try { cachedStrategyMap[strategies[selectedStrategy]] = $injector.get(strategies[selectedStrategy]); } catch (e) { cachedStrategyMap[strategies[selectedStrategy]] = function() {}; throw new Error('pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: \'' + selectedStrategy + '\''); } } value = cachedStrategyMap[strategies[selectedStrategy]](value, mode, context); } else { throw new Error('pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: \'' + selectedStrategy + '\''); } }); return value; }; // TODO: should be removed in 3.0 var showNoStrategyConfiguredWarning = function () { if (!hasConfiguredStrategy && !hasShownNoStrategyConfiguredWarning) { $log.warn('pascalprecht.translate.$translateSanitization: No sanitization strategy has been configured. This can have serious security implications. See http://angular-translate.github.io/docs/#/guide/19_security for details.'); hasShownNoStrategyConfiguredWarning = true; } }; if ($injector.has('$sanitize')) { $sanitize = $injector.get('$sanitize'); } if ($injector.has('$sce')) { $sce = $injector.get('$sce'); } return { /** * @ngdoc function * @name pascalprecht.translate.$translateSanitization#useStrategy * @methodOf pascalprecht.translate.$translateSanitization * * @description * Selects a sanitization strategy. When an array is provided the strategies will be executed in order. * * @param {string|StrategyFunction|array} strategy The sanitization strategy / strategies which should be used. Either a name of an existing strategy, a custom strategy function, or an array consisting of multiple names and / or custom functions. */ useStrategy: (function (self) { return function (strategy) { self.useStrategy(strategy); }; })(this), /** * @ngdoc function * @name pascalprecht.translate.$translateSanitization#sanitize * @methodOf pascalprecht.translate.$translateSanitization * * @description * Sanitizes a value. * * @param {string|object} value The value which should be sanitized. * @param {string} mode The current sanitization mode, either 'params' or 'text'. * @param {string|StrategyFunction|array} [strategy] Optional custom strategy which should be used instead of the currently selected strategy. * @param {string} [context] The context of this call: filter, service. Default is service * @returns {string|object} sanitized value */ sanitize: function (value, mode, strategy, context) { if (!currentStrategy) { showNoStrategyConfiguredWarning(); } if (!strategy && strategy !== null) { strategy = currentStrategy; } if (!strategy) { return value; } if (!context) { context = 'service'; } var selectedStrategies = angular.isArray(strategy) ? strategy : [strategy]; return applyStrategies(value, mode, context, selectedStrategies); } }; }]; var htmlEscapeValue = function (value) { var element = angular.element('
'); element.text(value); // not chainable, see #1044 return element.html(); }; var htmlSanitizeValue = function (value) { if (!$sanitize) { throw new Error('pascalprecht.translate.$translateSanitization: Error cannot find $sanitize service. Either include the ngSanitize module (https://docs.angularjs.org/api/ngSanitize) or use a sanitization strategy which does not depend on $sanitize, such as \'escape\'.'); } return $sanitize(value); }; var htmlTrustValue = function (value) { if (!$sce) { throw new Error('pascalprecht.translate.$translateSanitization: Error cannot find $sce service.'); } return $sce.trustAsHtml(value); }; var mapInterpolationParameters = function (value, iteratee, stack) { if (angular.isDate(value)) { return value; } else if (angular.isObject(value)) { var result = angular.isArray(value) ? [] : {}; if (!stack) { stack = []; } else { if (stack.indexOf(value) > -1) { throw new Error('pascalprecht.translate.$translateSanitization: Error cannot interpolate parameter due recursive object'); } } stack.push(value); angular.forEach(value, function (propertyValue, propertyKey) { /* Skipping function properties. */ if (angular.isFunction(propertyValue)) { return; } result[propertyKey] = mapInterpolationParameters(propertyValue, iteratee, stack); }); stack.splice(-1, 1); // remove last return result; } else if (angular.isNumber(value)) { return value; } else if (value === true || value === false) { return value; } else if (!angular.isUndefined(value) && value !== null) { return iteratee(value); } else { return value; } }; } /** * @ngdoc object * @name pascalprecht.translate.$translateProvider * @description * * $translateProvider allows developers to register translation-tables, asynchronous loaders * and similar to configure translation behavior directly inside of a module. * */ angular.module('pascalprecht.translate') .constant('pascalprechtTranslateOverrider', {}) .provider('$translate', $translate); function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvider, pascalprechtTranslateOverrider) { 'use strict'; var $translationTable = {}, $preferredLanguage, $availableLanguageKeys = [], $languageKeyAliases, $fallbackLanguage, $fallbackWasString, $uses, $nextLang, $storageFactory, $storageKey = $STORAGE_KEY, $storagePrefix, $missingTranslationHandlerFactory, $interpolationFactory, $interpolatorFactories = [], $loaderFactory, $cloakClassName = 'translate-cloak', $loaderOptions, $notFoundIndicatorLeft, $notFoundIndicatorRight, $postCompilingEnabled = false, $forceAsyncReloadEnabled = false, $nestedObjectDelimeter = '.', $isReady = false, $keepContent = false, loaderCache, directivePriority = 0, statefulFilter = true, postProcessFn, uniformLanguageTagResolver = 'default', languageTagResolver = { 'default' : function (tag) { return (tag || '').split('-').join('_'); }, java : function (tag) { var temp = (tag || '').split('-').join('_'); var parts = temp.split('_'); return parts.length > 1 ? (parts[0].toLowerCase() + '_' + parts[1].toUpperCase()) : temp; }, bcp47 : function (tag) { var temp = (tag || '').split('_').join('-'); var parts = temp.split('-'); switch (parts.length) { case 1: // language only parts[0] = parts[0].toLowerCase(); break; case 2: // language-script or language-region parts[0] = parts[0].toLowerCase(); if (parts[1].length === 4) { // parts[1] is script parts[1] = parts[1].charAt(0).toUpperCase() + parts[1].slice(1).toLowerCase(); } else { // parts[1] is region parts[1] = parts[1].toUpperCase(); } break; case 3: // language-script-region parts[0] = parts[0].toLowerCase(); parts[1] = parts[1].charAt(0).toUpperCase() + parts[1].slice(1).toLowerCase(); parts[2] = parts[2].toUpperCase(); break; default: return temp; } return parts.join('-'); }, 'iso639-1' : function (tag) { var temp = (tag || '').split('_').join('-'); var parts = temp.split('-'); return parts[0].toLowerCase(); } }; var version = '2.18.1'; // tries to determine the browsers language var getFirstBrowserLanguage = function () { // internal purpose only if (angular.isFunction(pascalprechtTranslateOverrider.getLocale)) { return pascalprechtTranslateOverrider.getLocale(); } var nav = $windowProvider.$get().navigator, browserLanguagePropertyKeys = ['language', 'browserLanguage', 'systemLanguage', 'userLanguage'], i, language; // support for HTML 5.1 "navigator.languages" if (angular.isArray(nav.languages)) { for (i = 0; i < nav.languages.length; i++) { language = nav.languages[i]; if (language && language.length) { return language; } } } // support for other well known properties in browsers for (i = 0; i < browserLanguagePropertyKeys.length; i++) { language = nav[browserLanguagePropertyKeys[i]]; if (language && language.length) { return language; } } return null; }; getFirstBrowserLanguage.displayName = 'angular-translate/service: getFirstBrowserLanguage'; // tries to determine the browsers locale var getLocale = function () { var locale = getFirstBrowserLanguage() || ''; if (languageTagResolver[uniformLanguageTagResolver]) { locale = languageTagResolver[uniformLanguageTagResolver](locale); } return locale; }; getLocale.displayName = 'angular-translate/service: getLocale'; /** * @name indexOf * @private * * @description * indexOf polyfill. Kinda sorta. * * @param {array} array Array to search in. * @param {string} searchElement Element to search for. * * @returns {int} Index of search element. */ var indexOf = function (array, searchElement) { for (var i = 0, len = array.length; i < len; i++) { if (array[i] === searchElement) { return i; } } return -1; }; /** * @name trim * @private * * @description * trim polyfill * * @returns {string} The string stripped of whitespace from both ends */ var trim = function () { return this.toString().replace(/^\s+|\s+$/g, ''); }; /** * @name lowercase * @private * * @description * Return the lowercase string only if the type is string * * @returns {string} The string all in lowercase */ var lowercase = function (string) { return angular.isString(string) ? string.toLowerCase() : string; }; var negotiateLocale = function (preferred) { if (!preferred) { return; } var avail = [], locale = lowercase(preferred), i = 0, n = $availableLanguageKeys.length; for (; i < n; i++) { avail.push(lowercase($availableLanguageKeys[i])); } // Check for an exact match in our list of available keys i = indexOf(avail, locale); if (i > -1) { return $availableLanguageKeys[i]; } if ($languageKeyAliases) { var alias; for (var langKeyAlias in $languageKeyAliases) { if ($languageKeyAliases.hasOwnProperty(langKeyAlias)) { var hasWildcardKey = false; var hasExactKey = Object.prototype.hasOwnProperty.call($languageKeyAliases, langKeyAlias) && lowercase(langKeyAlias) === lowercase(preferred); if (langKeyAlias.slice(-1) === '*') { hasWildcardKey = lowercase(langKeyAlias.slice(0, -1)) === lowercase(preferred.slice(0, langKeyAlias.length - 1)); } if (hasExactKey || hasWildcardKey) { alias = $languageKeyAliases[langKeyAlias]; if (indexOf(avail, lowercase(alias)) > -1) { return alias; } } } } } // Check for a language code without region var parts = preferred.split('_'); if (parts.length > 1 && indexOf(avail, lowercase(parts[0])) > -1) { return parts[0]; } // If everything fails, return undefined. return; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#translations * @methodOf pascalprecht.translate.$translateProvider * * @description * Registers a new translation table for specific language key. * * To register a translation table for specific language, pass a defined language * key as first parameter. * *
   *  // register translation table for language: 'de_DE'
   *  $translateProvider.translations('de_DE', {
   *    'GREETING': 'Hallo Welt!'
   *  });
   *
   *  // register another one
   *  $translateProvider.translations('en_US', {
   *    'GREETING': 'Hello world!'
   *  });
   * 
* * When registering multiple translation tables for for the same language key, * the actual translation table gets extended. This allows you to define module * specific translation which only get added, once a specific module is loaded in * your app. * * Invoking this method with no arguments returns the translation table which was * registered with no language key. Invoking it with a language key returns the * related translation table. * * @param {string} langKey A language key. * @param {object} translationTable A plain old JavaScript object that represents a translation table. * */ var translations = function (langKey, translationTable) { if (!langKey && !translationTable) { return $translationTable; } if (langKey && !translationTable) { if (angular.isString(langKey)) { return $translationTable[langKey]; } } else { if (!angular.isObject($translationTable[langKey])) { $translationTable[langKey] = {}; } angular.extend($translationTable[langKey], flatObject(translationTable)); } return this; }; this.translations = translations; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#cloakClassName * @methodOf pascalprecht.translate.$translateProvider * * @description * * Let's you change the class name for `translate-cloak` directive. * Default class name is `translate-cloak`. * * @param {string} name translate-cloak class name */ this.cloakClassName = function (name) { if (!name) { return $cloakClassName; } $cloakClassName = name; return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#nestedObjectDelimeter * @methodOf pascalprecht.translate.$translateProvider * * @description * * Let's you change the delimiter for namespaced translations. * Default delimiter is `.`. * * @param {string} delimiter namespace separator */ this.nestedObjectDelimeter = function (delimiter) { if (!delimiter) { return $nestedObjectDelimeter; } $nestedObjectDelimeter = delimiter; return this; }; /** * @name flatObject * @private * * @description * Flats an object. This function is used to flatten given translation data with * namespaces, so they are later accessible via dot notation. */ var flatObject = function (data, path, result, prevKey) { var key, keyWithPath, keyWithShortPath, val; if (!path) { path = []; } if (!result) { result = {}; } for (key in data) { if (!Object.prototype.hasOwnProperty.call(data, key)) { continue; } val = data[key]; if (angular.isObject(val)) { flatObject(val, path.concat(key), result, key); } else { keyWithPath = path.length ? ('' + path.join($nestedObjectDelimeter) + $nestedObjectDelimeter + key) : key; if (path.length && key === prevKey) { // Create shortcut path (foo.bar == foo.bar.bar) keyWithShortPath = '' + path.join($nestedObjectDelimeter); // Link it to original path result[keyWithShortPath] = '@:' + keyWithPath; } result[keyWithPath] = val; } } return result; }; flatObject.displayName = 'flatObject'; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#addInterpolation * @methodOf pascalprecht.translate.$translateProvider * * @description * Adds interpolation services to angular-translate, so it can manage them. * * @param {object} factory Interpolation service factory */ this.addInterpolation = function (factory) { $interpolatorFactories.push(factory); return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useMessageFormatInterpolation * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells angular-translate to use interpolation functionality of messageformat.js. * This is useful when having high level pluralization and gender selection. */ this.useMessageFormatInterpolation = function () { return this.useInterpolation('$translateMessageFormatInterpolation'); }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useInterpolation * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells angular-translate which interpolation style to use as default, application-wide. * Simply pass a factory/service name. The interpolation service has to implement * the correct interface. * * @param {string} factory Interpolation service name. */ this.useInterpolation = function (factory) { $interpolationFactory = factory; return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useSanitizeStrategy * @methodOf pascalprecht.translate.$translateProvider * * @description * Simply sets a sanitation strategy type. * * @param {string} value Strategy type. */ this.useSanitizeValueStrategy = function (value) { $translateSanitizationProvider.useStrategy(value); return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#preferredLanguage * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells the module which of the registered translation tables to use for translation * at initial startup by passing a language key. Similar to `$translateProvider#use` * only that it says which language to **prefer**. * It is recommended to call this after {@link pascalprecht.translate.$translate#fallbackLanguage fallbackLanguage()}. * * @param {string} langKey A language key. */ this.preferredLanguage = function (langKey) { if (langKey) { setupPreferredLanguage(langKey); return this; } return $preferredLanguage; }; var setupPreferredLanguage = function (langKey) { if (langKey) { $preferredLanguage = langKey; } return $preferredLanguage; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicator * @methodOf pascalprecht.translate.$translateProvider * * @description * Sets an indicator which is used when a translation isn't found. E.g. when * setting the indicator as 'X' and one tries to translate a translation id * called `NOT_FOUND`, this will result in `X NOT_FOUND X`. * * Internally this methods sets a left indicator and a right indicator using * `$translateProvider.translationNotFoundIndicatorLeft()` and * `$translateProvider.translationNotFoundIndicatorRight()`. * * **Note**: These methods automatically add a whitespace between the indicators * and the translation id. * * @param {string} indicator An indicator, could be any string. */ this.translationNotFoundIndicator = function (indicator) { this.translationNotFoundIndicatorLeft(indicator); this.translationNotFoundIndicatorRight(indicator); return this; }; /** * ngdoc function * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicatorLeft * @methodOf pascalprecht.translate.$translateProvider * * @description * Sets an indicator which is used when a translation isn't found left to the * translation id. * * @param {string} indicator An indicator. */ this.translationNotFoundIndicatorLeft = function (indicator) { if (!indicator) { return $notFoundIndicatorLeft; } $notFoundIndicatorLeft = indicator; return this; }; /** * ngdoc function * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicatorLeft * @methodOf pascalprecht.translate.$translateProvider * * @description * Sets an indicator which is used when a translation isn't found right to the * translation id. * * @param {string} indicator An indicator. */ this.translationNotFoundIndicatorRight = function (indicator) { if (!indicator) { return $notFoundIndicatorRight; } $notFoundIndicatorRight = indicator; return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#fallbackLanguage * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells the module which of the registered translation tables to use when missing translations * at initial startup by passing a language key. Similar to `$translateProvider#use` * only that it says which language to **fallback**. * * @param {string||array} langKey A language key. * */ this.fallbackLanguage = function (langKey) { fallbackStack(langKey); return this; }; var fallbackStack = function (langKey) { if (langKey) { if (angular.isString(langKey)) { $fallbackWasString = true; $fallbackLanguage = [langKey]; } else if (angular.isArray(langKey)) { $fallbackWasString = false; $fallbackLanguage = langKey; } if (angular.isString($preferredLanguage) && indexOf($fallbackLanguage, $preferredLanguage) < 0) { $fallbackLanguage.push($preferredLanguage); } return this; } else { if ($fallbackWasString) { return $fallbackLanguage[0]; } else { return $fallbackLanguage; } } }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#use * @methodOf pascalprecht.translate.$translateProvider * * @description * Set which translation table to use for translation by given language key. When * trying to 'use' a language which isn't provided, it'll throw an error. * * You actually don't have to use this method since `$translateProvider#preferredLanguage` * does the job too. * * @param {string} langKey A language key. */ this.use = function (langKey) { if (langKey) { if (!$translationTable[langKey] && (!$loaderFactory)) { // only throw an error, when not loading translation data asynchronously throw new Error('$translateProvider couldn\'t find translationTable for langKey: \'' + langKey + '\''); } $uses = langKey; return this; } return $uses; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#resolveClientLocale * @methodOf pascalprecht.translate.$translateProvider * * @description * This returns the current browser/client's language key. The result is processed with the configured uniform tag resolver. * * @returns {string} the current client/browser language key */ this.resolveClientLocale = function () { return getLocale(); }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#storageKey * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells the module which key must represent the choosed language by a user in the storage. * * @param {string} key A key for the storage. */ var storageKey = function (key) { if (!key) { if ($storagePrefix) { return $storagePrefix + $storageKey; } return $storageKey; } $storageKey = key; return this; }; this.storageKey = storageKey; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useUrlLoader * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells angular-translate to use `$translateUrlLoader` extension service as loader. * * @param {string} url Url * @param {Object=} options Optional configuration object */ this.useUrlLoader = function (url, options) { return this.useLoader('$translateUrlLoader', angular.extend({url : url}, options)); }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useStaticFilesLoader * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells angular-translate to use `$translateStaticFilesLoader` extension service as loader. * * @param {Object=} options Optional configuration object */ this.useStaticFilesLoader = function (options) { return this.useLoader('$translateStaticFilesLoader', options); }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useLoader * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells angular-translate to use any other service as loader. * * @param {string} loaderFactory Factory name to use * @param {Object=} options Optional configuration object */ this.useLoader = function (loaderFactory, options) { $loaderFactory = loaderFactory; $loaderOptions = options || {}; return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useLocalStorage * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells angular-translate to use `$translateLocalStorage` service as storage layer. * */ this.useLocalStorage = function () { return this.useStorage('$translateLocalStorage'); }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useCookieStorage * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells angular-translate to use `$translateCookieStorage` service as storage layer. */ this.useCookieStorage = function () { return this.useStorage('$translateCookieStorage'); }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useStorage * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells angular-translate to use custom service as storage layer. */ this.useStorage = function (storageFactory) { $storageFactory = storageFactory; return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#storagePrefix * @methodOf pascalprecht.translate.$translateProvider * * @description * Sets prefix for storage key. * * @param {string} prefix Storage key prefix */ this.storagePrefix = function (prefix) { if (!prefix) { return prefix; } $storagePrefix = prefix; return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useMissingTranslationHandlerLog * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells angular-translate to use built-in log handler when trying to translate * a translation Id which doesn't exist. * * This is actually a shortcut method for `useMissingTranslationHandler()`. * */ this.useMissingTranslationHandlerLog = function () { return this.useMissingTranslationHandler('$translateMissingTranslationHandlerLog'); }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useMissingTranslationHandler * @methodOf pascalprecht.translate.$translateProvider * * @description * Expects a factory name which later gets instantiated with `$injector`. * This method can be used to tell angular-translate to use a custom * missingTranslationHandler. Just build a factory which returns a function * and expects a translation id as argument. * * Example: *
   *  app.config(function ($translateProvider) {
   *    $translateProvider.useMissingTranslationHandler('customHandler');
   *  });
   *
   *  app.factory('customHandler', function (dep1, dep2) {
   *    return function (translationId) {
   *      // something with translationId and dep1 and dep2
   *    };
   *  });
   * 
* * @param {string} factory Factory name */ this.useMissingTranslationHandler = function (factory) { $missingTranslationHandlerFactory = factory; return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#usePostCompiling * @methodOf pascalprecht.translate.$translateProvider * * @description * If post compiling is enabled, all translated values will be processed * again with AngularJS' $compile. * * Example: *
   *  app.config(function ($translateProvider) {
   *    $translateProvider.usePostCompiling(true);
   *  });
   * 
* * @param {string} factory Factory name */ this.usePostCompiling = function (value) { $postCompilingEnabled = !(!value); return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#forceAsyncReload * @methodOf pascalprecht.translate.$translateProvider * * @description * If force async reload is enabled, async loader will always be called * even if $translationTable already contains the language key, adding * possible new entries to the $translationTable. * * Example: *
   *  app.config(function ($translateProvider) {
   *    $translateProvider.forceAsyncReload(true);
   *  });
   * 
* * @param {boolean} value - valid values are true or false */ this.forceAsyncReload = function (value) { $forceAsyncReloadEnabled = !(!value); return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#uniformLanguageTag * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells angular-translate which language tag should be used as a result when determining * the current browser language. * * This setting must be set before invoking {@link pascalprecht.translate.$translateProvider#methods_determinePreferredLanguage determinePreferredLanguage()}. * *
   * $translateProvider
   *   .uniformLanguageTag('bcp47')
   *   .determinePreferredLanguage()
   * 
* * The resolver currently supports: * * default * (traditionally: hyphens will be converted into underscores, i.e. en-US => en_US) * en-US => en_US * en_US => en_US * en-us => en_us * * java * like default, but the second part will be always in uppercase * en-US => en_US * en_US => en_US * en-us => en_US * * BCP 47 (RFC 4646 & 4647) * EN => en * en-US => en-US * en_US => en-US * en-us => en-US * sr-latn => sr-Latn * sr-latn-rs => sr-Latn-RS * * See also: * * http://en.wikipedia.org/wiki/IETF_language_tag * * http://www.w3.org/International/core/langtags/ * * http://tools.ietf.org/html/bcp47 * * @param {string|object} options - options (or standard) * @param {string} options.standard - valid values are 'default', 'bcp47', 'java' */ this.uniformLanguageTag = function (options) { if (!options) { options = {}; } else if (angular.isString(options)) { options = { standard : options }; } uniformLanguageTagResolver = options.standard; return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#determinePreferredLanguage * @methodOf pascalprecht.translate.$translateProvider * * @description * Tells angular-translate to try to determine on its own which language key * to set as preferred language. When `fn` is given, angular-translate uses it * to determine a language key, otherwise it uses the built-in `getLocale()` * method. * * The `getLocale()` returns a language key in the format `[lang]_[country]` or * `[lang]` depending on what the browser provides. * * Use this method at your own risk, since not all browsers return a valid * locale (see {@link pascalprecht.translate.$translateProvider#methods_uniformLanguageTag uniformLanguageTag()}). * * @param {Function=} fn Function to determine a browser's locale */ this.determinePreferredLanguage = function (fn) { var locale = (fn && angular.isFunction(fn)) ? fn() : getLocale(); if (!$availableLanguageKeys.length) { $preferredLanguage = locale; } else { $preferredLanguage = negotiateLocale(locale) || locale; } return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#registerAvailableLanguageKeys * @methodOf pascalprecht.translate.$translateProvider * * @description * Registers a set of language keys the app will work with. Use this method in * combination with * {@link pascalprecht.translate.$translateProvider#determinePreferredLanguage determinePreferredLanguage}. * When available languages keys are registered, angular-translate * tries to find the best fitting language key depending on the browsers locale, * considering your language key convention. * * @param {object} languageKeys Array of language keys the your app will use * @param {object=} aliases Alias map. */ this.registerAvailableLanguageKeys = function (languageKeys, aliases) { if (languageKeys) { $availableLanguageKeys = languageKeys; if (aliases) { $languageKeyAliases = aliases; } return this; } return $availableLanguageKeys; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#useLoaderCache * @methodOf pascalprecht.translate.$translateProvider * * @description * Registers a cache for internal $http based loaders. * {@link pascalprecht.translate.$translationCache $translationCache}. * When false the cache will be disabled (default). When true or undefined * the cache will be a default (see $cacheFactory). When an object it will * be treat as a cache object itself: the usage is $http({cache: cache}) * * @param {object} cache boolean, string or cache-object */ this.useLoaderCache = function (cache) { if (cache === false) { // disable cache loaderCache = undefined; } else if (cache === true) { // enable cache using AJS defaults loaderCache = true; } else if (typeof(cache) === 'undefined') { // enable cache using default loaderCache = '$translationCache'; } else if (cache) { // enable cache using given one (see $cacheFactory) loaderCache = cache; } return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#directivePriority * @methodOf pascalprecht.translate.$translateProvider * * @description * Sets the default priority of the translate directive. The standard value is `0`. * Calling this function without an argument will return the current value. * * @param {number} priority for the translate-directive */ this.directivePriority = function (priority) { if (priority === undefined) { // getter return directivePriority; } else { // setter with chaining directivePriority = priority; return this; } }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#statefulFilter * @methodOf pascalprecht.translate.$translateProvider * * @description * Since AngularJS 1.3, filters which are not stateless (depending at the scope) * have to explicit define this behavior. * Sets whether the translate filter should be stateful or stateless. The standard value is `true` * meaning being stateful. * Calling this function without an argument will return the current value. * * @param {boolean} state - defines the state of the filter */ this.statefulFilter = function (state) { if (state === undefined) { // getter return statefulFilter; } else { // setter with chaining statefulFilter = state; return this; } }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#postProcess * @methodOf pascalprecht.translate.$translateProvider * * @description * The post processor will be intercept right after the translation result. It can modify the result. * * @param {object} fn Function or service name (string) to be called after the translation value has been set / resolved. The function itself will enrich every value being processed and then continue the normal resolver process */ this.postProcess = function (fn) { if (fn) { postProcessFn = fn; } else { postProcessFn = undefined; } return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateProvider#keepContent * @methodOf pascalprecht.translate.$translateProvider * * @description * If keepContent is set to true than translate directive will always use innerHTML * as a default translation * * Example: *
   *  app.config(function ($translateProvider) {
   *    $translateProvider.keepContent(true);
   *  });
   * 
* * @param {boolean} value - valid values are true or false */ this.keepContent = function (value) { $keepContent = !(!value); return this; }; /** * @ngdoc object * @name pascalprecht.translate.$translate * @requires $interpolate * @requires $log * @requires $rootScope * @requires $q * * @description * The `$translate` service is the actual core of angular-translate. It expects a translation id * and optional interpolate parameters to translate contents. * *
   *  $translate('HEADLINE_TEXT').then(function (translation) {
   *    $scope.translatedText = translation;
   *  });
   * 
* * @param {string|array} translationId A token which represents a translation id * This can be optionally an array of translation ids which * results that the function returns an object where each key * is the translation id and the value the translation. * @param {object=} [interpolateParams={}] An object hash for dynamic values * @param {string=} [interpolationId=undefined] The id of the interpolation to use (use default unless set via useInterpolation()) * @param {string=} [defaultTranslationText=undefined] the optional default translation text that is written as * as default text in case it is not found in any configured language * @param {string=} [forceLanguage=false] A language to be used instead of the current language * @param {string=} [sanitizeStrategy=undefined] force sanitize strategy for this call instead of using the configured one (use default unless set) * @returns {object} promise */ this.$get = ['$log', '$injector', '$rootScope', '$q', function ($log, $injector, $rootScope, $q) { var Storage, defaultInterpolator = $injector.get($interpolationFactory || '$translateDefaultInterpolation'), pendingLoader = false, interpolatorHashMap = {}, langPromises = {}, fallbackIndex, startFallbackIteration; var $translate = function (translationId, interpolateParams, interpolationId, defaultTranslationText, forceLanguage, sanitizeStrategy) { if (!$uses && $preferredLanguage) { $uses = $preferredLanguage; } var uses = (forceLanguage && forceLanguage !== $uses) ? // we don't want to re-negotiate $uses (negotiateLocale(forceLanguage) || forceLanguage) : $uses; // Check forceLanguage is present if (forceLanguage) { loadTranslationsIfMissing(forceLanguage); } // Duck detection: If the first argument is an array, a bunch of translations was requested. // The result is an object. if (angular.isArray(translationId)) { // Inspired by Q.allSettled by Kris Kowal // https://github.com/kriskowal/q/blob/b0fa72980717dc202ffc3cbf03b936e10ebbb9d7/q.js#L1553-1563 // This transforms all promises regardless resolved or rejected var translateAll = function (translationIds) { var results = {}; // storing the actual results var promises = []; // promises to wait for // Wraps the promise a) being always resolved and b) storing the link id->value var translate = function (translationId) { var deferred = $q.defer(); var regardless = function (value) { results[translationId] = value; deferred.resolve([translationId, value]); }; // we don't care whether the promise was resolved or rejected; just store the values $translate(translationId, interpolateParams, interpolationId, defaultTranslationText, forceLanguage, sanitizeStrategy).then(regardless, regardless); return deferred.promise; }; for (var i = 0, c = translationIds.length; i < c; i++) { promises.push(translate(translationIds[i])); } // wait for all (including storing to results) return $q.all(promises).then(function () { // return the results return results; }); }; return translateAll(translationId); } var deferred = $q.defer(); // trim off any whitespace if (translationId) { translationId = trim.apply(translationId); } var promiseToWaitFor = (function () { var promise = langPromises[uses] || langPromises[$preferredLanguage]; fallbackIndex = 0; if ($storageFactory && !promise) { // looks like there's no pending promise for $preferredLanguage or // $uses. Maybe there's one pending for a language that comes from // storage. var langKey = Storage.get($storageKey); promise = langPromises[langKey]; if ($fallbackLanguage && $fallbackLanguage.length) { var index = indexOf($fallbackLanguage, langKey); // maybe the language from storage is also defined as fallback language // we increase the fallback language index to not search in that language // as fallback, since it's probably the first used language // in that case the index starts after the first element fallbackIndex = (index === 0) ? 1 : 0; // but we can make sure to ALWAYS fallback to preferred language at least if (indexOf($fallbackLanguage, $preferredLanguage) < 0) { $fallbackLanguage.push($preferredLanguage); } } } return promise; }()); if (!promiseToWaitFor) { // no promise to wait for? okay. Then there's no loader registered // nor is a one pending for language that comes from storage. // We can just translate. determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText, uses, sanitizeStrategy).then(deferred.resolve, deferred.reject); } else { var promiseResolved = function () { // $uses may have changed while waiting if (!forceLanguage) { uses = $uses; } determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText, uses, sanitizeStrategy).then(deferred.resolve, deferred.reject); }; promiseResolved.displayName = 'promiseResolved'; promiseToWaitFor['finally'](promiseResolved)['catch'](angular.noop); // we don't care about errors here, already handled } return deferred.promise; }; /** * @name applyNotFoundIndicators * @private * * @description * Applies not fount indicators to given translation id, if needed. * This function gets only executed, if a translation id doesn't exist, * which is why a translation id is expected as argument. * * @param {string} translationId Translation id. * @returns {string} Same as given translation id but applied with not found * indicators. */ var applyNotFoundIndicators = function (translationId) { // applying notFoundIndicators if ($notFoundIndicatorLeft) { translationId = [$notFoundIndicatorLeft, translationId].join(' '); } if ($notFoundIndicatorRight) { translationId = [translationId, $notFoundIndicatorRight].join(' '); } return translationId; }; /** * @name useLanguage * @private * * @description * Makes actual use of a language by setting a given language key as used * language and informs registered interpolators to also use the given * key as locale. * * @param {string} key Locale key. */ var useLanguage = function (key) { $uses = key; // make sure to store new language key before triggering success event if ($storageFactory) { Storage.put($translate.storageKey(), $uses); } $rootScope.$emit('$translateChangeSuccess', {language : key}); // inform default interpolator defaultInterpolator.setLocale($uses); var eachInterpolator = function (interpolator, id) { interpolatorHashMap[id].setLocale($uses); }; eachInterpolator.displayName = 'eachInterpolatorLocaleSetter'; // inform all others too! angular.forEach(interpolatorHashMap, eachInterpolator); $rootScope.$emit('$translateChangeEnd', {language : key}); }; /** * @name loadAsync * @private * * @description * Kicks off registered async loader using `$injector` and applies existing * loader options. When resolved, it updates translation tables accordingly * or rejects with given language key. * * @param {string} key Language key. * @return {Promise} A promise. */ var loadAsync = function (key) { if (!key) { throw 'No language key specified for loading.'; } var deferred = $q.defer(); $rootScope.$emit('$translateLoadingStart', {language : key}); pendingLoader = true; var cache = loaderCache; if (typeof(cache) === 'string') { // getting on-demand instance of loader cache = $injector.get(cache); } var loaderOptions = angular.extend({}, $loaderOptions, { key : key, $http : angular.extend({}, { cache : cache }, $loaderOptions.$http) }); var onLoaderSuccess = function (data) { var translationTable = {}; $rootScope.$emit('$translateLoadingSuccess', {language : key}); if (angular.isArray(data)) { angular.forEach(data, function (table) { angular.extend(translationTable, flatObject(table)); }); } else { angular.extend(translationTable, flatObject(data)); } pendingLoader = false; deferred.resolve({ key : key, table : translationTable }); $rootScope.$emit('$translateLoadingEnd', {language : key}); }; onLoaderSuccess.displayName = 'onLoaderSuccess'; var onLoaderError = function (key) { $rootScope.$emit('$translateLoadingError', {language : key}); deferred.reject(key); $rootScope.$emit('$translateLoadingEnd', {language : key}); }; onLoaderError.displayName = 'onLoaderError'; $injector.get($loaderFactory)(loaderOptions) .then(onLoaderSuccess, onLoaderError); return deferred.promise; }; if ($storageFactory) { Storage = $injector.get($storageFactory); if (!Storage.get || !Storage.put) { throw new Error('Couldn\'t use storage \'' + $storageFactory + '\', missing get() or put() method!'); } } // if we have additional interpolations that were added via // $translateProvider.addInterpolation(), we have to map'em if ($interpolatorFactories.length) { var eachInterpolationFactory = function (interpolatorFactory) { var interpolator = $injector.get(interpolatorFactory); // setting initial locale for each interpolation service interpolator.setLocale($preferredLanguage || $uses); // make'em recognizable through id interpolatorHashMap[interpolator.getInterpolationIdentifier()] = interpolator; }; eachInterpolationFactory.displayName = 'interpolationFactoryAdder'; angular.forEach($interpolatorFactories, eachInterpolationFactory); } /** * @name getTranslationTable * @private * * @description * Returns a promise that resolves to the translation table * or is rejected if an error occurred. * * @param langKey * @returns {Q.promise} */ var getTranslationTable = function (langKey) { var deferred = $q.defer(); if (Object.prototype.hasOwnProperty.call($translationTable, langKey)) { deferred.resolve($translationTable[langKey]); } else if (langPromises[langKey]) { var onResolve = function (data) { translations(data.key, data.table); deferred.resolve(data.table); }; onResolve.displayName = 'translationTableResolver'; langPromises[langKey].then(onResolve, deferred.reject); } else { deferred.reject(); } return deferred.promise; }; /** * @name getFallbackTranslation * @private * * @description * Returns a promise that will resolve to the translation * or be rejected if no translation was found for the language. * This function is currently only used for fallback language translation. * * @param langKey The language to translate to. * @param translationId * @param interpolateParams * @param Interpolator * @param sanitizeStrategy * @returns {Q.promise} */ var getFallbackTranslation = function (langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy) { var deferred = $q.defer(); var onResolve = function (translationTable) { if (Object.prototype.hasOwnProperty.call(translationTable, translationId) && translationTable[translationId] !== null) { Interpolator.setLocale(langKey); var translation = translationTable[translationId]; if (translation.substr(0, 2) === '@:') { getFallbackTranslation(langKey, translation.substr(2), interpolateParams, Interpolator, sanitizeStrategy) .then(deferred.resolve, deferred.reject); } else { var interpolatedValue = Interpolator.interpolate(translationTable[translationId], interpolateParams, 'service', sanitizeStrategy, translationId); interpolatedValue = applyPostProcessing(translationId, translationTable[translationId], interpolatedValue, interpolateParams, langKey); deferred.resolve(interpolatedValue); } Interpolator.setLocale($uses); } else { deferred.reject(); } }; onResolve.displayName = 'fallbackTranslationResolver'; getTranslationTable(langKey).then(onResolve, deferred.reject); return deferred.promise; }; /** * @name getFallbackTranslationInstant * @private * * @description * Returns a translation * This function is currently only used for fallback language translation. * * @param langKey The language to translate to. * @param translationId * @param interpolateParams * @param Interpolator * @param sanitizeStrategy sanitize strategy override * * @returns {string} translation */ var getFallbackTranslationInstant = function (langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy) { var result, translationTable = $translationTable[langKey]; if (translationTable && Object.prototype.hasOwnProperty.call(translationTable, translationId) && translationTable[translationId] !== null) { Interpolator.setLocale(langKey); result = Interpolator.interpolate(translationTable[translationId], interpolateParams, 'filter', sanitizeStrategy, translationId); result = applyPostProcessing(translationId, translationTable[translationId], result, interpolateParams, langKey, sanitizeStrategy); // workaround for TrustedValueHolderType if (!angular.isString(result) && angular.isFunction(result.$$unwrapTrustedValue)) { var result2 = result.$$unwrapTrustedValue(); if (result2.substr(0, 2) === '@:') { return getFallbackTranslationInstant(langKey, result2.substr(2), interpolateParams, Interpolator, sanitizeStrategy); } } else if (result.substr(0, 2) === '@:') { return getFallbackTranslationInstant(langKey, result.substr(2), interpolateParams, Interpolator, sanitizeStrategy); } Interpolator.setLocale($uses); } return result; }; /** * @name translateByHandler * @private * * Translate by missing translation handler. * * @param translationId * @param interpolateParams * @param defaultTranslationText * @param sanitizeStrategy sanitize strategy override * * @returns translation created by $missingTranslationHandler or translationId is $missingTranslationHandler is * absent */ var translateByHandler = function (translationId, interpolateParams, defaultTranslationText, sanitizeStrategy) { // If we have a handler factory - we might also call it here to determine if it provides // a default text for a translationid that can't be found anywhere in our tables if ($missingTranslationHandlerFactory) { return $injector.get($missingTranslationHandlerFactory)(translationId, $uses, interpolateParams, defaultTranslationText, sanitizeStrategy); } else { return translationId; } }; /** * @name resolveForFallbackLanguage * @private * * Recursive helper function for fallbackTranslation that will sequentially look * for a translation in the fallbackLanguages starting with fallbackLanguageIndex. * * @param fallbackLanguageIndex * @param translationId * @param interpolateParams * @param Interpolator * @param defaultTranslationText * @param sanitizeStrategy * @returns {Q.promise} Promise that will resolve to the translation. */ var resolveForFallbackLanguage = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy) { var deferred = $q.defer(); if (fallbackLanguageIndex < $fallbackLanguage.length) { var langKey = $fallbackLanguage[fallbackLanguageIndex]; getFallbackTranslation(langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy).then( function (data) { deferred.resolve(data); }, function () { // Look in the next fallback language for a translation. // It delays the resolving by passing another promise to resolve. return resolveForFallbackLanguage(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy).then(deferred.resolve, deferred.reject); } ); } else { // No translation found in any fallback language // if a default translation text is set in the directive, then return this as a result if (defaultTranslationText) { deferred.resolve(defaultTranslationText); } else { var missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, defaultTranslationText); // if no default translation is set and an error handler is defined, send it to the handler // and then return the result if it isn't undefined if ($missingTranslationHandlerFactory && missingTranslationHandlerTranslation) { deferred.resolve(missingTranslationHandlerTranslation); } else { deferred.reject(applyNotFoundIndicators(translationId)); } } } return deferred.promise; }; /** * @name resolveForFallbackLanguageInstant * @private * * Recursive helper function for fallbackTranslation that will sequentially look * for a translation in the fallbackLanguages starting with fallbackLanguageIndex. * * @param fallbackLanguageIndex * @param translationId * @param interpolateParams * @param Interpolator * @param sanitizeStrategy * @returns {string} translation */ var resolveForFallbackLanguageInstant = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, sanitizeStrategy) { var result; if (fallbackLanguageIndex < $fallbackLanguage.length) { var langKey = $fallbackLanguage[fallbackLanguageIndex]; result = getFallbackTranslationInstant(langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy); if (!result && result !== '') { result = resolveForFallbackLanguageInstant(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator); } } return result; }; /** * Translates with the usage of the fallback languages. * * @param translationId * @param interpolateParams * @param Interpolator * @param defaultTranslationText * @param sanitizeStrategy * @returns {Q.promise} Promise, that resolves to the translation. */ var fallbackTranslation = function (translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy) { // Start with the fallbackLanguage with index 0 return resolveForFallbackLanguage((startFallbackIteration > 0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy); }; /** * Translates with the usage of the fallback languages. * * @param translationId * @param interpolateParams * @param Interpolator * @param sanitizeStrategy * @returns {String} translation */ var fallbackTranslationInstant = function (translationId, interpolateParams, Interpolator, sanitizeStrategy) { // Start with the fallbackLanguage with index 0 return resolveForFallbackLanguageInstant((startFallbackIteration > 0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, sanitizeStrategy); }; var determineTranslation = function (translationId, interpolateParams, interpolationId, defaultTranslationText, uses, sanitizeStrategy) { var deferred = $q.defer(); var table = uses ? $translationTable[uses] : $translationTable, Interpolator = (interpolationId) ? interpolatorHashMap[interpolationId] : defaultInterpolator; // if the translation id exists, we can just interpolate it if (table && Object.prototype.hasOwnProperty.call(table, translationId) && table[translationId] !== null) { var translation = table[translationId]; // If using link, rerun $translate with linked translationId and return it if (translation.substr(0, 2) === '@:') { $translate(translation.substr(2), interpolateParams, interpolationId, defaultTranslationText, uses, sanitizeStrategy) .then(deferred.resolve, deferred.reject); } else { // var resolvedTranslation = Interpolator.interpolate(translation, interpolateParams, 'service', sanitizeStrategy, translationId); resolvedTranslation = applyPostProcessing(translationId, translation, resolvedTranslation, interpolateParams, uses); deferred.resolve(resolvedTranslation); } } else { var missingTranslationHandlerTranslation; // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise if ($missingTranslationHandlerFactory && !pendingLoader) { missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, defaultTranslationText); } // since we couldn't translate the inital requested translation id, // we try it now with one or more fallback languages, if fallback language(s) is // configured. if (uses && $fallbackLanguage && $fallbackLanguage.length) { fallbackTranslation(translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy) .then(function (translation) { deferred.resolve(translation); }, function (_translationId) { deferred.reject(applyNotFoundIndicators(_translationId)); }); } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) { // looks like the requested translation id doesn't exists. // Now, if there is a registered handler for missing translations and no // asyncLoader is pending, we execute the handler if (defaultTranslationText) { deferred.resolve(defaultTranslationText); } else { deferred.resolve(missingTranslationHandlerTranslation); } } else { if (defaultTranslationText) { deferred.resolve(defaultTranslationText); } else { deferred.reject(applyNotFoundIndicators(translationId)); } } } return deferred.promise; }; var determineTranslationInstant = function (translationId, interpolateParams, interpolationId, uses, sanitizeStrategy) { var result, table = uses ? $translationTable[uses] : $translationTable, Interpolator = defaultInterpolator; // if the interpolation id exists use custom interpolator if (interpolatorHashMap && Object.prototype.hasOwnProperty.call(interpolatorHashMap, interpolationId)) { Interpolator = interpolatorHashMap[interpolationId]; } // if the translation id exists, we can just interpolate it if (table && Object.prototype.hasOwnProperty.call(table, translationId) && table[translationId] !== null) { var translation = table[translationId]; // If using link, rerun $translate with linked translationId and return it if (translation.substr(0, 2) === '@:') { result = determineTranslationInstant(translation.substr(2), interpolateParams, interpolationId, uses, sanitizeStrategy); } else { result = Interpolator.interpolate(translation, interpolateParams, 'filter', sanitizeStrategy, translationId); result = applyPostProcessing(translationId, translation, result, interpolateParams, uses, sanitizeStrategy); } } else { var missingTranslationHandlerTranslation; // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise if ($missingTranslationHandlerFactory && !pendingLoader) { missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, sanitizeStrategy); } // since we couldn't translate the inital requested translation id, // we try it now with one or more fallback languages, if fallback language(s) is // configured. if (uses && $fallbackLanguage && $fallbackLanguage.length) { fallbackIndex = 0; result = fallbackTranslationInstant(translationId, interpolateParams, Interpolator, sanitizeStrategy); } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) { // looks like the requested translation id doesn't exists. // Now, if there is a registered handler for missing translations and no // asyncLoader is pending, we execute the handler result = missingTranslationHandlerTranslation; } else { result = applyNotFoundIndicators(translationId); } } return result; }; var clearNextLangAndPromise = function (key) { if ($nextLang === key) { $nextLang = undefined; } langPromises[key] = undefined; }; var applyPostProcessing = function (translationId, translation, resolvedTranslation, interpolateParams, uses, sanitizeStrategy) { var fn = postProcessFn; if (fn) { if (typeof(fn) === 'string') { // getting on-demand instance fn = $injector.get(fn); } if (fn) { return fn(translationId, translation, resolvedTranslation, interpolateParams, uses, sanitizeStrategy); } } return resolvedTranslation; }; var loadTranslationsIfMissing = function (key) { if (!$translationTable[key] && $loaderFactory && !langPromises[key]) { langPromises[key] = loadAsync(key).then(function (translation) { translations(translation.key, translation.table); return translation; }); } }; /** * @ngdoc function * @name pascalprecht.translate.$translate#preferredLanguage * @methodOf pascalprecht.translate.$translate * * @description * Returns the language key for the preferred language. * * @param {string} langKey language String or Array to be used as preferredLanguage (changing at runtime) * * @return {string} preferred language key */ $translate.preferredLanguage = function (langKey) { if (langKey) { setupPreferredLanguage(langKey); } return $preferredLanguage; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#cloakClassName * @methodOf pascalprecht.translate.$translate * * @description * Returns the configured class name for `translate-cloak` directive. * * @return {string} cloakClassName */ $translate.cloakClassName = function () { return $cloakClassName; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#nestedObjectDelimeter * @methodOf pascalprecht.translate.$translate * * @description * Returns the configured delimiter for nested namespaces. * * @return {string} nestedObjectDelimeter */ $translate.nestedObjectDelimeter = function () { return $nestedObjectDelimeter; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#fallbackLanguage * @methodOf pascalprecht.translate.$translate * * @description * Returns the language key for the fallback languages or sets a new fallback stack. * It is recommended to call this before {@link pascalprecht.translate.$translateProvider#preferredLanguage preferredLanguage()}. * * @param {string=} langKey language String or Array of fallback languages to be used (to change stack at runtime) * * @return {string||array} fallback language key */ $translate.fallbackLanguage = function (langKey) { if (langKey !== undefined && langKey !== null) { fallbackStack(langKey); // as we might have an async loader initiated and a new translation language might have been defined // we need to add the promise to the stack also. So - iterate. if ($loaderFactory) { if ($fallbackLanguage && $fallbackLanguage.length) { for (var i = 0, len = $fallbackLanguage.length; i < len; i++) { if (!langPromises[$fallbackLanguage[i]]) { langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]); } } } } $translate.use($translate.use()); } if ($fallbackWasString) { return $fallbackLanguage[0]; } else { return $fallbackLanguage; } }; /** * @ngdoc function * @name pascalprecht.translate.$translate#useFallbackLanguage * @methodOf pascalprecht.translate.$translate * * @description * Sets the first key of the fallback language stack to be used for translation. * Therefore all languages in the fallback array BEFORE this key will be skipped! * * @param {string=} langKey Contains the langKey the iteration shall start with. Set to false if you want to * get back to the whole stack */ $translate.useFallbackLanguage = function (langKey) { if (langKey !== undefined && langKey !== null) { if (!langKey) { startFallbackIteration = 0; } else { var langKeyPosition = indexOf($fallbackLanguage, langKey); if (langKeyPosition > -1) { startFallbackIteration = langKeyPosition; } } } }; /** * @ngdoc function * @name pascalprecht.translate.$translate#proposedLanguage * @methodOf pascalprecht.translate.$translate * * @description * Returns the language key of language that is currently loaded asynchronously. * * @return {string} language key */ $translate.proposedLanguage = function () { return $nextLang; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#storage * @methodOf pascalprecht.translate.$translate * * @description * Returns registered storage. * * @return {object} Storage */ $translate.storage = function () { return Storage; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#negotiateLocale * @methodOf pascalprecht.translate.$translate * * @description * Returns a language key based on available languages and language aliases. If a * language key cannot be resolved, returns undefined. * * If no or a falsy key is given, returns undefined. * * @param {string} [key] Language key * @return {string|undefined} Language key or undefined if no language key is found. */ $translate.negotiateLocale = negotiateLocale; /** * @ngdoc function * @name pascalprecht.translate.$translate#use * @methodOf pascalprecht.translate.$translate * * @description * Tells angular-translate which language to use by given language key. This method is * used to change language at runtime. It also takes care of storing the language * key in a configured store to let your app remember the choosed language. * * When trying to 'use' a language which isn't available it tries to load it * asynchronously with registered loaders. * * Returns promise object with loaded language file data or string of the currently used language. * * If no or a falsy key is given it returns the currently used language key. * The returned string will be ```undefined``` if setting up $translate hasn't finished. * @example * $translate.use("en_US").then(function(data){ * $scope.text = $translate("HELLO"); * }); * * @param {string=} key Language key * @return {object|string} Promise with loaded language data or the language key if a falsy param was given. */ $translate.use = function (key) { if (!key) { return $uses; } var deferred = $q.defer(); deferred.promise.then(null, angular.noop); // AJS "Possibly unhandled rejection" $rootScope.$emit('$translateChangeStart', {language : key}); // Try to get the aliased language key var aliasedKey = negotiateLocale(key); // Ensure only registered language keys will be loaded if ($availableLanguageKeys.length > 0 && !aliasedKey) { return $q.reject(key); } if (aliasedKey) { key = aliasedKey; } // if there isn't a translation table for the language we've requested, // we load it asynchronously $nextLang = key; if (($forceAsyncReloadEnabled || !$translationTable[key]) && $loaderFactory && !langPromises[key]) { langPromises[key] = loadAsync(key).then(function (translation) { translations(translation.key, translation.table); deferred.resolve(translation.key); if ($nextLang === key) { useLanguage(translation.key); } return translation; }, function (key) { $rootScope.$emit('$translateChangeError', {language : key}); deferred.reject(key); $rootScope.$emit('$translateChangeEnd', {language : key}); return $q.reject(key); }); langPromises[key]['finally'](function () { clearNextLangAndPromise(key); })['catch'](angular.noop); // we don't care about errors (clearing) } else if (langPromises[key]) { // we are already loading this asynchronously // resolve our new deferred when the old langPromise is resolved langPromises[key].then(function (translation) { if ($nextLang === translation.key) { useLanguage(translation.key); } deferred.resolve(translation.key); return translation; }, function (key) { // find first available fallback language if that request has failed if (!$uses && $fallbackLanguage && $fallbackLanguage.length > 0 && $fallbackLanguage[0] !== key) { return $translate.use($fallbackLanguage[0]).then(deferred.resolve, deferred.reject); } else { return deferred.reject(key); } }); } else { deferred.resolve(key); useLanguage(key); } return deferred.promise; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#resolveClientLocale * @methodOf pascalprecht.translate.$translate * * @description * This returns the current browser/client's language key. The result is processed with the configured uniform tag resolver. * * @returns {string} the current client/browser language key */ $translate.resolveClientLocale = function () { return getLocale(); }; /** * @ngdoc function * @name pascalprecht.translate.$translate#storageKey * @methodOf pascalprecht.translate.$translate * * @description * Returns the key for the storage. * * @return {string} storage key */ $translate.storageKey = function () { return storageKey(); }; /** * @ngdoc function * @name pascalprecht.translate.$translate#isPostCompilingEnabled * @methodOf pascalprecht.translate.$translate * * @description * Returns whether post compiling is enabled or not * * @return {bool} storage key */ $translate.isPostCompilingEnabled = function () { return $postCompilingEnabled; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#isForceAsyncReloadEnabled * @methodOf pascalprecht.translate.$translate * * @description * Returns whether force async reload is enabled or not * * @return {boolean} forceAsyncReload value */ $translate.isForceAsyncReloadEnabled = function () { return $forceAsyncReloadEnabled; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#isKeepContent * @methodOf pascalprecht.translate.$translate * * @description * Returns whether keepContent or not * * @return {boolean} keepContent value */ $translate.isKeepContent = function () { return $keepContent; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#refresh * @methodOf pascalprecht.translate.$translate * * @description * Refreshes a translation table pointed by the given langKey. If langKey is not specified, * the module will drop all existent translation tables and load new version of those which * are currently in use. * * Refresh means that the module will drop target translation table and try to load it again. * * In case there are no loaders registered the refresh() method will throw an Error. * * If the module is able to refresh translation tables refresh() method will broadcast * $translateRefreshStart and $translateRefreshEnd events. * * @example * // this will drop all currently existent translation tables and reload those which are * // currently in use * $translate.refresh(); * // this will refresh a translation table for the en_US language * $translate.refresh('en_US'); * * @param {string} langKey A language key of the table, which has to be refreshed * * @return {promise} Promise, which will be resolved in case a translation tables refreshing * process is finished successfully, and reject if not. */ $translate.refresh = function (langKey) { if (!$loaderFactory) { throw new Error('Couldn\'t refresh translation table, no loader registered!'); } $rootScope.$emit('$translateRefreshStart', {language : langKey}); var deferred = $q.defer(), updatedLanguages = {}; //private helper function loadNewData(languageKey) { var promise = loadAsync(languageKey); //update the load promise cache for this language langPromises[languageKey] = promise; //register a data handler for the promise promise.then(function (data) { //clear the cache for this language $translationTable[languageKey] = {}; //add the new data for this language translations(languageKey, data.table); //track that we updated this language updatedLanguages[languageKey] = true; }, //handle rejection to appease the $q validation angular.noop); return promise; } //set up post-processing deferred.promise.then( function () { for (var key in $translationTable) { if ($translationTable.hasOwnProperty(key)) { //delete cache entries that were not updated if (!(key in updatedLanguages)) { delete $translationTable[key]; } } } if ($uses) { useLanguage($uses); } }, //handle rejection to appease the $q validation angular.noop )['finally']( function () { $rootScope.$emit('$translateRefreshEnd', {language : langKey}); } ); if (!langKey) { // if there's no language key specified we refresh ALL THE THINGS! var languagesToReload = $fallbackLanguage && $fallbackLanguage.slice() || []; if ($uses && languagesToReload.indexOf($uses) === -1) { languagesToReload.push($uses); } $q.all(languagesToReload.map(loadNewData)).then(deferred.resolve, deferred.reject); } else if ($translationTable[langKey]) { //just refresh the specified language cache loadNewData(langKey).then(deferred.resolve, deferred.reject); } else { deferred.reject(); } return deferred.promise; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#instant * @methodOf pascalprecht.translate.$translate * * @description * Returns a translation instantly from the internal state of loaded translation. All rules * regarding the current language, the preferred language of even fallback languages will be * used except any promise handling. If a language was not found, an asynchronous loading * will be invoked in the background. * * @param {string|array} translationId A token which represents a translation id * This can be optionally an array of translation ids which * results that the function's promise returns an object where * each key is the translation id and the value the translation. * @param {object=} [interpolateParams={}] Params * @param {string=} [interpolationId=undefined] The id of the interpolation to use (use default unless set via useInterpolation()) * @param {string=} [forceLanguage=false] A language to be used instead of the current language * @param {string=} [sanitizeStrategy=undefined] force sanitize strategy for this call instead of using the configured one (use default unless set) * * @return {string|object} translation */ $translate.instant = function (translationId, interpolateParams, interpolationId, forceLanguage, sanitizeStrategy) { // we don't want to re-negotiate $uses var uses = (forceLanguage && forceLanguage !== $uses) ? // we don't want to re-negotiate $uses (negotiateLocale(forceLanguage) || forceLanguage) : $uses; // Detect undefined and null values to shorten the execution and prevent exceptions if (translationId === null || angular.isUndefined(translationId)) { return translationId; } // Check forceLanguage is present if (forceLanguage) { loadTranslationsIfMissing(forceLanguage); } // Duck detection: If the first argument is an array, a bunch of translations was requested. // The result is an object. if (angular.isArray(translationId)) { var results = {}; for (var i = 0, c = translationId.length; i < c; i++) { results[translationId[i]] = $translate.instant(translationId[i], interpolateParams, interpolationId, forceLanguage, sanitizeStrategy); } return results; } // We discarded unacceptable values. So we just need to verify if translationId is empty String if (angular.isString(translationId) && translationId.length < 1) { return translationId; } // trim off any whitespace if (translationId) { translationId = trim.apply(translationId); } var result, possibleLangKeys = []; if ($preferredLanguage) { possibleLangKeys.push($preferredLanguage); } if (uses) { possibleLangKeys.push(uses); } if ($fallbackLanguage && $fallbackLanguage.length) { possibleLangKeys = possibleLangKeys.concat($fallbackLanguage); } for (var j = 0, d = possibleLangKeys.length; j < d; j++) { var possibleLangKey = possibleLangKeys[j]; if ($translationTable[possibleLangKey]) { if (typeof $translationTable[possibleLangKey][translationId] !== 'undefined') { result = determineTranslationInstant(translationId, interpolateParams, interpolationId, uses, sanitizeStrategy); } } if (typeof result !== 'undefined') { break; } } if (!result && result !== '') { if ($notFoundIndicatorLeft || $notFoundIndicatorRight) { result = applyNotFoundIndicators(translationId); } else { // Return translation of default interpolator if not found anything. result = defaultInterpolator.interpolate(translationId, interpolateParams, 'filter', sanitizeStrategy); // looks like the requested translation id doesn't exists. // Now, if there is a registered handler for missing translations and no // asyncLoader is pending, we execute the handler var missingTranslationHandlerTranslation; if ($missingTranslationHandlerFactory && !pendingLoader) { missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, sanitizeStrategy); } if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) { result = missingTranslationHandlerTranslation; } } } return result; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#versionInfo * @methodOf pascalprecht.translate.$translate * * @description * Returns the current version information for the angular-translate library * * @return {string} angular-translate version */ $translate.versionInfo = function () { return version; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#loaderCache * @methodOf pascalprecht.translate.$translate * * @description * Returns the defined loaderCache. * * @return {boolean|string|object} current value of loaderCache */ $translate.loaderCache = function () { return loaderCache; }; // internal purpose only $translate.directivePriority = function () { return directivePriority; }; // internal purpose only $translate.statefulFilter = function () { return statefulFilter; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#isReady * @methodOf pascalprecht.translate.$translate * * @description * Returns whether the service is "ready" to translate (i.e. loading 1st language). * * See also {@link pascalprecht.translate.$translate#methods_onReady onReady()}. * * @return {boolean} current value of ready */ $translate.isReady = function () { return $isReady; }; var $onReadyDeferred = $q.defer(); $onReadyDeferred.promise.then(function () { $isReady = true; }); /** * @ngdoc function * @name pascalprecht.translate.$translate#onReady * @methodOf pascalprecht.translate.$translate * * @description * Calls the function provided or resolved the returned promise after the service is "ready" to translate (i.e. loading 1st language). * * See also {@link pascalprecht.translate.$translate#methods_isReady isReady()}. * * @param {Function=} fn Function to invoke when service is ready * @return {object} Promise resolved when service is ready */ $translate.onReady = function (fn) { var deferred = $q.defer(); if (angular.isFunction(fn)) { deferred.promise.then(fn); } if ($isReady) { deferred.resolve(); } else { $onReadyDeferred.promise.then(deferred.resolve); } return deferred.promise; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#getAvailableLanguageKeys * @methodOf pascalprecht.translate.$translate * * @description * This function simply returns the registered language keys being defined before in the config phase * With this, an application can use the array to provide a language selection dropdown or similar * without any additional effort * * @returns {object} returns the list of possibly registered language keys and mapping or null if not defined */ $translate.getAvailableLanguageKeys = function () { if ($availableLanguageKeys.length > 0) { return $availableLanguageKeys; } return null; }; /** * @ngdoc function * @name pascalprecht.translate.$translate#getTranslationTable * @methodOf pascalprecht.translate.$translate * * @description * Returns translation table by the given language key. * * Unless a language is provided it returns a translation table of the current one. * Note: If translation dictionary is currently downloading or in progress * it will return null. * * @param {string} langKey A token which represents a translation id * * @return {object} a copy of angular-translate $translationTable */ $translate.getTranslationTable = function (langKey) { langKey = langKey || $translate.use(); if (langKey && $translationTable[langKey]) { return angular.copy($translationTable[langKey]); } return null; }; // Whenever $translateReady is being fired, this will ensure the state of $isReady var globalOnReadyListener = $rootScope.$on('$translateReady', function () { $onReadyDeferred.resolve(); globalOnReadyListener(); // one time only globalOnReadyListener = null; }); var globalOnChangeListener = $rootScope.$on('$translateChangeEnd', function () { $onReadyDeferred.resolve(); globalOnChangeListener(); // one time only globalOnChangeListener = null; }); if ($loaderFactory) { // If at least one async loader is defined and there are no // (default) translations available we should try to load them. if (angular.equals($translationTable, {})) { if ($translate.use()) { $translate.use($translate.use()); } } // Also, if there are any fallback language registered, we start // loading them asynchronously as soon as we can. if ($fallbackLanguage && $fallbackLanguage.length) { var processAsyncResult = function (translation) { translations(translation.key, translation.table); $rootScope.$emit('$translateChangeEnd', {language : translation.key}); return translation; }; for (var i = 0, len = $fallbackLanguage.length; i < len; i++) { var fallbackLanguageId = $fallbackLanguage[i]; if ($forceAsyncReloadEnabled || !$translationTable[fallbackLanguageId]) { langPromises[fallbackLanguageId] = loadAsync(fallbackLanguageId).then(processAsyncResult); } } } } else { $rootScope.$emit('$translateReady', {language : $translate.use()}); } return $translate; }]; } $translate.displayName = 'displayName'; /** * @ngdoc object * @name pascalprecht.translate.$translateDefaultInterpolation * @requires $interpolate * * @description * Uses angular's `$interpolate` services to interpolate strings against some values. * * Be aware to configure a proper sanitization strategy. * * See also: * * {@link pascalprecht.translate.$translateSanitization} * * @return {object} $translateDefaultInterpolation Interpolator service */ angular.module('pascalprecht.translate').factory('$translateDefaultInterpolation', $translateDefaultInterpolation); function $translateDefaultInterpolation ($interpolate, $translateSanitization) { 'use strict'; var $translateInterpolator = {}, $locale, $identifier = 'default'; /** * @ngdoc function * @name pascalprecht.translate.$translateDefaultInterpolation#setLocale * @methodOf pascalprecht.translate.$translateDefaultInterpolation * * @description * Sets current locale (this is currently not use in this interpolation). * * @param {string} locale Language key or locale. */ $translateInterpolator.setLocale = function (locale) { $locale = locale; }; /** * @ngdoc function * @name pascalprecht.translate.$translateDefaultInterpolation#getInterpolationIdentifier * @methodOf pascalprecht.translate.$translateDefaultInterpolation * * @description * Returns an identifier for this interpolation service. * * @returns {string} $identifier */ $translateInterpolator.getInterpolationIdentifier = function () { return $identifier; }; /** * @deprecated will be removed in 3.0 * @see {@link pascalprecht.translate.$translateSanitization} */ $translateInterpolator.useSanitizeValueStrategy = function (value) { $translateSanitization.useStrategy(value); return this; }; /** * @ngdoc function * @name pascalprecht.translate.$translateDefaultInterpolation#interpolate * @methodOf pascalprecht.translate.$translateDefaultInterpolation * * @description * Interpolates given value agains given interpolate params using angulars * `$interpolate` service. * * Since AngularJS 1.5, `value` must not be a string but can be anything input. * * @param {string} value translation * @param {object} [interpolationParams={}] interpolation params * @param {string} [context=undefined] current context (filter, directive, service) * @param {string} [sanitizeStrategy=undefined] sanitize strategy (use default unless set) * @param {string} translationId current translationId * * @returns {string} interpolated string */ $translateInterpolator.interpolate = function (value, interpolationParams, context, sanitizeStrategy, translationId) { // jshint ignore:line interpolationParams = interpolationParams || {}; interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params', sanitizeStrategy, context); var interpolatedText; if (angular.isNumber(value)) { // numbers are safe interpolatedText = '' + value; } else if (angular.isString(value)) { // strings must be interpolated (that's the job here) interpolatedText = $interpolate(value)(interpolationParams); interpolatedText = $translateSanitization.sanitize(interpolatedText, 'text', sanitizeStrategy, context); } else { // neither a number or a string, cant interpolate => empty string interpolatedText = ''; } return interpolatedText; }; return $translateInterpolator; } $translateDefaultInterpolation.displayName = '$translateDefaultInterpolation'; angular.module('pascalprecht.translate').constant('$STORAGE_KEY', 'NG_TRANSLATE_LANG_KEY'); angular.module('pascalprecht.translate') /** * @ngdoc directive * @name pascalprecht.translate.directive:translate * @requires $interpolate, * @requires $compile, * @requires $parse, * @requires $rootScope * @restrict AE * * @description * Translates given translation id either through attribute or DOM content. * Internally it uses $translate service to translate the translation id. It possible to * pass an optional `translate-values` object literal as string into translation id. * * @param {string=} translate Translation id which could be either string or interpolated string. * @param {string=} translate-values Values to pass into translation id. Can be passed as object literal string or interpolated object. * @param {string=} translate-attr-ATTR translate Translation id and put it into ATTR attribute. * @param {string=} translate-default will be used unless translation was successful * @param {string=} translate-sanitize-strategy defines locally sanitize strategy * @param {boolean=} translate-compile (default true if present) defines locally activation of {@link pascalprecht.translate.$translateProvider#methods_usePostCompiling} * @param {boolean=} translate-keep-content (default true if present) defines that in case a KEY could not be translated, that the existing content is left in the innerHTML} * * @example

        
TRANSLATION_ID

        

        
{{translationId}}

        
WITH_VALUES

        
WITH_VALUES

        


      
angular.module('ngView', ['pascalprecht.translate']) .config(function ($translateProvider) { $translateProvider.translations('en',{ 'TRANSLATION_ID': 'Hello there!', 'WITH_VALUES': 'The following value is dynamic: {{value}}', 'WITH_CAMEL_CASE_KEY': 'The interpolation key is camel cased: {{camelCaseKey}}' }).preferredLanguage('en'); }); angular.module('ngView').controller('TranslateCtrl', function ($scope) { $scope.translationId = 'TRANSLATION_ID'; $scope.values = { value: 78 }; }); it('should translate', function () { inject(function ($rootScope, $compile) { $rootScope.translationId = 'TRANSLATION_ID'; element = $compile('

')($rootScope); $rootScope.$digest(); expect(element.text()).toBe('Hello there!'); element = $compile('

')($rootScope); $rootScope.$digest(); expect(element.text()).toBe('Hello there!'); element = $compile('

TRANSLATION_ID

')($rootScope); $rootScope.$digest(); expect(element.text()).toBe('Hello there!'); element = $compile('

{{translationId}}

')($rootScope); $rootScope.$digest(); expect(element.text()).toBe('Hello there!'); element = $compile('

')($rootScope); $rootScope.$digest(); expect(element.attr('title')).toBe('Hello there!'); element = $compile('

')($rootScope); $rootScope.$digest(); expect(element.text()).toBe('The interpolation key is camel cased: Hello'); }); });
*/ .directive('translate', translateDirective); function translateDirective($translate, $interpolate, $compile, $parse, $rootScope) { 'use strict'; /** * @name trim * @private * * @description * trim polyfill * * @returns {string} The string stripped of whitespace from both ends */ var trim = function() { return this.toString().replace(/^\s+|\s+$/g, ''); }; /** * @name lowercase * @private * * @description * Return the lowercase string only if the type is string * * @returns {string} The string all in lowercase */ var lowercase = function (string) { return angular.isString(string) ? string.toLowerCase() : string; }; return { restrict: 'AE', scope: true, priority: $translate.directivePriority(), compile: function (tElement, tAttr) { var translateValuesExist = (tAttr.translateValues) ? tAttr.translateValues : undefined; var translateInterpolation = (tAttr.translateInterpolation) ? tAttr.translateInterpolation : undefined; var translateSanitizeStrategyExist = (tAttr.translateSanitizeStrategy) ? tAttr.translateSanitizeStrategy : undefined; var translateValueExist = tElement[0].outerHTML.match(/translate-value-+/i); var interpolateRegExp = '^(.*)(' + $interpolate.startSymbol() + '.*' + $interpolate.endSymbol() + ')(.*)', watcherRegExp = '^(.*)' + $interpolate.startSymbol() + '(.*)' + $interpolate.endSymbol() + '(.*)'; return function linkFn(scope, iElement, iAttr) { scope.interpolateParams = {}; scope.preText = ''; scope.postText = ''; scope.translateNamespace = getTranslateNamespace(scope); var translationIds = {}; var initInterpolationParams = function (interpolateParams, iAttr, tAttr) { // initial setup if (iAttr.translateValues) { angular.extend(interpolateParams, $parse(iAttr.translateValues)(scope.$parent)); } // initially fetch all attributes if existing and fill the params if (translateValueExist) { for (var attr in tAttr) { if (Object.prototype.hasOwnProperty.call(iAttr, attr) && attr.substr(0, 14) === 'translateValue' && attr !== 'translateValues') { var attributeName = lowercase(attr.substr(14, 1)) + attr.substr(15); interpolateParams[attributeName] = tAttr[attr]; } } } }; // Ensures any change of the attribute "translate" containing the id will // be re-stored to the scope's "translationId". // If the attribute has no content, the element's text value (white spaces trimmed off) will be used. var observeElementTranslation = function (translationId) { // Remove any old watcher if (angular.isFunction(observeElementTranslation._unwatchOld)) { observeElementTranslation._unwatchOld(); observeElementTranslation._unwatchOld = undefined; } if (angular.equals(translationId , '') || !angular.isDefined(translationId)) { var iElementText = trim.apply(iElement.text()); // Resolve translation id by inner html if required var interpolateMatches = iElementText.match(interpolateRegExp); // Interpolate translation id if required if (angular.isArray(interpolateMatches)) { scope.preText = interpolateMatches[1]; scope.postText = interpolateMatches[3]; translationIds.translate = $interpolate(interpolateMatches[2])(scope.$parent); var watcherMatches = iElementText.match(watcherRegExp); if (angular.isArray(watcherMatches) && watcherMatches[2] && watcherMatches[2].length) { observeElementTranslation._unwatchOld = scope.$watch(watcherMatches[2], function (newValue) { translationIds.translate = newValue; updateTranslations(); }); } } else { // do not assigne the translation id if it is empty. translationIds.translate = !iElementText ? undefined : iElementText; } } else { translationIds.translate = translationId; } updateTranslations(); }; var observeAttributeTranslation = function (translateAttr) { iAttr.$observe(translateAttr, function (translationId) { translationIds[translateAttr] = translationId; updateTranslations(); }); }; // initial setup with values initInterpolationParams(scope.interpolateParams, iAttr, tAttr); var firstAttributeChangedEvent = true; iAttr.$observe('translate', function (translationId) { if (typeof translationId === 'undefined') { // case of element "xyz" observeElementTranslation(''); } else { // case of regular attribute if (translationId !== '' || !firstAttributeChangedEvent) { translationIds.translate = translationId; updateTranslations(); } } firstAttributeChangedEvent = false; }); for (var translateAttr in iAttr) { if (iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr' && translateAttr.length > 13) { observeAttributeTranslation(translateAttr); } } iAttr.$observe('translateDefault', function (value) { scope.defaultText = value; updateTranslations(); }); if (translateSanitizeStrategyExist) { iAttr.$observe('translateSanitizeStrategy', function (value) { scope.sanitizeStrategy = $parse(value)(scope.$parent); updateTranslations(); }); } if (translateValuesExist) { iAttr.$observe('translateValues', function (interpolateParams) { if (interpolateParams) { scope.$parent.$watch(function () { angular.extend(scope.interpolateParams, $parse(interpolateParams)(scope.$parent)); }); } }); } if (translateValueExist) { var observeValueAttribute = function (attrName) { iAttr.$observe(attrName, function (value) { var attributeName = lowercase(attrName.substr(14, 1)) + attrName.substr(15); scope.interpolateParams[attributeName] = value; }); }; for (var attr in iAttr) { if (Object.prototype.hasOwnProperty.call(iAttr, attr) && attr.substr(0, 14) === 'translateValue' && attr !== 'translateValues') { observeValueAttribute(attr); } } } // Master update function var updateTranslations = function () { for (var key in translationIds) { if (translationIds.hasOwnProperty(key) && translationIds[key] !== undefined) { updateTranslation(key, translationIds[key], scope, scope.interpolateParams, scope.defaultText, scope.translateNamespace); } } }; // Put translation processing function outside loop var updateTranslation = function(translateAttr, translationId, scope, interpolateParams, defaultTranslationText, translateNamespace) { if (translationId) { // if translation id starts with '.' and translateNamespace given, prepend namespace if (translateNamespace && translationId.charAt(0) === '.') { translationId = translateNamespace + translationId; } $translate(translationId, interpolateParams, translateInterpolation, defaultTranslationText, scope.translateLanguage, scope.sanitizeStrategy) .then(function (translation) { applyTranslation(translation, scope, true, translateAttr); }, function (translationId) { applyTranslation(translationId, scope, false, translateAttr); }); } else { // as an empty string cannot be translated, we can solve this using successful=false applyTranslation(translationId, scope, false, translateAttr); } }; var applyTranslation = function (value, scope, successful, translateAttr) { if (!successful) { if (typeof scope.defaultText !== 'undefined') { value = scope.defaultText; } } if (translateAttr === 'translate') { // default translate into innerHTML if (successful || (!successful && !$translate.isKeepContent() && typeof iAttr.translateKeepContent === 'undefined')) { iElement.empty().append(scope.preText + value + scope.postText); } var globallyEnabled = $translate.isPostCompilingEnabled(); var locallyDefined = typeof tAttr.translateCompile !== 'undefined'; var locallyEnabled = locallyDefined && tAttr.translateCompile !== 'false'; if ((globallyEnabled && !locallyDefined) || locallyEnabled) { $compile(iElement.contents())(scope); } } else { // translate attribute var attributeName = iAttr.$attr[translateAttr]; if (attributeName.substr(0, 5) === 'data-') { // ensure html5 data prefix is stripped attributeName = attributeName.substr(5); } attributeName = attributeName.substr(15); iElement.attr(attributeName, value); } }; if (translateValuesExist || translateValueExist || iAttr.translateDefault) { scope.$watch('interpolateParams', updateTranslations, true); } // Replaced watcher on translateLanguage with event listener scope.$on('translateLanguageChanged', updateTranslations); // Ensures the text will be refreshed after the current language was changed // w/ $translate.use(...) var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations); // ensure translation will be looked up at least one if (iElement.text().length) { if (iAttr.translate) { observeElementTranslation(iAttr.translate); } else { observeElementTranslation(''); } } else if (iAttr.translate) { // ensure attribute will be not skipped observeElementTranslation(iAttr.translate); } updateTranslations(); scope.$on('$destroy', unbind); }; } }; } /** * Returns the scope's namespace. * @private * @param scope * @returns {string} */ function getTranslateNamespace(scope) { 'use strict'; if (scope.translateNamespace) { return scope.translateNamespace; } if (scope.$parent) { return getTranslateNamespace(scope.$parent); } } translateDirective.displayName = 'translateDirective'; angular.module('pascalprecht.translate') /** * @ngdoc directive * @name pascalprecht.translate.directive:translate-attr * @restrict A * * @description * Translates attributes like translate-attr-ATTR, but with an object like ng-class. * Internally it uses `translate` service to translate translation id. It possible to * pass an optional `translate-values` object literal as string into translation id. * * @param {string=} translate-attr Object literal mapping attributes to translation ids. * @param {string=} translate-values Values to pass into the translation ids. Can be passed as object literal string. * @param {string=} translate-sanitize-strategy defines locally sanitize strategy * * @example
angular.module('ngView', ['pascalprecht.translate']) .config(function ($translateProvider) { $translateProvider.translations('en',{ 'TRANSLATION_ID': 'Hello there!', 'WITH_VALUES': 'The following value is dynamic: {{value}}', }).preferredLanguage('en'); }); angular.module('ngView').controller('TranslateCtrl', function ($scope) { $scope.translationId = 'TRANSLATION_ID'; $scope.values = { value: 78 }; }); it('should translate', function () { inject(function ($rootScope, $compile) { $rootScope.translationId = 'TRANSLATION_ID'; element = $compile('')($rootScope); $rootScope.$digest(); expect(element.attr('placeholder)).toBe('Hello there!'); expect(element.attr('title)).toBe('The following value is dynamic: 5'); }); });
*/ .directive('translateAttr', translateAttrDirective); function translateAttrDirective($translate, $rootScope) { 'use strict'; return { restrict: 'A', priority: $translate.directivePriority(), link: function linkFn(scope, element, attr) { var translateAttr, translateValues, translateSanitizeStrategy, previousAttributes = {}; // Main update translations function var updateTranslations = function () { angular.forEach(translateAttr, function (translationId, attributeName) { if (!translationId) { return; } previousAttributes[attributeName] = true; // if translation id starts with '.' and translateNamespace given, prepend namespace if (scope.translateNamespace && translationId.charAt(0) === '.') { translationId = scope.translateNamespace + translationId; } $translate(translationId, translateValues, attr.translateInterpolation, undefined, scope.translateLanguage, translateSanitizeStrategy) .then(function (translation) { element.attr(attributeName, translation); }, function (translationId) { element.attr(attributeName, translationId); }); }); // Removing unused attributes that were previously used angular.forEach(previousAttributes, function (flag, attributeName) { if (!translateAttr[attributeName]) { element.removeAttr(attributeName); delete previousAttributes[attributeName]; } }); }; // Watch for attribute changes watchAttribute( scope, attr.translateAttr, function (newValue) { translateAttr = newValue; }, updateTranslations ); // Watch for value changes watchAttribute( scope, attr.translateValues, function (newValue) { translateValues = newValue; }, updateTranslations ); // Watch for sanitize strategy changes watchAttribute( scope, attr.translateSanitizeStrategy, function (newValue) { translateSanitizeStrategy = newValue; }, updateTranslations ); if (attr.translateValues) { scope.$watch(attr.translateValues, updateTranslations, true); } // Replaced watcher on translateLanguage with event listener scope.$on('translateLanguageChanged', updateTranslations); // Ensures the text will be refreshed after the current language was changed // w/ $translate.use(...) var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations); updateTranslations(); scope.$on('$destroy', unbind); } }; } function watchAttribute(scope, attribute, valueCallback, changeCallback) { 'use strict'; if (!attribute) { return; } if (attribute.substr(0, 2) === '::') { attribute = attribute.substr(2); } else { scope.$watch(attribute, function(newValue) { valueCallback(newValue); changeCallback(); }, true); } valueCallback(scope.$eval(attribute)); } translateAttrDirective.displayName = 'translateAttrDirective'; angular.module('pascalprecht.translate') /** * @ngdoc directive * @name pascalprecht.translate.directive:translateCloak * @requires $translate * @restrict A * * $description * Adds a `translate-cloak` class name to the given element where this directive * is applied initially and removes it, once a loader has finished loading. * * This directive can be used to prevent initial flickering when loading translation * data asynchronously. * * The class name is defined in * {@link pascalprecht.translate.$translateProvider#cloakClassName $translate.cloakClassName()}. * * @param {string=} translate-cloak If a translationId is provided, it will be used for showing * or hiding the cloak. Basically it relies on the translation * resolve. */ .directive('translateCloak', translateCloakDirective); function translateCloakDirective($translate, $rootScope) { 'use strict'; return { compile : function (tElement) { var applyCloak = function (element) { element.addClass($translate.cloakClassName()); }, removeCloak = function (element) { element.removeClass($translate.cloakClassName()); }; applyCloak(tElement); return function linkFn(scope, iElement, iAttr) { //Create bound functions that incorporate the active DOM element. var iRemoveCloak = removeCloak.bind(this, iElement), iApplyCloak = applyCloak.bind(this, iElement); if (iAttr.translateCloak && iAttr.translateCloak.length) { // Register a watcher for the defined translation allowing a fine tuned cloak iAttr.$observe('translateCloak', function (translationId) { $translate(translationId).then(iRemoveCloak, iApplyCloak); }); $rootScope.$on('$translateChangeSuccess', function () { $translate(iAttr.translateCloak).then(iRemoveCloak, iApplyCloak); }); } else { $translate.onReady(iRemoveCloak); } }; } }; } translateCloakDirective.displayName = 'translateCloakDirective'; angular.module('pascalprecht.translate') /** * @ngdoc directive * @name pascalprecht.translate.directive:translateNamespace * @restrict A * * @description * Translates given translation id either through attribute or DOM content. * Internally it uses `translate` filter to translate translation id. It is possible to * pass an optional `translate-values` object literal as string into translation id. * * @param {string=} translate namespace name which could be either string or interpolated string. * * @example

.HEADERS.TITLE

.HEADERS.WELCOME

.TITLE

.WELCOME

angular.module('ngView', ['pascalprecht.translate']) .config(function ($translateProvider) { $translateProvider.translations('en',{ 'TRANSLATION_ID': 'Hello there!', 'CONTENT': { 'HEADERS': { TITLE: 'Title' } }, 'CONTENT.HEADERS.WELCOME': 'Welcome' }).preferredLanguage('en'); });
*/ .directive('translateNamespace', translateNamespaceDirective); function translateNamespaceDirective() { 'use strict'; return { restrict: 'A', scope: true, compile: function () { return { pre: function (scope, iElement, iAttrs) { scope.translateNamespace = _getTranslateNamespace(scope); if (scope.translateNamespace && iAttrs.translateNamespace.charAt(0) === '.') { scope.translateNamespace += iAttrs.translateNamespace; } else { scope.translateNamespace = iAttrs.translateNamespace; } } }; } }; } /** * Returns the scope's namespace. * @private * @param scope * @returns {string} */ function _getTranslateNamespace(scope) { 'use strict'; if (scope.translateNamespace) { return scope.translateNamespace; } if (scope.$parent) { return _getTranslateNamespace(scope.$parent); } } translateNamespaceDirective.displayName = 'translateNamespaceDirective'; angular.module('pascalprecht.translate') /** * @ngdoc directive * @name pascalprecht.translate.directive:translateLanguage * @restrict A * * @description * Forces the language to the directives in the underlying scope. * * @param {string=} translate language that will be negotiated. * * @example

HELLO

HELLO

angular.module('ngView', ['pascalprecht.translate']) .config(function ($translateProvider) { $translateProvider .translations('en',{ 'HELLO': 'Hello world!' }) .translations('de',{ 'HELLO': 'Hallo Welt!' }) .preferredLanguage('en'); });
*/ .directive('translateLanguage', translateLanguageDirective); function translateLanguageDirective() { 'use strict'; return { restrict: 'A', scope: true, compile: function () { return function linkFn(scope, iElement, iAttrs) { iAttrs.$observe('translateLanguage', function (newTranslateLanguage) { scope.translateLanguage = newTranslateLanguage; }); scope.$watch('translateLanguage', function(){ scope.$broadcast('translateLanguageChanged'); }); }; } }; } translateLanguageDirective.displayName = 'translateLanguageDirective'; angular.module('pascalprecht.translate') /** * @ngdoc filter * @name pascalprecht.translate.filter:translate * @requires $parse * @requires pascalprecht.translate.$translate * @function * * @description * Uses `$translate` service to translate contents. Accepts interpolate parameters * to pass dynamized values though translation. * * @param {string} translationId A translation id to be translated. * @param {*=} interpolateParams Optional object literal (as hash or string) to pass values into translation. * * @returns {string} Translated text. * * @example
{{ 'TRANSLATION_ID' | translate }}
{{ translationId | translate }}
{{ 'WITH_VALUES' | translate:'{value: 5}' }}
{{ 'WITH_VALUES' | translate:values }}
angular.module('ngView', ['pascalprecht.translate']) .config(function ($translateProvider) { $translateProvider.translations('en', { 'TRANSLATION_ID': 'Hello there!', 'WITH_VALUES': 'The following value is dynamic: {{value}}' }); $translateProvider.preferredLanguage('en'); }); angular.module('ngView').controller('TranslateCtrl', function ($scope) { $scope.translationId = 'TRANSLATION_ID'; $scope.values = { value: 78 }; });
*/ .filter('translateAddress', translateFilterFactory); function translateFilterFactory($parse, $translate) { 'use strict'; var translateFilter = function (translationId, interpolateParams, interpolation, forceLanguage) { if (!angular.isObject(interpolateParams)) { var ctx = this || { '__SCOPE_IS_NOT_AVAILABLE': 'More info at https://github.com/angular/angular.js/commit/8863b9d04c722b278fa93c5d66ad1e578ad6eb1f' }; interpolateParams = $parse(interpolateParams)(ctx); } return $translate.instant(translationId, interpolateParams, interpolation, forceLanguage); }; if ($translate.statefulFilter()) { translateFilter.$stateful = true; } return translateFilter; } translateFilterFactory.displayName = 'translateFilterFactory'; angular.module('pascalprecht.translate') /** * @ngdoc object * @name pascalprecht.translate.$translationCache * @requires $cacheFactory * * @description * The first time a translation table is used, it is loaded in the translation cache for quick retrieval. You * can load translation tables directly into the cache by consuming the * `$translationCache` service directly. * * @return {object} $cacheFactory object. */ .factory('$translationCache', $translationCache); function $translationCache($cacheFactory) { 'use strict'; return $cacheFactory('translations'); } $translationCache.displayName = '$translationCache'; return 'pascalprecht.translate'; })); /*! * jQuery */ !function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)}(function(e){function t(t,n){var r,a,o,s=t.nodeName.toLowerCase();return"area"===s?(r=t.parentNode,a=r.name,!(!t.href||!a||"map"!==r.nodeName.toLowerCase())&&(o=e("img[usemap='#"+a+"']")[0],!!o&&i(o))):(/^(input|select|textarea|button|object)$/.test(s)?!t.disabled:"a"===s?t.href||n:n)&&i(t)}function i(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.4",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(t){var i=this.css("position"),n="absolute"===i,r=t?/(auto|scroll|hidden)/:/(auto|scroll)/,a=this.parents().filter(function(){var t=e(this);return(!n||"static"!==t.css("position"))&&r.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==i&&a.length?a:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,n){return!!e.data(t,n[3])},focusable:function(i){return t(i,!isNaN(e.attr(i,"tabindex")))},tabbable:function(i){var n=e.attr(i,"tabindex"),r=isNaN(n);return(r||n>=0)&&t(i,!r)}}),e("").outerWidth(1).jquery||e.each(["Width","Height"],function(t,i){function n(t,i,n,a){return e.each(r,function(){i-=parseFloat(e.css(t,"padding"+this))||0,n&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var r="Width"===i?["Left","Right"]:["Top","Bottom"],a=i.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+i]=function(t){return void 0===t?o["inner"+i].call(this):this.each(function(){e(this).css(a,n(this,t)+"px")})},e.fn["outer"+i]=function(t,r){return"number"!=typeof t?o["outer"+i].call(this,t):this.each(function(){e(this).css(a,n(this,t,!0,r)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(i,n){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),n&&n.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var i,n,r=e(this[0]);r.length&&r[0]!==document;){if(i=r.css("position"),("absolute"===i||"relative"===i||"fixed"===i)&&(n=parseInt(r.css("zIndex"),10),!isNaN(n)&&0!==n))return n;r=r.parent()}return 0}}),e.ui.plugin={add:function(t,i,n){var r,a=e.ui[t].prototype;for(r in n)a.plugins[r]=a.plugins[r]||[],a.plugins[r].push([i,n[r]])},call:function(e,t,i,n){var r,a=e.plugins[t];if(a&&(n||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(r=0;a.length>r;r++)e.options[a[r][0]]&&a[r][1].apply(e.element,i)}};var n=0,r=Array.prototype.slice;e.cleanData=function(t){return function(i){var n,r,a;for(a=0;null!=(r=i[a]);a++)try{n=e._data(r,"events"),n&&n.remove&&e(r).triggerHandler("remove")}catch(o){}t(i)}}(e.cleanData),e.widget=function(t,i,n){var r,a,o,s,u={},l=t.split(".")[0];return t=t.split(".")[1],r=l+"-"+t,n||(n=i,i=e.Widget),e.expr[":"][r.toLowerCase()]=function(t){return!!e.data(t,r)},e[l]=e[l]||{},a=e[l][t],o=e[l][t]=function(e,t){return this._createWidget?void(arguments.length&&this._createWidget(e,t)):new o(e,t)},e.extend(o,a,{version:n.version,_proto:e.extend({},n),_childConstructors:[]}),s=new i,s.options=e.widget.extend({},s.options),e.each(n,function(t,n){return e.isFunction(n)?void(u[t]=function(){var e=function(){return i.prototype[t].apply(this,arguments)},r=function(e){return i.prototype[t].apply(this,e)};return function(){var t,i=this._super,a=this._superApply;return this._super=e,this._superApply=r,t=n.apply(this,arguments),this._super=i,this._superApply=a,t}}()):void(u[t]=n)}),o.prototype=e.widget.extend(s,{widgetEventPrefix:a?s.widgetEventPrefix||t:t},u,{constructor:o,namespace:l,widgetName:t,widgetFullName:r}),a?(e.each(a._childConstructors,function(t,i){var n=i.prototype;e.widget(n.namespace+"."+n.widgetName,o,i._proto)}),delete a._childConstructors):i._childConstructors.push(o),e.widget.bridge(t,o),o},e.widget.extend=function(t){for(var i,n,a=r.call(arguments,1),o=0,s=a.length;s>o;o++)for(i in a[o])n=a[o][i],a[o].hasOwnProperty(i)&&void 0!==n&&(t[i]=e.isPlainObject(n)?e.isPlainObject(t[i])?e.widget.extend({},t[i],n):e.widget.extend({},n):n);return t},e.widget.bridge=function(t,i){var n=i.prototype.widgetFullName||t;e.fn[t]=function(a){var o="string"==typeof a,s=r.call(arguments,1),u=this;return o?this.each(function(){var i,r=e.data(this,n);return"instance"===a?(u=r,!1):r?e.isFunction(r[a])&&"_"!==a.charAt(0)?(i=r[a].apply(r,s),i!==r&&void 0!==i?(u=i&&i.jquery?u.pushStack(i.get()):i,!1):void 0):e.error("no such method '"+a+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; attempted to call method '"+a+"'")}):(s.length&&(a=e.widget.extend.apply(null,[a].concat(s))),this.each(function(){var t=e.data(this,n);t?(t.option(a||{}),t._init&&t._init()):e.data(this,n,new i(a,this))})),u}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{disabled:!1,create:null},_createWidget:function(t,i){i=e(i||this.defaultElement||this)[0],this.element=e(i),this.uuid=n++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=e(),this.hoverable=e(),this.focusable=e(),i!==this&&(e.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===i&&this.destroy()}}),this.document=e(i.style?i.ownerDocument:i.document||i),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,i){var n,r,a,o=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(o={},n=t.split("."),t=n.shift(),n.length){for(r=o[t]=e.widget.extend({},this.options[t]),a=0;n.length-1>a;a++)r[n[a]]=r[n[a]]||{},r=r[n[a]];if(t=n.pop(),1===arguments.length)return void 0===r[t]?null:r[t];r[t]=i}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=i}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,i,n){var r,a=this;"boolean"!=typeof t&&(n=i,i=t,t=!1),n?(i=r=e(i),this.bindings=this.bindings.add(i)):(n=i,i=this.element,r=this.widget()),e.each(n,function(n,o){function s(){return t||a.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(s.guid=o.guid=o.guid||s.guid||e.guid++);var u=n.match(/^([\w:-]*)\s*(.*)$/),l=u[1]+a.eventNamespace,c=u[2];c?r.delegate(c,l,s):i.bind(l,s)})},_off:function(t,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(i).undelegate(i),this.bindings=e(this.bindings.not(t).get()),this.focusable=e(this.focusable.not(t).get()),this.hoverable=e(this.hoverable.not(t).get())},_delay:function(e,t){function i(){return("string"==typeof e?n[e]:e).apply(n,arguments)}var n=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,n){var r,a,o=this.options[t];if(n=n||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(r in a)r in i||(i[r]=a[r]);return this.element.trigger(i,n),!(e.isFunction(o)&&o.apply(this.element[0],[i].concat(n))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(n,r,a){"string"==typeof r&&(r={effect:r});var o,s=r?r===!0||"number"==typeof r?i:r.effect||i:t;r=r||{},"number"==typeof r&&(r={duration:r}),o=!e.isEmptyObject(r),r.complete=a,r.delay&&n.delay(r.delay),o&&e.effects&&e.effects.effect[s]?n[t](r):s!==t&&n[s]?n[s](r.duration,r.easing,a):n.queue(function(i){e(this)[t](),a&&a.call(n[0]),i()})}}),e.widget,function(){function t(e,t,i){return[parseFloat(e[0])*(f.test(e[0])?t/100:1),parseFloat(e[1])*(f.test(e[1])?i/100:1)]}function i(t,i){return parseInt(e.css(t,i),10)||0}function n(t){var i=t[0];return 9===i.nodeType?{width:t.width(),height:t.height(),offset:{top:0,left:0}}:e.isWindow(i)?{width:t.width(),height:t.height(),offset:{top:t.scrollTop(),left:t.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:t.outerWidth(),height:t.outerHeight(),offset:t.offset()}}e.ui=e.ui||{};var r,a,o=Math.max,s=Math.abs,u=Math.round,l=/left|center|right/,c=/top|center|bottom/,d=/[\+\-]\d+(\.[\d]+)?%?/,h=/^\w+/,f=/%$/,p=e.fn.position;e.position={scrollbarWidth:function(){if(void 0!==r)return r;var t,i,n=e("
"),a=n.children()[0];return e("body").append(n),t=a.offsetWidth,n.css("overflow","scroll"),i=a.offsetWidth,t===i&&(i=n[0].clientWidth),n.remove(),r=t-i},getScrollInfo:function(t){var i=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),n=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),r="scroll"===i||"auto"===i&&t.widthi?"left":t>0?"right":"center",vertical:0>a?"top":n>0?"bottom":"middle"};d>m&&m>s(t+i)&&(u.horizontal="center"),h>g&&g>s(n+a)&&(u.vertical="middle"),u.important=o(s(t),s(i))>o(s(n),s(a))?"horizontal":"vertical",r.using.call(this,e,u)}),c.offset(e.extend(C,{using:l}))})},e.ui.position={fit:{left:function(e,t){var i,n=t.within,r=n.isWindow?n.scrollLeft:n.offset.left,a=n.width,s=e.left-t.collisionPosition.marginLeft,u=r-s,l=s+t.collisionWidth-a-r;t.collisionWidth>a?u>0&&0>=l?(i=e.left+u+t.collisionWidth-a-r,e.left+=u-i):e.left=l>0&&0>=u?r:u>l?r+a-t.collisionWidth:r:u>0?e.left+=u:l>0?e.left-=l:e.left=o(e.left-s,e.left)},top:function(e,t){var i,n=t.within,r=n.isWindow?n.scrollTop:n.offset.top,a=t.within.height,s=e.top-t.collisionPosition.marginTop,u=r-s,l=s+t.collisionHeight-a-r;t.collisionHeight>a?u>0&&0>=l?(i=e.top+u+t.collisionHeight-a-r,e.top+=u-i):e.top=l>0&&0>=u?r:u>l?r+a-t.collisionHeight:r:u>0?e.top+=u:l>0?e.top-=l:e.top=o(e.top-s,e.top)}},flip:{left:function(e,t){var i,n,r=t.within,a=r.offset.left+r.scrollLeft,o=r.width,u=r.isWindow?r.scrollLeft:r.offset.left,l=e.left-t.collisionPosition.marginLeft,c=l-u,d=l+t.collisionWidth-o-u,h="left"===t.my[0]?-t.elemWidth:"right"===t.my[0]?t.elemWidth:0,f="left"===t.at[0]?t.targetWidth:"right"===t.at[0]?-t.targetWidth:0,p=-2*t.offset[0];0>c?(i=e.left+h+f+p+t.collisionWidth-o-a,(0>i||s(c)>i)&&(e.left+=h+f+p)):d>0&&(n=e.left-t.collisionPosition.marginLeft+h+f+p-u,(n>0||d>s(n))&&(e.left+=h+f+p))},top:function(e,t){var i,n,r=t.within,a=r.offset.top+r.scrollTop,o=r.height,u=r.isWindow?r.scrollTop:r.offset.top,l=e.top-t.collisionPosition.marginTop,c=l-u,d=l+t.collisionHeight-o-u,h="top"===t.my[1],f=h?-t.elemHeight:"bottom"===t.my[1]?t.elemHeight:0,p="top"===t.at[1]?t.targetHeight:"bottom"===t.at[1]?-t.targetHeight:0,m=-2*t.offset[1];0>c?(n=e.top+f+p+m+t.collisionHeight-o-a,(0>n||s(c)>n)&&(e.top+=f+p+m)):d>0&&(i=e.top-t.collisionPosition.marginTop+f+p+m-u,(i>0||d>s(i))&&(e.top+=f+p+m))}},flipfit:{left:function(){e.ui.position.flip.left.apply(this,arguments),e.ui.position.fit.left.apply(this,arguments)},top:function(){e.ui.position.flip.top.apply(this,arguments),e.ui.position.fit.top.apply(this,arguments)}}},function(){var t,i,n,r,o,s=document.getElementsByTagName("body")[0],u=document.createElement("div");t=document.createElement(s?"div":"body"),n={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},s&&e.extend(n,{position:"absolute",left:"-1000px",top:"-1000px"});for(o in n)t.style[o]=n[o];t.appendChild(u),i=s||document.documentElement,i.insertBefore(t,i.firstChild),u.style.cssText="position: absolute; left: 10.7432222px;",r=e(u).offset().left,a=r>10&&11>r,t.innerHTML="",i.removeChild(t)}()}(),e.ui.position,e.widget("ui.menu",{version:"1.11.4",defaultElement:"
    ",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},items:"> *",menus:"ul",position:{my:"left-1 top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item":function(e){e.preventDefault()},"click .ui-menu-item":function(t){var i=e(t.target);!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(t),t.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(t):!this.element.is(":focus")&&e(this.document[0].activeElement).closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(t){if(!this.previousFilter){var i=e(t.currentTarget);i.siblings(".ui-state-active").removeClass("ui-state-active"),this.focus(t,i)}},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(e,t){var i=this.active||this.element.find(this.options.items).eq(0);t||this.focus(e,i)},blur:function(t){this._delay(function(){e.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){this._closeOnDocumentClick(e)&&this.collapseAll(e),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-menu-icons ui-front").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").removeUniqueId().removeClass("ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var t=e(this);t.data("ui-menu-submenu-carat")&&t.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(t){var i,n,r,a,o=!0;switch(t.keyCode){case e.ui.keyCode.PAGE_UP:this.previousPage(t);break;case e.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case e.ui.keyCode.HOME:this._move("first","first",t);break;case e.ui.keyCode.END:this._move("last","last",t);break;case e.ui.keyCode.UP:this.previous(t);break;case e.ui.keyCode.DOWN:this.next(t);break;case e.ui.keyCode.LEFT:this.collapse(t);break;case e.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case e.ui.keyCode.ENTER:case e.ui.keyCode.SPACE:this._activate(t);break;case e.ui.keyCode.ESCAPE:this.collapse(t);break;default:o=!1,n=this.previousFilter||"",r=String.fromCharCode(t.keyCode),a=!1,clearTimeout(this.filterTimer),r===n?a=!0:r=n+r,i=this._filterMenuItems(r),i=a&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(r=String.fromCharCode(t.keyCode),i=this._filterMenuItems(r)),i.length?(this.focus(t,i),this.previousFilter=r,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}o&&t.preventDefault()},_activate:function(e){this.active.is(".ui-state-disabled")||(this.active.is("[aria-haspopup='true']")?this.expand(e):this.select(e))},refresh:function(){var t,i,n=this,r=this.options.icons.submenu,a=this.element.find(this.options.menus);this.element.toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length),a.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-front").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=e(this),i=t.parent(),n=e("").addClass("ui-menu-icon ui-icon "+r).data("ui-menu-submenu-carat",!0);i.attr("aria-haspopup","true").prepend(n),t.attr("aria-labelledby",i.attr("id"))}),t=a.add(this.element),i=t.find(this.options.items),i.not(".ui-menu-item").each(function(){var t=e(this);n._isDivider(t)&&t.addClass("ui-widget-content ui-menu-divider")}),i.not(".ui-menu-item, .ui-menu-divider").addClass("ui-menu-item").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!e.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(e,t){"icons"===e&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(t.submenu),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},focus:function(e,t){var i,n;this.blur(e,e&&"focus"===e.type),this._scrollIntoView(t),this.active=t.first(),n=this.active.addClass("ui-state-focus").removeClass("ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",n.attr("id")),this.active.parent().closest(".ui-menu-item").addClass("ui-state-active"),e&&"keydown"===e.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=t.children(".ui-menu"),i.length&&e&&/^mouse/.test(e.type)&&this._startOpening(i),this.activeMenu=t.parent(),this._trigger("focus",e,{item:t})},_scrollIntoView:function(t){var i,n,r,a,o,s;this._hasScroll()&&(i=parseFloat(e.css(this.activeMenu[0],"borderTopWidth"))||0,n=parseFloat(e.css(this.activeMenu[0],"paddingTop"))||0,r=t.offset().top-this.activeMenu.offset().top-i-n,a=this.activeMenu.scrollTop(),o=this.activeMenu.height(),s=t.outerHeight(),0>r?this.activeMenu.scrollTop(a+r):r+s>o&&this.activeMenu.scrollTop(a+r-o+s))},blur:function(e,t){t||clearTimeout(this.timer),this.active&&(this.active.removeClass("ui-state-focus"),this.active=null,this._trigger("blur",e,{item:this.active}))},_startOpening:function(e){clearTimeout(this.timer),"true"===e.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(e)},this.delay))},_open:function(t){var i=e.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(t.parents(".ui-menu")).hide().attr("aria-hidden","true"),t.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(t,i){clearTimeout(this.timer),this.timer=this._delay(function(){var n=i?this.element:e(t&&t.target).closest(this.element.find(".ui-menu"));n.length||(n=this.element),this._close(n),this.blur(t),this.activeMenu=n},this.delay)},_close:function(e){e||(e=this.active?this.active.parent():this.element),e.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find(".ui-state-active").not(".ui-state-focus").removeClass("ui-state-active")},_closeOnDocumentClick:function(t){return!e(t.target).closest(".ui-menu").length},_isDivider:function(e){return!/[^\-\u2014\u2013\s]/.test(e.text())},collapse:function(e){var t=this.active&&this.active.parent().closest(".ui-menu-item",this.element);t&&t.length&&(this._close(),this.focus(e,t))},expand:function(e){var t=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();t&&t.length&&(this._open(t.parent()),this._delay(function(){this.focus(e,t)}))},next:function(e){this._move("next","first",e)},previous:function(e){this._move("prev","last",e)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(e,t,i){var n;this.active&&(n="first"===e||"last"===e?this.active["first"===e?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[e+"All"](".ui-menu-item").eq(0)),n&&n.length&&this.active||(n=this.activeMenu.find(this.options.items)[t]()),this.focus(i,n)},nextPage:function(t){var i,n,r;return this.active?void(this.isLastItem()||(this._hasScroll()?(n=this.active.offset().top,r=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=e(this),0>i.offset().top-n-r}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items)[this.active?"last":"first"]()))):void this.next(t)},previousPage:function(t){var i,n,r;return this.active?void(this.isFirstItem()||(this._hasScroll()?(n=this.active.offset().top,r=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=e(this),i.offset().top-n+r>0}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items).first()))):void this.next(t)},_hasScroll:function(){return this.element.outerHeight()",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var t,i,n,r=this.element[0].nodeName.toLowerCase(),a="textarea"===r,o="input"===r;this.isMultiLine=!!a||!o&&this.element.prop("isContentEditable"),this.valueMethod=this.element[a||o?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete",(window.navigator.userAgent.indexOf("Edg")<0?"off":"new-password")),this._on(this.element,{keydown:function(r){if(this.element.prop("readOnly"))return t=!0,n=!0,void(i=!0);t=!1,n=!1,i=!1;var a=e.ui.keyCode;switch(r.keyCode){case a.PAGE_UP:t=!0,this._move("previousPage",r);break;case a.PAGE_DOWN:t=!0,this._move("nextPage",r);break;case a.UP:t=!0,this._keyEvent("previous",r);break;case a.DOWN:t=!0,this._keyEvent("next",r);break;case a.ENTER:this.menu.active&&(t=!0,r.preventDefault(),this.menu.select(r));break;case a.TAB:this.menu.active&&this.menu.select(r);break;case a.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(r),r.preventDefault());break;default:i=!0,this._searchTimeout(r)}},keypress:function(n){if(t)return t=!1,void((!this.isMultiLine||this.menu.element.is(":visible"))&&n.preventDefault());if(!i){var r=e.ui.keyCode;switch(n.keyCode){case r.PAGE_UP:this._move("previousPage",n);break;case r.PAGE_DOWN:this._move("nextPage",n);break;case r.UP:this._keyEvent("previous",n);break;case r.DOWN:this._keyEvent("next",n)}}},input:function(e){return n?(n=!1,void e.preventDefault()):void this._searchTimeout(e)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){return this.cancelBlur?void delete this.cancelBlur:(clearTimeout(this.searching),this.close(e),void this._change(e))}}),this._initSource(),this.menu=e("
      ").addClass("ui-autocomplete ui-front").appendTo(this._appendTo()).menu({role:null}).hide().menu("instance"),this._on(this.menu.element,{mousedown:function(t){t.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur});var i=this.menu.element[0];e(t.target).closest(".ui-menu-item").length||this._delay(function(){var t=this;this.document.one("mousedown",function(n){n.target===t.element[0]||n.target===i||e.contains(i,n.target)||t.close()})})},menufocus:function(t,i){var n,r;return this.isNewMenu&&(this.isNewMenu=!1,t.originalEvent&&/^mouse/.test(t.originalEvent.type))?(this.menu.blur(),void this.document.one("mousemove",function(){e(t.target).trigger(t.originalEvent)})):(r=i.item.data("ui-autocomplete-item"),!1!==this._trigger("focus",t,{item:r})&&t.originalEvent&&/^key/.test(t.originalEvent.type)&&this._value(r.value),n=i.item.attr("aria-label")||r.value,void(n&&e.trim(n).length&&(this.liveRegion.children().hide(),e("
      ").text(n).appendTo(this.liveRegion))))},menuselect:function(e,t){var i=t.item.data("ui-autocomplete-item"),n=this.previous;this.element[0]!==this.document[0].activeElement&&(this.element.focus(),this.previous=n,this._delay(function(){this.previous=n,this.selectedItem=i})),!1!==this._trigger("select",e,{item:i})&&this._value(i.value),this.term=this._value(),this.close(e),this.selectedItem=i}}),this.liveRegion=e("",{role:"status","aria-live":"assertive","aria-relevant":"additions"}).addClass("ui-helper-hidden-accessible").appendTo(this.document[0].body),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(e,t){this._super(e,t),"source"===e&&this._initSource(),"appendTo"===e&&this.menu.element.appendTo(this._appendTo()),"disabled"===e&&t&&this.xhr&&this.xhr.abort()},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t&&t[0]||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_initSource:function(){var t,i,n=this;e.isArray(this.options.source)?(t=this.options.source,this.source=function(i,n){n(e.ui.autocomplete.filter(t,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(t,r){n.xhr&&n.xhr.abort(),n.xhr=e.ajax({url:i,data:t,dataType:"json",success:function(e){r(e)},error:function(){r([])}})}):this.source=this.options.source},_searchTimeout:function(e){clearTimeout(this.searching),this.searching=this._delay(function(){var t=this.term===this._value(),i=this.menu.element.is(":visible"),n=e.altKey||e.ctrlKey||e.metaKey||e.shiftKey;(!t||t&&!i&&!n)&&(this.selectedItem=null,this.search(null,e))},this.options.delay)},search:function(e,t){return e=null!=e?e:this._value(),this.term=this._value(),e.length").text(i.label).appendTo(t)},_move:function(e,t){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(e)||this.menu.isLastItem()&&/^next/.test(e)?(this.isMultiLine||this._value(this.term),void this.menu.blur()):void this.menu[e](t):void this.search(null,t)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(e,t){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(e,t),t.preventDefault())}}),e.extend(e.ui.autocomplete,{escapeRegex:function(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(t,i){var n=RegExp(e.ui.autocomplete.escapeRegex(i),"i");return e.grep(t,function(e){return n.test(e.label||e.value||e)})}}),e.widget("ui.autocomplete",e.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(e){return e+(e>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(t){var i;this._superApply(arguments),this.options.disabled||this.cancelSearch||(i=t&&t.length?this.options.messages.results(t.length):this.options.messages.noResults,this.liveRegion.children().hide(),e("
      ").text(i).appendTo(this.liveRegion))}}),e.ui.autocomplete}),function(e){var t={picker:'
      {link:prev}{link:today}{link:next}
      {months}{popup:start}
      {button:clear}{button:close}
      {popup:end}
      ',monthRow:'
      {months}
      ',month:'
      {monthHeader:MM yyyy}
      {weekHeader}{weeks}
      ',weekHeader:"{days}",dayHeader:"{day}",week:"{days}",day:"{day}",monthSelector:".ui-datepicker-group",daySelector:"td",rtlClass:"ui-datepicker-rtl",multiClass:"ui-datepicker-multi",defaultClass:"ui-state-default",selectedClass:"ui-state-active",highlightedClass:"ui-state-hover",todayClass:"ui-state-highlight",otherMonthClass:"ui-datepicker-other-month",weekendClass:"ui-datepicker-week-end",commandClass:"ui-datepicker-cmd",commandButtonClass:"ui-state-default ui-corner-all",commandLinkClass:"",disabledClass:"ui-datepicker-disabled"};e.extend(e.datepick,{weekOfYearRenderer:e.extend({},e.datepick.defaultRenderer,{weekHeader:'{l10n:weekText}{days}',week:'{weekOfYear}{days}'}),themeRollerRenderer:t,themeRollerWeekOfYearRenderer:e.extend({},t,{weekHeader:'{l10n:weekText}{days}',week:'{weekOfYear}{days}'}),noWeekends:function(e){return{selectable:(e.getDay()||7)<6}},changeFirstDay:function(t,i){var n=e(this);t.find("th span").each(function(){var t=e(this).parent();t.is(".datepick-week")||t.is(".ui-state-hover")||e('
      '+e(this).text()+"").click(function(){var e=parseInt(this.className.replace(/^.*datepick-dow-(\d+).*$/,"$1"),10);n.datepick("option",{firstDay:e})}).replaceAll(this)})},hoverCallback:function(t){return function(i,n){var r=this,a=n.get("renderer");i.find(a.daySelector+" a, "+a.daySelector+" span").hover(function(){t.apply(r,[e.datepick.retrieveDate(r,this),"a"===this.nodeName.toLowerCase()])},function(){t.apply(r,[])})}},highlightWeek:function(t,i){var n=i.get("renderer");t.find(n.daySelector+" a, "+n.daySelector+" span").hover(function(){e(this).parents("tr").find(n.daySelector+" *").addClass(n.highlightedClass)},function(){e(this).parents("tr").find(n.daySelector+" *").removeClass(n.highlightedClass)})},showStatus:function(i,n){var r=n.get("renderer"),a=r.selectedClass===t.selectedClass,o=n.get("defaultStatus")||" ",s=e('
      '+o+"
      ").insertAfter(i.find(".datepick-month-row:last,.ui-datepicker-row-break:last"));i.find("*[title]").each(function(){var t=e(this).attr("title");e(this).removeAttr("title").hover(function(){s.text(t||o)},function(){s.text(o)})})},monthNavigation:function(i,n){for(var r=e(this),a=n.get("renderer"),o=a.selectedClass===t.selectedClass,s=n.curMinDate(),u=n.get("maxDate"),l=n.get("monthNames"),c=n.get("monthNamesShort"),d=n.drawDate.getMonth(),h=n.drawDate.getFullYear(),f='",f=e(f).insertAfter(i.find("div.datepick-nav,div.ui-datepicker-header:first")),f.find("a").click(function(){var t=e.datepick.retrieveDate(r[0],this);return f.slideToggle(function(){r.datepick("showMonth",t.getFullYear(),t.getMonth()+1)}),!1}),i.find("div.datepick-month-header,div.ui-datepicker-month-header").click(function(){f.slideToggle()}).css("cursor","pointer")},selectWeek:function(t,i){var n=e(this);t.find("td.datepick-week span,td.ui-state-hover span").each(function(){e(''+e(this).text()+"").click(function(){for(var t=n.datepick("retrieveDate",this),r=[t],a=1;a<7;a++)r.push(t=e.datepick.add(e.datepick.newDate(t),1,"d"));i.get("rangeSelect")&&r.splice(1,r.length-2),n.datepick("setDate",r).datepick("hide")}).replaceAll(this)})},selectMonth:function(t,i){var n=e(this);t.find("th.datepick-week span,th.ui-state-hover span").each(function(){e(''+e(this).text()+"").click(function(){for(var t=n.datepick("retrieveDate",e(this).parents("table").find("td:not(.datepick-week):not(.ui-state-hover) *:not(.datepick-other-month):not(.ui-datepicker-other-month)")[0]),r=[t],a=e.datepick.daysInMonth(t),o=1;o
      ').insertAfter(t.find(".datepick-month-row:last,.ui-datepicker-row-break:last")).children().click(function(){var i=t.find(".datepick-month-year:first").val().split("/");n.datepick("setDate",e.datepick.newDate(parseInt(i[1],10),parseInt(i[0],10),1)).datepick("hide")});t.find(".datepick-month-row table,.ui-datepicker-row-break table").remove()}})}(jQuery),function(e){e.datepick.regionalOptions.en={monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:e.datepick.defaultRenderer,prevText:"←",prevStatus:"Show the previous month",prevJumpText:"<<",prevJumpStatus:"Show the previous year",nextText:"→",nextStatus:"Show the next month",nextJumpText:">>",nextJumpStatus:"Show the next year",currentText:"Current",currentStatus:"Show the current month",todayText:"Today",todayStatus:"Current month",clearText:"Clear",clearStatus:"Erase the current date",closeText:"Done",closeStatus:"Close without change",yearStatus:"Show a different year",monthStatus:"Show a different month",weekText:"Wk",weekStatus:"Week of the year",dayStatus:"Select DD, M d",defaultStatus:"Select a date",isRTL:!1},e.datepick.regionalOptions.fr={monthNames:["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"],monthNamesShort:["Jan","Fév","Mar","Avr","Mai","Jun","Jul","Aoû","Sep","Oct","Nov","Déc"],dayNames:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],dayNamesShort:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],dayNamesMin:["Di","Lu","Ma","Me","Je","Ve","Sa"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:e.datepick.defaultRenderer,prevText:"←",prevStatus:"Voir le mois précédent",prevJumpText:"<<",prevJumpStatus:"Voir l'année précédent",nextText:"→",nextStatus:"Voir le mois suivant",nextJumpText:">>",nextJumpStatus:"Voir l'année suivant",currentText:"Courant",currentStatus:"Voir le mois courant",todayText:"Aujourd'hui",todayStatus:"Mois courant",clearText:"Effacer",clearStatus:"Effacer la date sélectionnée",closeText:"Fermer",closeStatus:"Fermer sans modifier",yearStatus:"Voir une autre année",monthStatus:"Voir un autre mois",weekText:"Sm",weekStatus:"Semaine de l'année",dayStatus:"'Choisir' le DD d MM",defaultStatus:"Choisir la date",isRTL:!1},e.datepick.regionalOptions.nl={monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","maa","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:e.datepick.defaultRenderer,prevText:"←",prevStatus:"Bekijk de vorige maand",prevJumpText:"«",nextJumpStatus:"Bekijk het vorige jaar",nextText:"→",nextStatus:"Bekijk de volgende maand",nextJumpText:"»",nextJumpStatus:"Bekijk het volgende jaar",currentText:"Vandaag",currentStatus:"Bekijk de huidige maand",todayText:"Vandaag",todayStatus:"Huidige maand",clearText:"Wissen",clearStatus:"Wis de huidige datum",closeText:"Sluiten",closeStatus:"Sluit zonder verandering",yearStatus:"Bekijk een ander jaar",monthStatus:"Bekijk een andere maand",weekText:"Wk",weekStatus:"Week van het jaar",dayStatus:"dd/mm/yyyy",defaultStatus:"Kies een datum",isRTL:!1};var t=function(e){e=e.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");var t=new RegExp("[\\?&]"+e+"=([^&#]*)"),i=t.exec(location.search);return null==i?"":decodeURIComponent(i[1].replace(/\+/g," "))},i=function(e){switch(e){case"url":return t("lang")||null;case"cookie":var i="; "+document.cookie,n=i.split("; LANGUAGE=");if(2==n.length)return n.pop().split(";").shift()}return null},n=i("url")||i("cookie")||"en";n=n.toLowerCase(),"fr"===n?e.datepick.setDefaults(e.datepick.regionalOptions.fr):"nl"===n?e.datepick.setDefaults(e.datepick.regionalOptions.nl):e.datepick.setDefaults(e.datepick.regionalOptions.en)}(jQuery),!window.XMLHttpRequest||window.FileAPI&&FileAPI.shouldLoad||(window.XMLHttpRequest.prototype.setRequestHeader=function(e){return function(t,i){if("__setXHR_"===t){var n=i(this);n instanceof Function&&n(this)}else e.apply(this,arguments)}}(window.XMLHttpRequest.prototype.setRequestHeader));var ngFileUpload=angular.module("ngFileUpload",[]);ngFileUpload.version="12.2.13",ngFileUpload.service("UploadBase",["$http","$q","$timeout",function(e,t,i){function n(n){function r(e){l.notify&&l.notify(e),c.progressFunc&&i(function(){c.progressFunc(e)})}function s(e){return null!=n._start&&o?{loaded:e.loaded+n._start,total:n._file&&n._file.size||e.total,type:e.type,config:n,lengthComputable:!0,target:e.target}:e}function u(){e(n).then(function(e){if(o&&n._chunkSize&&!n._finished&&n._file){var t=n._file&&n._file.size||0;r({loaded:Math.min(n._end,t),total:t,config:n,type:"progress"}),a.upload(n,!0)}else n._finished&&delete n._finished,l.resolve(e)},function(e){l.reject(e)},function(e){l.notify(e)})}n.method=n.method||"POST",n.headers=n.headers||{};var l=n._deferred=n._deferred||t.defer(),c=l.promise;return n.disableProgress||(n.headers.__setXHR_=function(){return function(e){e&&e.upload&&e.upload.addEventListener&&(n.__XHR=e,n.xhrFn&&n.xhrFn(e),e.upload.addEventListener("progress",function(e){e.config=n,r(s(e))},!1),e.upload.addEventListener("load",function(e){e.lengthComputable&&(e.config=n,r(s(e)))},!1))}}),o?n._chunkSize&&n._end&&!n._finished?(n._start=n._end,n._end+=n._chunkSize,u()):n.resumeSizeUrl?e.get(n.resumeSizeUrl).then(function(e){n.resumeSizeResponseReader?n._start=n.resumeSizeResponseReader(e.data):n._start=parseInt((null==e.data.size?e.data:e.data.size).toString()),n._chunkSize&&(n._end=n._start+n._chunkSize),u()},function(e){throw e}):n.resumeSize?n.resumeSize().then(function(e){n._start=e,n._chunkSize&&(n._end=n._start+n._chunkSize),u()},function(e){throw e}):(n._chunkSize&&(n._start=0,n._end=n._start+n._chunkSize),u()):u(),c.success=function(e){return c.then(function(t){e(t.data,t.status,t.headers,n)}),c},c.error=function(e){return c.then(null,function(t){e(t.data,t.status,t.headers,n)}),c},c.progress=function(e){return c.progressFunc=e,c.then(null,null,function(t){e(t)}),c},c.abort=c.pause=function(){return n.__XHR&&i(function(){n.__XHR.abort()}),c},c.xhr=function(e){return n.xhrFn=function(t){return function(){t&&t.apply(c,arguments),e.apply(c,arguments)}}(n.xhrFn),c},a.promisesCount++,c["finally"]&&c["finally"]instanceof Function&&c["finally"](function(){a.promisesCount--}),c}function r(e){var t={};for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return t}var a=this;a.promisesCount=0,this.isResumeSupported=function(){return window.Blob&&window.Blob.prototype.slice};var o=this.isResumeSupported();this.isUploadInProgress=function(){return a.promisesCount>0},this.rename=function(e,t){return e.ngfName=t,e},this.jsonBlob=function(e){null==e||angular.isString(e)||(e=JSON.stringify(e));var t=new window.Blob([e],{type:"application/json"});return t._ngfBlob=!0,t},this.json=function(e){return angular.toJson(e)},this.isFile=function(e){return null!=e&&(e instanceof window.Blob||e.flashId&&e.name&&e.size)},this.upload=function(e,t){function i(t,i){if(t._ngfBlob)return t;if(e._file=e._file||t,null!=e._start&&o){e._end&&e._end>=t.size&&(e._finished=!0,e._end=t.size);var n=t.slice(e._start,e._end||t.size);return n.name=t.name,n.ngfName=t.ngfName,e._chunkSize&&(i.append("_chunkSize",e._chunkSize),i.append("_currentChunkSize",e._end-e._start),i.append("_chunkNumber",Math.floor(e._start/e._chunkSize)),i.append("_totalSize",e._file.size)),n}return t}function s(t,n,r){if(void 0!==n)if(angular.isDate(n)&&(n=n.toISOString()),angular.isString(n))t.append(r,n);else if(a.isFile(n)){var o=i(n,t),u=r.split(",");u[1]&&(o.ngfName=u[1].replace(/^\s+|\s+$/g,""),r=u[0]),e._fileKey=e._fileKey||r,t.append(r,o,o.ngfName||o.name)}else if(angular.isObject(n)){if(n.$$ngfCircularDetection)throw"ngFileUpload: Circular reference in config.data. Make sure specified data for Upload.upload() has no circular reference: "+r;n.$$ngfCircularDetection=!0;try{for(var l in n)if(n.hasOwnProperty(l)&&"$$ngfCircularDetection"!==l){var c=null==e.objectKey?"[i]":e.objectKey;n.length&&parseInt(l)>-1&&(c=null==e.arrayKey?c:e.arrayKey),s(t,n[l],r+c.replace(/[ik]/g,l))}}finally{delete n.$$ngfCircularDetection}}else t.append(r,n)}function u(){e._chunkSize=a.translateScalars(e.resumeChunkSize),e._chunkSize=e._chunkSize?parseInt(e._chunkSize.toString()):null,e.headers=e.headers||{},e.headers["Content-Type"]=void 0,e.transformRequest=e.transformRequest?angular.isArray(e.transformRequest)?e.transformRequest:[e.transformRequest]:[],e.transformRequest.push(function(t){var i,n=new window.FormData;t=t||e.fields||{},e.file&&(t.file=e.file);for(i in t)if(t.hasOwnProperty(i)){var r=t[i];e.formDataAppender?e.formDataAppender(n,i,r):s(n,r,i)}return n})}return t||(e=r(e)),e._isDigested||(e._isDigested=!0,u()),n(e)},this.http=function(t){return t=r(t),t.transformRequest=t.transformRequest||function(t){return window.ArrayBuffer&&t instanceof window.ArrayBuffer||t instanceof window.Blob?t:e.defaults.transformRequest[0].apply(this,arguments)},t._chunkSize=a.translateScalars(t.resumeChunkSize),t._chunkSize=t._chunkSize?parseInt(t._chunkSize.toString()):null,n(t)},this.translateScalars=function(e){if(angular.isString(e)){if(e.search(/kb/i)===e.length-2)return parseFloat(1024*e.substring(0,e.length-2));if(e.search(/mb/i)===e.length-2)return parseFloat(1048576*e.substring(0,e.length-2));if(e.search(/gb/i)===e.length-2)return parseFloat(1073741824*e.substring(0,e.length-2));if(e.search(/b/i)===e.length-1)return parseFloat(e.substring(0,e.length-1));if(e.search(/s/i)===e.length-1)return parseFloat(e.substring(0,e.length-1));if(e.search(/m/i)===e.length-1)return parseFloat(60*e.substring(0,e.length-1));if(e.search(/h/i)===e.length-1)return parseFloat(3600*e.substring(0,e.length-1))}return e},this.urlToBlob=function(i){var n=t.defer();return e({url:i,method:"get",responseType:"arraybuffer"}).then(function(e){var t=new Uint8Array(e.data),r=e.headers("content-type")||"image/WebP",a=new window.Blob([t],{type:r}),o=i.match(/.*\/(.+?)(\?.*)?$/);o.length>1&&(a.name=o[1]),n.resolve(a)},function(e){n.reject(e)}),n.promise},this.setDefaults=function(e){this.defaults=e||{}},this.defaults={},this.version=ngFileUpload.version}]),ngFileUpload.service("Upload",["$parse","$timeout","$compile","$q","UploadExif",function(e,t,i,n,r){function a(e,t,i){var r=[u.emptyPromise()];return angular.forEach(e,function(n,a){0===n.type.indexOf("image/jpeg")&&u.attrGetter("ngfFixOrientation",t,i,{$file:n})&&r.push(u.happyPromise(u.applyExifRotation(n),n).then(function(t){e.splice(a,1,t)}))}),n.all(r)}function o(e,t,i,r){var a=u.attrGetter("ngfResize",t,i);if(!a||!u.isResizeSupported()||!e.length)return u.emptyPromise();if(a instanceof Function){var o=n.defer();return a(e).then(function(n){s(n,e,t,i,r).then(function(e){o.resolve(e)},function(e){o.reject(e)})},function(e){o.reject(e)})}return s(a,e,t,i,r)}function s(e,t,i,r,a){function o(n,o){if(0===n.type.indexOf("image")){if(e.pattern&&!u.validatePattern(n,e.pattern))return;e.resizeIf=function(e,t){return u.attrGetter("ngfResizeIf",i,r,{$width:e,$height:t,$file:n})};var l=u.resize(n,e);s.push(l),l.then(function(e){t.splice(o,1,e)},function(e){n.$error="resize",(n.$errorMessages=n.$errorMessages||{}).resize=!0,n.$errorParam=(e?(e.message?e.message:e)+": ":"")+(n&&n.name),a.$ngfValidations.push({name:"resize",valid:!1}),u.applyModelValidation(a,t)})}}for(var s=[u.emptyPromise()],l=0;l-1},u.emptyPromise=function(){var e=n.defer(),i=arguments;return t(function(){e.resolve.apply(e,i)}),e.promise},u.rejectPromise=function(){var e=n.defer(),i=arguments;return t(function(){e.reject.apply(e,i)}),e.promise},u.happyPromise=function(e,i){var r=n.defer();return e.then(function(e){r.resolve(e)},function(e){t(function(){throw e}),r.resolve(i)}),r.promise},u.updateModel=function(i,n,r,s,l,c,d){function h(a,o,l,d,h){n.$$ngfPrevValidFiles=a,n.$$ngfPrevInvalidFiles=o;var f=a&&a.length?a[0]:null,p=o&&o.length?o[0]:null;i&&(u.applyModelValidation(i,a),i.$setViewValue(h?f:a)),s&&e(s)(r,{$files:a,$file:f,$newFiles:l,$duplicateFiles:d,$invalidFiles:o,$invalidFile:p,$event:c});var m=u.attrGetter("ngfModelInvalid",n);m&&t(function(){e(m).assign(r,h?p:o)}),t(function(){})}function f(){function e(e,t){return e.name===t.name&&(e.$ngfOrigSize||e.size)===(t.$ngfOrigSize||t.size)&&e.type===t.type}function t(t){var i;for(i=0;i-1&&(x.splice(n,1),w.push(i)),e()}}})}var g,v,y,b=[],w=[],x=[];v=n.$$ngfPrevValidFiles||[],y=n.$$ngfPrevInvalidFiles||[],i&&i.$modelValue&&(v=p(i.$modelValue));var _=u.attrGetter("ngfKeep",n,r);g=(l||[]).slice(0),"distinct"!==_&&u.attrGetter("ngfKeepDistinct",n,r)!==!0||f(n,r);var k=!_&&!u.attrGetter("ngfMultiple",n,r)&&!u.attrGetter("multiple",n);if(!_||g.length){u.attrGetter("ngfBeforeModelChange",n,r,{$files:l,$file:l&&l.length?l[0]:null,$newFiles:g,$duplicateFiles:b,$event:c});var S=u.attrGetter("ngfValidateAfterResize",n,r),D=u.attrGetter("ngfModelOptions",n,r);u.validate(g,_?v.length:0,i,n,r).then(function(e){d?h(g,[],l,b,k):(D&&D.allowInvalid||S?x=g:(x=e.validFiles,w=e.invalidFiles),u.attrGetter("ngfFixOrientation",n,r)&&u.isExifSupported()?a(x,n,r).then(function(){m()}):m())})}},u}]),ngFileUpload.directive("ngfSelect",["$parse","$timeout","$compile","Upload",function(e,t,i,n){function r(e){var t=e.match(/Android[^\d]*(\d+)\.(\d+)/);if(t&&t.length>2){var i=n.defaults.androidFixMinorVersion||4;return parseInt(t[1])<4||parseInt(t[1])===i&&parseInt(t[2])'),i=angular.element("");return i.css("visibility","hidden").css("position","absolute").css("overflow","hidden").css("width","0px").css("height","0px").css("border","none").css("margin","0px").css("padding","0px").attr("tabindex","-1"),f(e,i),o.push({el:t,ref:i}),document.body.appendChild(i.append(e)[0]),e}function m(i){if(t.attr("disabled"))return!1;if(!b("ngfSelectDisabled",e)){var n=g(i);if(null!=n)return n;v(i);try{c()||document.body.contains(k[0])||(o.push({el:t,ref:k.parent()}),document.body.appendChild(k.parent()[0]),k.bind("change",h))}catch(a){}return r(navigator.userAgent)?setTimeout(function(){k[0].click()},0):k[0].click(),!1}}function g(e){var t=e.changedTouches||e.originalEvent&&e.originalEvent.changedTouches;if(t){if("touchstart"===e.type)return _=t[0].clientX,x=t[0].clientY,!0;if("touchend"===e.type){var i=t[0].clientX,n=t[0].clientY;if(Math.abs(i-_)>20||Math.abs(n-x)>20)return e.stopPropagation(),e.preventDefault(),!1}return!0}}function v(t){l.shouldUpdateOn("click",i,e)&&k.val()&&(k.val(null),l.updateModel(n,i,e,d(),null,t,!0))}function y(e){if(k&&!k.attr("__ngf_ie10_Fix_")){if(!k[0].parentNode)return void(k=null);e.preventDefault(),e.stopPropagation(),k.unbind("click");var t=k.clone();return k.replaceWith(t),k=t,k.attr("__ngf_ie10_Fix_","true"),k.bind("change",h),k.bind("click",y),k[0].click(),!1}k.removeAttr("__ngf_ie10_Fix_")}var b=function(e,t){return l.attrGetter(e,i,t)};l.registerModelChangeValidator(n,i,e);var w=[];b("ngfMultiple")&&w.push(e.$watch(b("ngfMultiple"),function(){k.attr("multiple",b("ngfMultiple",e))})),b("ngfCapture")&&w.push(e.$watch(b("ngfCapture"),function(){k.attr("capture",b("ngfCapture",e))})),b("ngfAccept")&&w.push(e.$watch(b("ngfAccept"),function(){k.attr("accept",b("ngfAccept",e))})),w.push(i.$observe("accept",function(){k.attr("accept",b("accept"))}));var x=0,_=0,k=t;c()||(k=p()),k.bind("change",h),c()?t.bind("click",v):t.bind("click touchstart touchend",m),navigator.appVersion.indexOf("MSIE 10")!==-1&&k.bind("click",y),n&&n.$formatters.push(function(e){return null!=e&&0!==e.length||k.val()&&k.val(null),e}),e.$on("$destroy",function(){c()||k.parent().remove(),angular.forEach(w,function(e){e()})}),s(function(){for(var e=0;e-1&&d.height&&d.height.indexOf("px")>-1&&(c={width:parseInt(d.width.slice(0,-2)),height:parseInt(d.height.slice(0,-2))})}return angular.isString(i)?(r.removeClass("ng-hide"),u?r.css("background-image","url('"+i+"')"):r.attr("src",i)):void(!i||!i.type||0!==i.type.search(e(r[0]))||u&&0!==i.type.indexOf("image")?r.addClass("ng-hide"):c&&t.isResizeSupported()?(c.resizeIf=function(e,r){return t.attrGetter("ngfResizeIf",a,n,{$width:e,$height:r,$file:i})},t.resize(i,c).then(function(e){l(e)},function(e){throw e})):l(i))});n.$on("$destroy",function(){i()})})}ngFileUpload.service("UploadDataUrl",["UploadBase","$timeout","$q",function(e,t,i){var n=e;return n.base64DataUrl=function(e){if(angular.isArray(e)){var t=i.defer(),r=0;return angular.forEach(e,function(i){n.dataUrl(i,!0)["finally"](function(){if(r++,r===e.length){var i=[];angular.forEach(e,function(e){i.push(e.$ngfDataUrl)}),t.resolve(i,e)}})}),t.promise}return n.dataUrl(e,!0)},n.dataUrl=function(e,r){if(!e)return n.emptyPromise(e,e);if(r&&null!=e.$ngfDataUrl||!r&&null!=e.$ngfBlobUrl)return n.emptyPromise(r?e.$ngfDataUrl:e.$ngfBlobUrl,e);var a=r?e.$$ngfDataUrlPromise:e.$$ngfBlobUrlPromise;if(a)return a;var o=i.defer();return t(function(){if(window.FileReader&&e&&(!window.FileAPI||navigator.userAgent.indexOf("MSIE 8")===-1||e.size<2e4)&&(!window.FileAPI||navigator.userAgent.indexOf("MSIE 9")===-1||e.size<4e6)){var i=window.URL||window.webkitURL;if(i&&i.createObjectURL&&!r){var a;try{a=i.createObjectURL(e)}catch(s){return void t(function(){e.$ngfBlobUrl="",o.reject()})}t(function(){if(e.$ngfBlobUrl=a,a){o.resolve(a,e),n.blobUrls=n.blobUrls||[],n.blobUrlsTotalSize=n.blobUrlsTotalSize||0,n.blobUrls.push({url:a,size:e.size}),n.blobUrlsTotalSize+=e.size||0;for(var t=n.defaults.blobUrlsMaxMemory||268435456,r=n.defaults.blobUrlsMaxQueueSize||200;(n.blobUrlsTotalSize>t||n.blobUrls.length>r)&&n.blobUrls.length>1;){var s=n.blobUrls.splice(0,1)[0];i.revokeObjectURL(s.url),n.blobUrlsTotalSize-=s.size}}})}else{var u=new FileReader;u.onload=function(i){t(function(){e.$ngfDataUrl=i.target.result,o.resolve(i.target.result,e),t(function(){delete e.$ngfDataUrl},1e3)})},u.onerror=function(){t(function(){e.$ngfDataUrl="",o.reject()})},u.readAsDataURL(e)}}else t(function(){e[r?"$ngfDataUrl":"$ngfBlobUrl"]="",o.reject()})}),a=r?e.$$ngfDataUrlPromise=o.promise:e.$$ngfBlobUrlPromise=o.promise,a["finally"](function(){delete e[r?"$$ngfDataUrlPromise":"$$ngfBlobUrlPromise"]}),a},n}]),ngFileUpload.directive("ngfSrc",["Upload","$timeout",function(e,i){return{restrict:"AE",link:function(n,r,a){t(e,i,n,r,a,"ngfSrc",e.attrGetter("ngfResize",a,n),!1)}}}]),ngFileUpload.directive("ngfBackground",["Upload","$timeout",function(e,i){return{restrict:"AE",link:function(n,r,a){t(e,i,n,r,a,"ngfBackground",e.attrGetter("ngfResize",a,n),!0)}}}]),ngFileUpload.directive("ngfThumbnail",["Upload","$timeout",function(e,i){return{restrict:"AE",link:function(n,r,a){var o=e.attrGetter("ngfSize",a,n);t(e,i,n,r,a,"ngfThumbnail",o,e.attrGetter("ngfAsBackground",a,n))}}}]),ngFileUpload.config(["$compileProvider",function(e){e.imgSrcSanitizationWhitelist&&e.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|webcal|local|file|data|blob):/),e.aHrefSanitizationWhitelist&&e.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|webcal|local|file|data|blob):/)}]),ngFileUpload.filter("ngfDataUrl",["UploadDataUrl","$sce",function(e,t){return function(i,n,r){if(angular.isString(i))return t.trustAsResourceUrl(i);var a=i&&((n?i.$ngfDataUrl:i.$ngfBlobUrl)||i.$ngfDataUrl);return i&&!a?(!i.$ngfDataUrlFilterInProgress&&angular.isObject(i)&&(i.$ngfDataUrlFilterInProgress=!0,e.dataUrl(i,n)),""):(i&&delete i.$ngfDataUrlFilterInProgress,(i&&a?r?t.trustAsResourceUrl(a):a:i)||"")}}])}(),ngFileUpload.service("UploadValidate",["UploadDataUrl","$q","$timeout",function(e,t,i){function n(e){var t="",i=[];if(e.length>2&&"/"===e[0]&&"/"===e[e.length-1])t=e.substring(1,e.length-1);else{var r=e.split(",");if(r.length>1)for(var a=0;a|:\\-]","g"),"\\$&")+"$",t=t.replace(/\\\*/g,".*").replace(/\\\?/g,"."))}return{regexp:t,excludes:i}}function r(e,t){null==t||e.$dirty||(e.$setDirty?e.$setDirty():e.$dirty=!0)}var a=e;return a.validatePattern=function(e,t){if(!t)return!0;var i=n(t),r=!0;if(i.regexp&&i.regexp.length){var a=new RegExp(i.regexp,"i");r=null!=e.type&&a.test(e.type)||null!=e.name&&a.test(e.name)}for(var o=i.excludes.length;o--;){var s=new RegExp(i.excludes[o],"i");r=r&&(null==e.type||s.test(e.type))&&(null==e.name||s.test(e.name))}return r},a.ratioToFloat=function(e){var t=e.toString(),i=t.search(/[x:]/i);return t=i>-1?parseFloat(t.substring(0,i))/parseFloat(t.substring(i+1)):parseFloat(t)},a.registerModelChangeValidator=function(e,t,i){e&&e.$formatters.push(function(n){if(e.$dirty){var r=n;n&&!angular.isArray(n)&&(r=[n]),a.validate(r,0,e,t,i).then(function(){a.applyModelValidation(e,r)})}return n})},a.applyModelValidation=function(e,t){r(e,t),angular.forEach(e.$ngfValidations,function(t){e.$setValidity(t.name,t.valid)})},a.getValidationAttr=function(e,t,i,n,r){ var o="ngf"+i[0].toUpperCase()+i.substr(1),s=a.attrGetter(o,e,t,{$file:r});if(null==s&&(s=a.attrGetter("ngfValidate",e,t,{$file:r}))){var u=(n||i).split(".");s=s[u[0]],u.length>1&&(s=s&&s[u[1]])}return s},a.validate=function(e,i,n,r,o){function s(t,i,s){if(e){for(var u=e.length,l=null;u--;){var f=e[u];if(f){var p=a.getValidationAttr(r,o,t,i,f);null!=p&&(s(f,p,u)||(c.indexOf(t)===-1?(f.$error=t,(f.$errorMessages=f.$errorMessages||{})[t]=!0,f.$errorParam=p,h.indexOf(f)===-1&&h.push(f),d||e.splice(u,1),l=!1):e.splice(u,1)))}}null!==l&&n.$ngfValidations.push({name:t,valid:l})}}function u(i,s,u,f,p){function m(t,n,r){function a(a){if(a())if(c.indexOf(i)===-1){if(n.$error=i,(n.$errorMessages=n.$errorMessages||{})[i]=!0,n.$errorParam=r,h.indexOf(n)===-1&&h.push(n),!d){var o=e.indexOf(n);o>-1&&e.splice(o,1)}t.resolve(!1)}else{var s=e.indexOf(n);s>-1&&e.splice(s,1),t.resolve(!0)}else t.resolve(!0)}null!=r?f(n,r).then(function(e){a(function(){return!p(e,r)})},function(){a(function(){return l("ngfValidateForce",{$file:n})})}):t.resolve(!0)}var g=[a.emptyPromise(!0)];e&&(e=void 0===e.length?[e]:e,angular.forEach(e,function(e){var n=t.defer();return g.push(n.promise),!u||null!=e.type&&0===e.type.search(u)?void("dimensions"===i&&null!=a.attrGetter("ngfDimensions",r)?a.imageDimensions(e).then(function(t){m(n,e,l("ngfDimensions",{$file:e,$width:t.width,$height:t.height}))},function(){n.resolve(!1)}):"duration"===i&&null!=a.attrGetter("ngfDuration",r)?a.mediaDuration(e).then(function(t){m(n,e,l("ngfDuration",{$file:e,$duration:t}))},function(){n.resolve(!1)}):m(n,e,a.getValidationAttr(r,o,i,s,e))):void n.resolve(!0)}));var v=t.defer();return t.all(g).then(function(e){for(var t=!0,r=0;r=a.translateScalars(t)}),s("maxSize","size.max",function(e,t){return e.size-.1<=a.translateScalars(t)});var f=0;if(s("maxTotalSize",null,function(t,i){return f+=t.size,!(f>a.translateScalars(i))||(e.splice(0,e.length),!1)}),s("validateFn",null,function(e,t){return t===!0||null===t||""===t}),!e.length)return a.emptyPromise({validFiles:[],invalidFiles:h});var p=t.defer(),m=[];return m.push(u("maxHeight","height.max",/image/,this.imageDimensions,function(e,t){return e.height<=t})),m.push(u("minHeight","height.min",/image/,this.imageDimensions,function(e,t){return e.height>=t})),m.push(u("maxWidth","width.max",/image/,this.imageDimensions,function(e,t){return e.width<=t})),m.push(u("minWidth","width.min",/image/,this.imageDimensions,function(e,t){return e.width>=t})),m.push(u("dimensions",null,/image/,function(e,t){return a.emptyPromise(t)},function(e){return e})),m.push(u("ratio",null,/image/,this.imageDimensions,function(e,t){for(var i=t.toString().split(","),n=!1,r=0;r-1e-4})),m.push(u("maxDuration","duration.max",/audio|video/,this.mediaDuration,function(e,t){return e<=a.translateScalars(t)})),m.push(u("minDuration","duration.min",/audio|video/,this.mediaDuration,function(e,t){return e>=a.translateScalars(t)})),m.push(u("duration",null,/audio|video/,function(e,t){return a.emptyPromise(t)},function(e){return e})),m.push(u("validateAsyncFn",null,null,function(e,t){return t},function(e){return e===!0||null===e||""===e})),t.all(m).then(function(){if(d)for(var t=0;t10?a():o())},1e3)}var s=angular.element("").attr("src",t).css("visibility","hidden").css("position","fixed").css("max-width","none !important").css("max-height","none !important");s.on("load",n),s.on("error",a);var u=0;o(),angular.element(document.getElementsByTagName("body")[0]).append(s)},function(){r.reject("load error")})}),e.$ngfDimensionPromise=r.promise,e.$ngfDimensionPromise["finally"](function(){delete e.$ngfDimensionPromise}),e.$ngfDimensionPromise},a.mediaDuration=function(e){if(e.$ngfDuration){var n=t.defer();return i(function(){n.resolve(e.$ngfDuration)}),n.promise}if(e.$ngfDurationPromise)return e.$ngfDurationPromise;var r=t.defer();return i(function(){return 0!==e.type.indexOf("audio")&&0!==e.type.indexOf("video")?void r.reject("not media"):void a.dataUrl(e).then(function(t){function n(){var t=s[0].duration;e.$ngfDuration=t,s.remove(),r.resolve(t)}function a(){s.remove(),r.reject("load error")}function o(){i(function(){s[0].parentNode&&(s[0].duration?n():u>10?a():o())},1e3)}var s=angular.element(0===e.type.indexOf("audio")?"