',c.querySelector("svg img")?d:b):function(b){b=" "+b;try{b=encodeURI(b)}catch(d){return}var e=new a.XMLHttpRequest;e.responseType="document";e.open("GET","data:text/html;charset=utf-8,"+b,!1);e.send(null);b=e.response.body;b.firstChild.remove();return b}}(s,s.document)}).info({angularVersion:"1.8.0"});c.module("ngSanitize").filter("linky",["$sanitize",function(f){var h=/((s?ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
+t=/^mailto:/i,q=c.$$minErr("linky"),s=c.isDefined,A=c.isFunction,v=c.isObject,y=c.isString;return function(c,z,u){function r(c){c&&l.push(P(c))}function x(c,g){var f,a=p(c);l.push("');r(g);l.push(" ")}if(null==c||""===c)return c;if(!y(c))throw q("notstring",c);for(var p=A(u)?u:v(u)?function(){return u}:function(){return{}},n=c,l=[],w,m;c=n.match(h);)w=c[0],c[2]||
+c[4]||(w=(c[3]?"http://":"mailto:")+w),m=c.index,r(n.substr(0,m)),x(w,c[0].replace(t,"")),n=n.substring(m+c[0].length);r(n);return f(l.join(""))}}])})(window,window.angular);
+//# sourceMappingURL=angular-sanitize.min.js.map
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-sanitize/angular-sanitize.min.js.map b/keycloak/themes/ashvin/common/resources/node_modules/angular-sanitize/angular-sanitize.min.js.map
new file mode 100644
index 00000000..133f378a
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-sanitize/angular-sanitize.min.js.map
@@ -0,0 +1,8 @@
+{
+"version":3,
+"file":"angular-sanitize.min.js",
+"lineCount":17,
+"mappings":"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",
+"sources":["angular-sanitize.js"],
+"names":["window","angular","sanitizeText","chars","buf","htmlSanitizeWriter","writer","noop","join","$sanitizeMinErr","$$minErr","bind","extend","forEach","isArray","isDefined","lowercase","nodeContains","htmlParser","module","provider","$SanitizeProvider","stringToMap","str","lowercaseKeys","arrayToMap","split","items","obj","i","length","addElementsTo","elementsMap","newElements","attrToMap","attrs","map","ii","attr","name","value","encodeEntities","replace","SURROGATE_PAIR_REGEXP","hi","charCodeAt","low","NON_ALPHANUMERIC_REGEXP","stripCustomNsAttrs","node","nodeType","Node","ELEMENT_NODE","attributes","l","attrNode","attrName","toLowerCase","lastIndexOf","removeAttributeNode","nextNode","firstChild","getNonDescendant","propName","call","outerHTML","outerText","hasBeenInstantiated","svgEnabled","$get","$$sanitizeUri","validElements","svgElements","html","uri","isImage","test","enableSvg","this.enableSvg","addValidElements","this.addValidElements","elements","htmlElements","voidElements","htmlVoidElements","addValidAttrs","this.addValidAttrs","validAttrs","$$lowercase","htmlParserImpl","handler","undefined","inertBodyElement","getInertBodyElement","mXSSAttempts","innerHTML","start","nodeName","textContent","end","removeChild","htmlSanitizeWriterImpl","uriValidator","ignoreCurrentElement","out","push","tag","blockedElements","key","lkey","uriAttrs","prototype","contains","arg","compareDocumentPosition","optionalEndTagBlockElements","optionalEndTagInlineElements","optionalEndTagElements","blockElements","inlineElements","htmlAttrs","svgAttrs","document","getInertBodyElement_DOMParser","body","parseFromString","DOMParser","remove","e","getInertBodyElement_InertDocument","documentMode","inertDocument","implementation","createHTMLDocument","querySelector","documentElement","getDocumentElement","getInertBodyElement_XHR","encodeURI","xhr","XMLHttpRequest","responseType","open","send","response","info","angularVersion","filter","$sanitize","LINKY_URL_REGEXP","MAILTO_REGEXP","linkyMinErr","isFunction","isObject","isString","text","target","addText","addLink","url","linkAttributes","attributesFn","getAttributesObject","getEmptyAttributesObject","raw","match","index","substr","substring"]
+}
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-sanitize/bower.json b/keycloak/themes/ashvin/common/resources/node_modules/angular-sanitize/bower.json
new file mode 100644
index 00000000..d72cde18
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-sanitize/bower.json
@@ -0,0 +1,10 @@
+{
+ "name": "angular-sanitize",
+ "version": "1.8.0",
+ "license": "MIT",
+ "main": "./angular-sanitize.js",
+ "ignore": [],
+ "dependencies": {
+ "angular": "1.8.0"
+ }
+}
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-sanitize/index.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-sanitize/index.js
new file mode 100644
index 00000000..dd5d22e4
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-sanitize/index.js
@@ -0,0 +1,2 @@
+require('./angular-sanitize');
+module.exports = 'ngSanitize';
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate-loader-url/angular-translate-loader-url.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate-loader-url/angular-translate-loader-url.js
new file mode 100644
index 00000000..a2e881e1
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate-loader-url/angular-translate-loader-url.js
@@ -0,0 +1,73 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 The angular-translate team, Pascal Precht; Licensed MIT
+ */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module unless amdModuleId is set
+ define([], function () {
+ return (factory());
+ });
+ } else if (typeof module === 'object' && module.exports) {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like environments that support module.exports,
+ // like Node.
+ module.exports = factory();
+ } else {
+ factory();
+ }
+}(this, function () {
+
+$translateUrlLoader.$inject = ['$q', '$http'];
+angular.module('pascalprecht.translate')
+/**
+ * @ngdoc object
+ * @name pascalprecht.translate.$translateUrlLoader
+ * @requires $q
+ * @requires $http
+ *
+ * @description
+ * Creates a loading function for a typical dynamic url pattern:
+ * "locale.php?lang=en_US", "locale.php?lang=de_DE", "locale.php?language=nl_NL" etc.
+ * Prefixing the specified url, the current requested, language id will be applied
+ * with "?{queryParameter}={key}".
+ * Using this service, the response of these urls must be an object of
+ * key-value pairs.
+ *
+ * @param {object} options Options object, which gets the url, key and
+ * optional queryParameter ('lang' is used by default).
+ */
+.factory('$translateUrlLoader', $translateUrlLoader);
+
+function $translateUrlLoader($q, $http) {
+
+ 'use strict';
+
+ return function (options) {
+
+ if (!options || !options.url) {
+ throw new Error('Couldn\'t use urlLoader since no url is given!');
+ }
+
+ var requestParams = {};
+
+ requestParams[options.queryParameter || 'lang'] = options.key;
+
+ return $http(angular.extend({
+ url: options.url,
+ params: requestParams,
+ method: 'GET'
+ }, options.$http))
+ .then(function(result) {
+ return result.data;
+ }, function () {
+ return $q.reject(options.key);
+ });
+ };
+}
+
+$translateUrlLoader.displayName = '$translateUrlLoader';
+return 'pascalprecht.translate';
+
+}));
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate-loader-url/angular-translate-loader-url.min.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate-loader-url/angular-translate-loader-url.min.js
new file mode 100644
index 00000000..04f4c423
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate-loader-url/angular-translate-loader-url.min.js
@@ -0,0 +1,6 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 The angular-translate team, Pascal Precht; Licensed MIT
+ */
+!function(e,t){"function"==typeof define&&define.amd?define([],function(){return t()}):"object"==typeof module&&module.exports?module.exports=t():t()}(0,function(){function e(r,n){"use strict";return function(e){if(!e||!e.url)throw new Error("Couldn't use urlLoader since no url is given!");var t={};return t[e.queryParameter||"lang"]=e.key,n(angular.extend({url:e.url,params:t,method:"GET"},e.$http)).then(function(e){return e.data},function(){return r.reject(e.key)})}}return e.$inject=["$q","$http"],angular.module("pascalprecht.translate").factory("$translateUrlLoader",e),e.displayName="$translateUrlLoader","pascalprecht.translate"});
\ No newline at end of file
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate-loader-url/bower.json b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate-loader-url/bower.json
new file mode 100644
index 00000000..5e573106
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate-loader-url/bower.json
@@ -0,0 +1,12 @@
+{
+ "name": "angular-translate-loader-url",
+ "description": "A plugin for Angular Translate",
+ "version": "2.18.2",
+ "main": "./angular-translate-loader-url.js",
+ "ignore": [],
+ "author": "Pascal Precht",
+ "license": "MIT",
+ "dependencies": {
+ "angular-translate": "~2.18.2"
+ }
+}
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/LICENSE b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/LICENSE
new file mode 100644
index 00000000..d4d931c2
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2017 The angular-translate team and Pascal Precht
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-handler-log/angular-translate-handler-log.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-handler-log/angular-translate-handler-log.js
new file mode 100644
index 00000000..e2909f6b
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-handler-log/angular-translate-handler-log.js
@@ -0,0 +1,50 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 The angular-translate team, Pascal Precht; Licensed MIT
+ */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module unless amdModuleId is set
+ define([], function () {
+ return (factory());
+ });
+ } else if (typeof module === 'object' && module.exports) {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like environments that support module.exports,
+ // like Node.
+ module.exports = factory();
+ } else {
+ factory();
+ }
+}(this, function () {
+
+$translateMissingTranslationHandlerLog.$inject = ['$log'];
+angular.module('pascalprecht.translate')
+
+/**
+ * @ngdoc object
+ * @name pascalprecht.translate.$translateMissingTranslationHandlerLog
+ * @requires $log
+ *
+ * @description
+ * Uses angular's `$log` service to give a warning when trying to translate a
+ * translation id which doesn't exist.
+ *
+ * @returns {function} Handler function
+ */
+.factory('$translateMissingTranslationHandlerLog', $translateMissingTranslationHandlerLog);
+
+function $translateMissingTranslationHandlerLog ($log) {
+
+ 'use strict';
+
+ return function (translationId) {
+ $log.warn('Translation for ' + translationId + ' doesn\'t exist');
+ };
+}
+
+$translateMissingTranslationHandlerLog.displayName = '$translateMissingTranslationHandlerLog';
+return 'pascalprecht.translate';
+
+}));
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-handler-log/angular-translate-handler-log.min.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-handler-log/angular-translate-handler-log.min.js
new file mode 100644
index 00000000..2eb5872a
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-handler-log/angular-translate-handler-log.min.js
@@ -0,0 +1,6 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 The angular-translate team, Pascal Precht; Licensed MIT
+ */
+!function(n,t){"function"==typeof define&&define.amd?define([],function(){return t()}):"object"==typeof module&&module.exports?module.exports=t():t()}(0,function(){function n(t){"use strict";return function(n){t.warn("Translation for "+n+" doesn't exist")}}return n.$inject=["$log"],angular.module("pascalprecht.translate").factory("$translateMissingTranslationHandlerLog",n),n.displayName="$translateMissingTranslationHandlerLog","pascalprecht.translate"});
\ No newline at end of file
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat.js
new file mode 100644
index 00000000..039d02a0
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat.js
@@ -0,0 +1,197 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 The angular-translate team, Pascal Precht; Licensed MIT
+ */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module unless amdModuleId is set
+ define(["messageformat"], function (a0) {
+ return (factory(a0));
+ });
+ } else if (typeof module === 'object' && module.exports) {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like environments that support module.exports,
+ // like Node.
+ module.exports = factory(require("messageformat"));
+ } else {
+ factory(root["MessageFormat"]);
+ }
+}(this, function (MessageFormat) {
+
+angular.module('pascalprecht.translate')
+
+/**
+ * @ngdoc property
+ * @name pascalprecht.translate.TRANSLATE_MF_INTERPOLATION_CACHE
+ * @requires TRANSLATE_MF_INTERPOLATION_CACHE
+ *
+ * @description
+ * Uses MessageFormat.js to interpolate strings against some values.
+ */
+.constant('TRANSLATE_MF_INTERPOLATION_CACHE', '$translateMessageFormatInterpolation')
+
+/**
+ * @ngdoc object
+ * @name pascalprecht.translate.$translateMessageFormatInterpolationProvider
+ *
+ * @description
+ * Configurations for $translateMessageFormatInterpolation
+ */
+.provider('$translateMessageFormatInterpolation', $translateMessageFormatInterpolationProvider);
+
+function $translateMessageFormatInterpolationProvider() {
+
+ 'use strict';
+
+ var configurer;
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translateMessageFormatInterpolationProvider#messageFormatConfigurer
+ * @methodOf pascalprecht.translate.$translateMessageFormatInterpolationProvider
+ *
+ * @description
+ * Defines an optional configurer for the MessageFormat instance.
+ *
+ * Note: This hook will be called whenever a new instance of MessageFormat will be created.
+ *
+ * @param {function} fn callback with the instance as argument
+ */
+ this.messageFormatConfigurer = function (fn) {
+ configurer = fn;
+ };
+
+ /**
+ * @ngdoc object
+ * @name pascalprecht.translate.$translateMessageFormatInterpolation
+ * @requires pascalprecht.translate.TRANSLATE_MF_INTERPOLATION_CACHE
+ *
+ * @description
+ * Uses MessageFormat.js to interpolate strings against some values.
+ *
+ * Be aware to configure a proper sanitization strategy.
+ *
+ * See also:
+ * * {@link pascalprecht.translate.$translateSanitization}
+ * * {@link https://github.com/SlexAxton/messageformat.js}
+ *
+ * @return {object} $translateMessageFormatInterpolation Interpolator service
+ */
+ this.$get = ['$translateSanitization', '$cacheFactory', 'TRANSLATE_MF_INTERPOLATION_CACHE', function ($translateSanitization, $cacheFactory, TRANSLATE_MF_INTERPOLATION_CACHE) {
+ return $translateMessageFormatInterpolation($translateSanitization, $cacheFactory, TRANSLATE_MF_INTERPOLATION_CACHE, configurer);
+ }];
+
+}
+
+function $translateMessageFormatInterpolation($translateSanitization, $cacheFactory, TRANSLATE_MF_INTERPOLATION_CACHE, messageFormatConfigurer) {
+
+ 'use strict';
+
+ var $translateInterpolator = {},
+ $cache = $cacheFactory.get(TRANSLATE_MF_INTERPOLATION_CACHE),
+ // instantiate with default locale (which is 'en')
+ $mf = new MessageFormat('en'),
+ $identifier = 'messageformat';
+
+ if (angular.isFunction(messageFormatConfigurer)) {
+ messageFormatConfigurer($mf);
+ }
+
+ if (!$cache) {
+ // create cache if it doesn't exist already
+ $cache = $cacheFactory(TRANSLATE_MF_INTERPOLATION_CACHE);
+ }
+
+ $cache.put('en', $mf);
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translateMessageFormatInterpolation#setLocale
+ * @methodOf pascalprecht.translate.$translateMessageFormatInterpolation
+ *
+ * @description
+ * Sets current locale (this is currently not use in this interpolation).
+ *
+ * @param {string} locale Language key or locale.
+ */
+ $translateInterpolator.setLocale = function (locale) {
+ $mf = $cache.get(locale);
+ if (!$mf) {
+ $mf = new MessageFormat(locale);
+ if (angular.isFunction(messageFormatConfigurer)) {
+ messageFormatConfigurer($mf);
+ }
+ $cache.put(locale, $mf);
+ }
+ };
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translateMessageFormatInterpolation#getInterpolationIdentifier
+ * @methodOf pascalprecht.translate.$translateMessageFormatInterpolation
+ *
+ * @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.$translateMessageFormatInterpolation#interpolate
+ * @methodOf pascalprecht.translate.$translateMessageFormatInterpolation
+ *
+ * @description
+ * Interpolates given string against given interpolate params using MessageFormat.js.
+ *
+ * @returns {string} interpolated string.
+ */
+ $translateInterpolator.interpolate = function (string, interpolationParams, context, sanitizeStrategy) {
+ interpolationParams = interpolationParams || {};
+ interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params', sanitizeStrategy);
+
+ var compiledFunction = $cache.get('mf:' + string);
+
+ // if given string wasn't compiled yet, we do so now and never have to do it again
+ if (!compiledFunction) {
+
+ // Ensure explicit type if possible
+ // MessageFormat checks the actual type (i.e. for amount based conditions)
+ for (var key in interpolationParams) {
+ if (interpolationParams.hasOwnProperty(key)) {
+ // ensure number
+ var number = parseInt(interpolationParams[key], 10);
+ if (angular.isNumber(number) && ('' + number) === interpolationParams[key]) {
+ interpolationParams[key] = number;
+ }
+ }
+ }
+
+ compiledFunction = $mf.compile(string);
+ $cache.put('mf:' + string, compiledFunction);
+ }
+
+ var interpolatedText = compiledFunction(interpolationParams);
+ return $translateSanitization.sanitize(interpolatedText, 'text', sanitizeStrategy);
+ };
+
+ return $translateInterpolator;
+}
+
+$translateMessageFormatInterpolation.displayName = '$translateMessageFormatInterpolation';
+return 'pascalprecht.translate';
+
+}));
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat.min.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat.min.js
new file mode 100644
index 00000000..6050aefe
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat.min.js
@@ -0,0 +1,6 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 The angular-translate team, Pascal Precht; Licensed MIT
+ */
+!function(t,e){"function"==typeof define&&define.amd?define(["messageformat"],function(t){return e(t)}):"object"==typeof module&&module.exports?module.exports=e(require("messageformat")):e(t.MessageFormat)}(this,function(r){function i(u,t,e,n){"use strict";var a={},c=t.get(e),f=new r("en");return angular.isFunction(n)&&n(f),c||(c=t(e)),c.put("en",f),a.setLocale=function(t){(f=c.get(t))||(f=new r(t),angular.isFunction(n)&&n(f),c.put(t,f))},a.getInterpolationIdentifier=function(){return"messageformat"},a.useSanitizeValueStrategy=function(t){return u.useStrategy(t),this},a.interpolate=function(t,e,n,a){e=e||{},e=u.sanitize(e,"params",a);var r=c.get("mf:"+t);if(!r){for(var i in e)if(e.hasOwnProperty(i)){var o=parseInt(e[i],10);angular.isNumber(o)&&""+o===e[i]&&(e[i]=o)}r=f.compile(t),c.put("mf:"+t,r)}var s=r(e);return u.sanitize(s,"text",a)},a}return angular.module("pascalprecht.translate").constant("TRANSLATE_MF_INTERPOLATION_CACHE","$translateMessageFormatInterpolation").provider("$translateMessageFormatInterpolation",function(){"use strict";var a;this.messageFormatConfigurer=function(t){a=t},this.$get=["$translateSanitization","$cacheFactory","TRANSLATE_MF_INTERPOLATION_CACHE",function(t,e,n){return i(t,e,n,a)}]}),i.displayName="$translateMessageFormatInterpolation","pascalprecht.translate"});
\ No newline at end of file
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-partial/angular-translate-loader-partial.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-partial/angular-translate-loader-partial.js
new file mode 100644
index 00000000..79419f2a
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-partial/angular-translate-loader-partial.js
@@ -0,0 +1,585 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 The angular-translate team, Pascal Precht; Licensed MIT
+ */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module unless amdModuleId is set
+ define([], function () {
+ return (factory());
+ });
+ } else if (typeof module === 'object' && module.exports) {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like environments that support module.exports,
+ // like Node.
+ module.exports = factory();
+ } else {
+ factory();
+ }
+}(this, function () {
+
+angular.module('pascalprecht.translate')
+/**
+ * @ngdoc object
+ * @name pascalprecht.translate.$translatePartialLoaderProvider
+ *
+ * @description
+ * By using a $translatePartialLoaderProvider you can configure a list of a needed
+ * translation parts directly during the configuration phase of your application's
+ * lifetime. All parts you add by using this provider would be loaded by
+ * angular-translate at the startup as soon as possible.
+ */
+ .provider('$translatePartialLoader', $translatePartialLoader);
+
+function $translatePartialLoader() {
+
+ 'use strict';
+
+ /**
+ * @constructor
+ * @name Part
+ *
+ * @description
+ * Represents Part object to add and set parts at runtime.
+ */
+ function Part(name, priority, urlTemplate) {
+ this.name = name;
+ this.isActive = true;
+ this.tables = {};
+ this.priority = priority || 0;
+ this.langPromises = {};
+ this.urlTemplate = urlTemplate;
+ }
+
+ /**
+ * @name parseUrl
+ * @method
+ *
+ * @description
+ * Returns a parsed url template string and replaces given target lang
+ * and part name it.
+ *
+ * @param {string|function} urlTemplate - Either a string containing an url pattern (with
+ * '{part}' and '{lang}') or a function(part, lang)
+ * returning a string.
+ * @param {string} targetLang - Language key for language to be used.
+ * @return {string} Parsed url template string
+ */
+ Part.prototype.parseUrl = function (urlTemplate, targetLang) {
+ if (angular.isFunction(urlTemplate)) {
+ return urlTemplate(this.name, targetLang);
+ }
+ return urlTemplate.replace(/\{part\}/g, this.name).replace(/\{lang\}/g, targetLang);
+ };
+
+ Part.prototype.getTable = function (lang, $q, $http, $httpOptions, urlTemplate, errorHandler) {
+
+ //locals
+ var self = this;
+ var lastLangPromise = this.langPromises[lang];
+ var deferred = $q.defer();
+
+ //private helper helpers
+ var fetchData = function () {
+ return $http(
+ angular.extend({
+ method : 'GET',
+ url : self.parseUrl(self.urlTemplate || urlTemplate, lang)
+ },
+ $httpOptions)
+ );
+ };
+
+ //private helper
+ var handleNewData = function (data) {
+ self.tables[lang] = data;
+ deferred.resolve(data);
+ };
+
+ //private helper
+ var rejectDeferredWithPartName = function () {
+ deferred.reject(self.name);
+ };
+
+ //private helper
+ var tryGettingThisTable = function () {
+ //data fetching logic
+ fetchData().then(
+ function (result) {
+ handleNewData(result.data);
+ },
+ function (errorResponse) {
+ if (errorHandler) {
+ errorHandler(self.name, lang, errorResponse).then(handleNewData, rejectDeferredWithPartName);
+ } else {
+ rejectDeferredWithPartName();
+ }
+ });
+ };
+
+ //loading logic
+ if (!this.tables[lang]) {
+ //let's try loading the data
+ if (!lastLangPromise) {
+ //this is the first request - just go ahead and hit the server
+ tryGettingThisTable();
+ } else {
+ //this is an additional request after one or more unfinished or failed requests
+ //chain the deferred off the previous request's promise so that this request conditionally executes
+ //if the previous request succeeds then the result will be passed through, but if it fails then this request will try again and hit the server
+ lastLangPromise.then(deferred.resolve, tryGettingThisTable);
+ }
+ //retain a reference to the last promise so we can continue the chain if another request is made before any succeed
+ //you can picture the promise chain as a singly-linked list (formed by the .then handler queues) that's traversed by the execution context
+ this.langPromises[lang] = deferred.promise;
+ }
+ else {
+ //the part has already been loaded - if lastLangPromise is also undefined then the table has been populated using setPart
+ //this breaks the promise chain because we're not tying langDeferred's outcome to a previous call's promise handler queues, but we don't care because there's no asynchronous execution context to keep track of anymore
+ deferred.resolve(this.tables[lang]);
+ }
+ return deferred.promise;
+ };
+
+ var parts = {};
+
+ function hasPart(name) {
+ return Object.prototype.hasOwnProperty.call(parts, name);
+ }
+
+ function isStringValid(str) {
+ return angular.isString(str) && str !== '';
+ }
+
+ function isPartAvailable(name) {
+ if (!isStringValid(name)) {
+ throw new TypeError('Invalid type of a first argument, a non-empty string expected.');
+ }
+
+ return (hasPart(name) && parts[name].isActive);
+ }
+
+ function deepExtend(dst, src) {
+ for (var property in src) {
+ if (src[property] && src[property].constructor &&
+ src[property].constructor === Object) {
+ dst[property] = dst[property] || {};
+ deepExtend(dst[property], src[property]);
+ } else {
+ dst[property] = src[property];
+ }
+ }
+ return dst;
+ }
+
+ function getPrioritizedParts() {
+ var prioritizedParts = [];
+ for (var part in parts) {
+ if (parts[part].isActive) {
+ prioritizedParts.push(parts[part]);
+ }
+ }
+ prioritizedParts.sort(function (a, b) {
+ return a.priority - b.priority;
+ });
+ return prioritizedParts;
+ }
+
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translatePartialLoaderProvider#addPart
+ * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
+ *
+ * @description
+ * Registers a new part of the translation table to be loaded once the
+ * `angular-translate` gets into runtime phase. It does not actually load any
+ * translation data, but only registers a part to be loaded in the future.
+ *
+ * @param {string} name A name of the part to add
+ * @param {int} [priority=0] Sets the load priority of this part.
+ * @param {string|function} urlTemplate Either a string containing an url pattern (with
+ * '{part}' and '{lang}') or a function(part, lang)
+ * returning a string.
+ *
+ * @returns {object} $translatePartialLoaderProvider, so this method is chainable
+ * @throws {TypeError} The method could throw a **TypeError** if you pass the param
+ * of the wrong type. Please, note that the `name` param has to be a
+ * non-empty **string**.
+ */
+ this.addPart = function (name, priority, urlTemplate) {
+ if (!isStringValid(name)) {
+ throw new TypeError('Couldn\'t add part, part name has to be a string!');
+ }
+
+ if (!hasPart(name)) {
+ parts[name] = new Part(name, priority, urlTemplate);
+ }
+ parts[name].isActive = true;
+
+ return this;
+ };
+
+ /**
+ * @ngdocs function
+ * @name pascalprecht.translate.$translatePartialLoaderProvider#setPart
+ * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
+ *
+ * @description
+ * Sets a translation table to the specified part. This method does not make the
+ * specified part available, but only avoids loading this part from the server.
+ *
+ * @param {string} lang A language of the given translation table
+ * @param {string} part A name of the target part
+ * @param {object} table A translation table to set to the specified part
+ *
+ * @return {object} $translatePartialLoaderProvider, so this method is chainable
+ * @throws {TypeError} The method could throw a **TypeError** if you pass params
+ * of the wrong type. Please, note that the `lang` and `part` params have to be a
+ * non-empty **string**s and the `table` param has to be an object.
+ */
+ this.setPart = function (lang, part, table) {
+ if (!isStringValid(lang)) {
+ throw new TypeError('Couldn\'t set part.`lang` parameter has to be a string!');
+ }
+ if (!isStringValid(part)) {
+ throw new TypeError('Couldn\'t set part.`part` parameter has to be a string!');
+ }
+ if (typeof table !== 'object' || table === null) {
+ throw new TypeError('Couldn\'t set part. `table` parameter has to be an object!');
+ }
+
+ if (!hasPart(part)) {
+ parts[part] = new Part(part);
+ parts[part].isActive = false;
+ }
+
+ parts[part].tables[lang] = table;
+ return this;
+ };
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translatePartialLoaderProvider#deletePart
+ * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
+ *
+ * @description
+ * Removes the previously added part of the translation data. So, `angular-translate` will not
+ * load it at the startup.
+ *
+ * @param {string} name A name of the part to delete
+ *
+ * @returns {object} $translatePartialLoaderProvider, so this method is chainable
+ *
+ * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong
+ * type. Please, note that the `name` param has to be a non-empty **string**.
+ */
+ this.deletePart = function (name) {
+ if (!isStringValid(name)) {
+ throw new TypeError('Couldn\'t delete part, first arg has to be string.');
+ }
+
+ if (hasPart(name)) {
+ parts[name].isActive = false;
+ }
+
+ return this;
+ };
+
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translatePartialLoaderProvider#isPartAvailable
+ * @methodOf pascalprecht.translate.$translatePartialLoaderProvider
+ *
+ * @description
+ * Checks if the specific part is available. A part becomes available after it was added by the
+ * `addPart` method. Available parts would be loaded from the server once the `angular-translate`
+ * asks the loader to that.
+ *
+ * @param {string} name A name of the part to check
+ *
+ * @returns {boolean} Returns **true** if the part is available now and **false** if not.
+ *
+ * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong
+ * type. Please, note that the `name` param has to be a non-empty **string**.
+ */
+ this.isPartAvailable = isPartAvailable;
+
+ /**
+ * @ngdoc object
+ * @name pascalprecht.translate.$translatePartialLoader
+ *
+ * @requires $q
+ * @requires $http
+ * @requires $injector
+ * @requires $rootScope
+ * @requires $translate
+ *
+ * @description
+ *
+ * @param {object} options Options object
+ *
+ * @throws {TypeError}
+ */
+ this.$get = ['$rootScope', '$injector', '$q', '$http', '$log',
+ function ($rootScope, $injector, $q, $http, $log) {
+
+ /**
+ * @ngdoc event
+ * @name pascalprecht.translate.$translatePartialLoader#$translatePartialLoaderStructureChanged
+ * @eventOf pascalprecht.translate.$translatePartialLoader
+ * @eventType broadcast on root scope
+ *
+ * @description
+ * A $translatePartialLoaderStructureChanged event is called when a state of the loader was
+ * changed somehow. It could mean either some part is added or some part is deleted. Anyway when
+ * you get this event the translation table is not longer current and has to be updated.
+ *
+ * @param {string} name A name of the part which is a reason why the event was fired
+ */
+
+ var service = function (options) {
+ if (!isStringValid(options.key)) {
+ throw new TypeError('Unable to load data, a key is not a non-empty string.');
+ }
+
+ if (!isStringValid(options.urlTemplate) && !angular.isFunction(options.urlTemplate)) {
+ throw new TypeError('Unable to load data, a urlTemplate is not a non-empty string or not a function.');
+ }
+
+ var errorHandler = options.loadFailureHandler;
+ if (errorHandler !== undefined) {
+ if (!angular.isString(errorHandler)) {
+ throw new Error('Unable to load data, a loadFailureHandler is not a string.');
+ } else {
+ errorHandler = $injector.get(errorHandler);
+ }
+ }
+
+ var loaders = [],
+ prioritizedParts = getPrioritizedParts();
+
+ angular.forEach(prioritizedParts, function (part) {
+ loaders.push(
+ part.getTable(options.key, $q, $http, options.$http, options.urlTemplate, errorHandler)
+ );
+ part.urlTemplate = part.urlTemplate || options.urlTemplate;
+ });
+
+ // workaround for #1781
+ var structureHasBeenChangedWhileLoading = false;
+ var dirtyCheckEventCloser = $rootScope.$on('$translatePartialLoaderStructureChanged', function () {
+ structureHasBeenChangedWhileLoading = true;
+ });
+
+ return $q.all(loaders)
+ .then(function () {
+ dirtyCheckEventCloser();
+ if (structureHasBeenChangedWhileLoading) {
+ if (!options.__retries) {
+ // the part structure has been changed while loading (the origin ones)
+ // this can happen if an addPart/removePart has been invoked right after a $translate.use(lang)
+ // TODO maybe we can optimize this with the actual list of missing parts
+ options.__retries = (options.__retries || 0) + 1;
+ return service(options);
+ } else {
+ // the part structure has been changed again while loading (retried one)
+ // because this could an infinite loop, this will not load another one again
+ $log.warn('The partial loader has detected a multiple structure change (with addPort/removePart) ' +
+ 'while loading translations. You should consider using promises of $translate.use(lang) and ' +
+ '$translate.refresh(). Also parts should be added/removed right before an explicit refresh ' +
+ 'if possible.');
+ }
+ }
+ var table = {};
+ prioritizedParts = getPrioritizedParts();
+ angular.forEach(prioritizedParts, function (part) {
+ deepExtend(table, part.tables[options.key]);
+ });
+ return table;
+ }, function () {
+ dirtyCheckEventCloser();
+ return $q.reject(options.key);
+ });
+ };
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translatePartialLoader#addPart
+ * @methodOf pascalprecht.translate.$translatePartialLoader
+ *
+ * @description
+ * Registers a new part of the translation table. This method does not actually perform any xhr
+ * requests to get translation data. The new parts will be loaded in order of priority from the server next time
+ * `angular-translate` asks the loader to load translations.
+ *
+ * @param {string} name A name of the part to add
+ * @param {int} [priority=0] Sets the load priority of this part.
+ *
+ * @returns {object} $translatePartialLoader, so this method is chainable
+ *
+ * @fires {$translatePartialLoaderStructureChanged} The $translatePartialLoaderStructureChanged
+ * event would be fired by this method in case the new part affected somehow on the loaders
+ * state. This way it means that there are a new translation data available to be loaded from
+ * the server.
+ *
+ * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong
+ * type. Please, note that the `name` param has to be a non-empty **string**.
+ */
+ service.addPart = function (name, priority, urlTemplate) {
+ if (!isStringValid(name)) {
+ throw new TypeError('Couldn\'t add part, first arg has to be a string');
+ }
+
+ if (!hasPart(name)) {
+ parts[name] = new Part(name, priority, urlTemplate);
+ $rootScope.$emit('$translatePartialLoaderStructureChanged', name);
+ } else if (!parts[name].isActive) {
+ parts[name].isActive = true;
+ $rootScope.$emit('$translatePartialLoaderStructureChanged', name);
+ }
+
+ return service;
+ };
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translatePartialLoader#deletePart
+ * @methodOf pascalprecht.translate.$translatePartialLoader
+ *
+ * @description
+ * Deletes the previously added part of the translation data. The target part could be deleted
+ * either logically or physically. When the data is deleted logically it is not actually deleted
+ * from the browser, but the loader marks it as not active and prevents it from affecting on the
+ * translations. If the deleted in such way part is added again, the loader will use the
+ * previously loaded data rather than loading it from the server once more time. But if the data
+ * is deleted physically, the loader will completely remove all information about it. So in case
+ * of recycling this part will be loaded from the server again.
+ *
+ * @param {string} name A name of the part to delete
+ * @param {boolean=} [removeData=false] An indicator if the loader has to remove a loaded
+ * translation data physically. If the `removeData` if set to **false** the loaded data will not be
+ * deleted physically and might be reused in the future to prevent an additional xhr requests.
+ *
+ * @returns {object} $translatePartialLoader, so this method is chainable
+ *
+ * @fires {$translatePartialLoaderStructureChanged} The $translatePartialLoaderStructureChanged
+ * event would be fired by this method in case a part deletion process affects somehow on the
+ * loaders state. This way it means that some part of the translation data is now deprecated and
+ * the translation table has to be recompiled with the remaining translation parts.
+ *
+ * @throws {TypeError} The method could throw a **TypeError** if you pass some param of the
+ * wrong type. Please, note that the `name` param has to be a non-empty **string** and
+ * the `removeData` param has to be either **undefined** or **boolean**.
+ */
+ service.deletePart = function (name, removeData) {
+ if (!isStringValid(name)) {
+ throw new TypeError('Couldn\'t delete part, first arg has to be string');
+ }
+
+ if (removeData === undefined) {
+ removeData = false;
+ } else if (typeof removeData !== 'boolean') {
+ throw new TypeError('Invalid type of a second argument, a boolean expected.');
+ }
+
+ if (hasPart(name)) {
+ var wasActive = parts[name].isActive;
+ if (removeData) {
+ var $translate = $injector.get('$translate');
+ var cache = $translate.loaderCache();
+ if (typeof(cache) === 'string') {
+ // getting on-demand instance of loader
+ cache = $injector.get(cache);
+ }
+ // Purging items from cache...
+ if (typeof(cache) === 'object') {
+ angular.forEach(parts[name].tables, function (value, key) {
+ cache.remove(parts[name].parseUrl(parts[name].urlTemplate, key));
+ });
+ }
+ delete parts[name];
+ } else {
+ parts[name].isActive = false;
+ }
+ if (wasActive) {
+ $rootScope.$emit('$translatePartialLoaderStructureChanged', name);
+ }
+ }
+
+ return service;
+ };
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translatePartialLoader#isPartLoaded
+ * @methodOf pascalprecht.translate.$translatePartialLoader
+ *
+ * @description
+ * Checks if the registered translation part is loaded into the translation table.
+ *
+ * @param {string} name A name of the part
+ * @param {string} lang A key of the language
+ *
+ * @returns {boolean} Returns **true** if the translation of the part is loaded to the translation table and **false** if not.
+ *
+ * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong
+ * type. Please, note that the `name` and `lang` params have to be non-empty **string**.
+ */
+ service.isPartLoaded = function (name, lang) {
+ return angular.isDefined(parts[name]) && angular.isDefined(parts[name].tables[lang]);
+ };
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translatePartialLoader#getRegisteredParts
+ * @methodOf pascalprecht.translate.$translatePartialLoader
+ *
+ * @description
+ * Gets names of the parts that were added with the `addPart`.
+ *
+ * @returns {array} Returns array of registered parts, if none were registered then an empty array is returned.
+ */
+ service.getRegisteredParts = function () {
+ var registeredParts = [];
+ angular.forEach(parts, function (p) {
+ if (p.isActive) {
+ registeredParts.push(p.name);
+ }
+ });
+ return registeredParts;
+ };
+
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translatePartialLoader#isPartAvailable
+ * @methodOf pascalprecht.translate.$translatePartialLoader
+ *
+ * @description
+ * Checks if a target translation part is available. The part becomes available just after it was
+ * added by the `addPart` method. Part's availability does not mean that it was loaded from the
+ * server, but only that it was added to the loader. The available part might be loaded next
+ * time the loader is called.
+ *
+ * @param {string} name A name of the part to delete
+ *
+ * @returns {boolean} Returns **true** if the part is available now and **false** if not.
+ *
+ * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong
+ * type. Please, note that the `name` param has to be a non-empty **string**.
+ */
+ service.isPartAvailable = isPartAvailable;
+
+ return service;
+
+ }];
+
+}
+
+$translatePartialLoader.displayName = '$translatePartialLoader';
+return 'pascalprecht.translate';
+
+}));
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-partial/angular-translate-loader-partial.min.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-partial/angular-translate-loader-partial.min.js
new file mode 100644
index 00000000..74ad786f
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-partial/angular-translate-loader-partial.min.js
@@ -0,0 +1,6 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 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(){"use strict";function a(t,e,r){this.name=t,this.isActive=!0,this.tables={},this.priority=e||0,this.langPromises={},this.urlTemplate=r}a.prototype.parseUrl=function(t,e){return angular.isFunction(t)?t(this.name,e):t.replace(/\{part\}/g,this.name).replace(/\{lang\}/g,e)},a.prototype.getTable=function(e,t,r,a,n,i){var o=this,s=this.langPromises[e],l=t.defer(),u=function(t){o.tables[e]=t,l.resolve(t)},c=function(){l.reject(o.name)},p=function(){r(angular.extend({method:"GET",url:o.parseUrl(o.urlTemplate||n,e)},a)).then(function(t){u(t.data)},function(t){i?i(o.name,e,t).then(u,c):c()})};return this.tables[e]?l.resolve(this.tables[e]):(s?s.then(l.resolve,p):p(),this.langPromises[e]=l.promise),l.promise};var n={};function i(t){return Object.prototype.hasOwnProperty.call(n,t)}function f(t){return angular.isString(t)&&""!==t}function t(t){if(!f(t))throw new TypeError("Invalid type of a first argument, a non-empty string expected.");return i(t)&&n[t].isActive}function d(){var t=[];for(var e in n)n[e].isActive&&t.push(n[e]);return t.sort(function(t,e){return t.priority-e.priority}),t}this.addPart=function(t,e,r){if(!f(t))throw new TypeError("Couldn't add part, part name has to be a string!");return i(t)||(n[t]=new a(t,e,r)),n[t].isActive=!0,this},this.setPart=function(t,e,r){if(!f(t))throw new TypeError("Couldn't set part.`lang` parameter has to be a string!");if(!f(e))throw new TypeError("Couldn't set part.`part` parameter has to be a string!");if("object"!=typeof r||null===r)throw new TypeError("Couldn't set part. `table` parameter has to be an object!");return i(e)||(n[e]=new a(e),n[e].isActive=!1),n[e].tables[t]=r,this},this.deletePart=function(t){if(!f(t))throw new TypeError("Couldn't delete part, first arg has to be string.");return i(t)&&(n[t].isActive=!1),this},this.isPartAvailable=t,this.$get=["$rootScope","$injector","$q","$http","$log",function(o,s,l,u,c){var p=function(r){if(!f(r.key))throw new TypeError("Unable to load data, a key is not a non-empty string.");if(!f(r.urlTemplate)&&!angular.isFunction(r.urlTemplate))throw new TypeError("Unable to load data, a urlTemplate is not a non-empty string or not a function.");var e=r.loadFailureHandler;if(void 0!==e){if(!angular.isString(e))throw new Error("Unable to load data, a loadFailureHandler is not a string.");e=s.get(e)}var a=[],t=d();angular.forEach(t,function(t){a.push(t.getTable(r.key,l,u,r.$http,r.urlTemplate,e)),t.urlTemplate=t.urlTemplate||r.urlTemplate});var n=!1,i=o.$on("$translatePartialLoaderStructureChanged",function(){n=!0});return l.all(a).then(function(){if(i(),n){if(!r.__retries)return r.__retries=(r.__retries||0)+1,p(r);c.warn("The partial loader has detected a multiple structure change (with addPort/removePart) while loading translations. You should consider using promises of $translate.use(lang) and $translate.refresh(). Also parts should be added/removed right before an explicit refresh if possible.")}var e={};return t=d(),angular.forEach(t,function(t){!function t(e,r){for(var a in r)r[a]&&r[a].constructor&&r[a].constructor===Object?(e[a]=e[a]||{},t(e[a],r[a])):e[a]=r[a];return e}(e,t.tables[r.key])}),e},function(){return i(),l.reject(r.key)})};return p.addPart=function(t,e,r){if(!f(t))throw new TypeError("Couldn't add part, first arg has to be a string");return i(t)?n[t].isActive||(n[t].isActive=!0,o.$emit("$translatePartialLoaderStructureChanged",t)):(n[t]=new a(t,e,r),o.$emit("$translatePartialLoaderStructureChanged",t)),p},p.deletePart=function(r,t){if(!f(r))throw new TypeError("Couldn't delete part, first arg has to be string");if(void 0===t)t=!1;else if("boolean"!=typeof t)throw new TypeError("Invalid type of a second argument, a boolean expected.");if(i(r)){var e=n[r].isActive;if(t){var a=s.get("$translate").loaderCache();"string"==typeof a&&(a=s.get(a)),"object"==typeof a&&angular.forEach(n[r].tables,function(t,e){a.remove(n[r].parseUrl(n[r].urlTemplate,e))}),delete n[r]}else n[r].isActive=!1;e&&o.$emit("$translatePartialLoaderStructureChanged",r)}return p},p.isPartLoaded=function(t,e){return angular.isDefined(n[t])&&angular.isDefined(n[t].tables[e])},p.getRegisteredParts=function(){var e=[];return angular.forEach(n,function(t){t.isActive&&e.push(t.name)}),e},p.isPartAvailable=t,p}]}return angular.module("pascalprecht.translate").provider("$translatePartialLoader",t),t.displayName="$translatePartialLoader","pascalprecht.translate"});
\ No newline at end of file
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-static-files/angular-translate-loader-static-files.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-static-files/angular-translate-loader-static-files.js
new file mode 100644
index 00000000..7a911f11
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-static-files/angular-translate-loader-static-files.js
@@ -0,0 +1,112 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 The angular-translate team, Pascal Precht; Licensed MIT
+ */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module unless amdModuleId is set
+ define([], function () {
+ return (factory());
+ });
+ } else if (typeof module === 'object' && module.exports) {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like environments that support module.exports,
+ // like Node.
+ module.exports = factory();
+ } else {
+ factory();
+ }
+}(this, function () {
+
+$translateStaticFilesLoader.$inject = ['$q', '$http'];
+angular.module('pascalprecht.translate')
+/**
+ * @ngdoc object
+ * @name pascalprecht.translate.$translateStaticFilesLoader
+ * @requires $q
+ * @requires $http
+ *
+ * @description
+ * Creates a loading function for a typical static file url pattern:
+ * "lang-en_US.json", "lang-de_DE.json", etc. Using this builder,
+ * the response of these urls must be an object of key-value pairs.
+ *
+ * @param {object} options Options object, which gets prefix, suffix, key, and fileMap
+ */
+.factory('$translateStaticFilesLoader', $translateStaticFilesLoader);
+
+function $translateStaticFilesLoader($q, $http) {
+
+ 'use strict';
+
+ return function (options) {
+
+ if (!options || (!angular.isArray(options.files) && (!angular.isString(options.prefix) || !angular.isString(options.suffix)))) {
+ throw new Error('Couldn\'t load static files, no files and prefix or suffix specified!');
+ }
+
+ if (!options.files) {
+ options.files = [{
+ prefix: options.prefix,
+ suffix: options.suffix
+ }];
+ }
+
+ var load = function (file) {
+ if (!file || (!angular.isString(file.prefix) || !angular.isString(file.suffix))) {
+ throw new Error('Couldn\'t load static file, no prefix or suffix specified!');
+ }
+
+ var fileUrl = [
+ file.prefix,
+ options.key,
+ file.suffix
+ ].join('');
+
+ if (angular.isObject(options.fileMap) && options.fileMap[fileUrl]) {
+ fileUrl = options.fileMap[fileUrl];
+ }
+
+ return $http(angular.extend({
+ url: fileUrl,
+ method: 'GET'
+ }, options.$http))
+ .then(function(result) {
+ return result.data;
+ }, function () {
+ return $q.reject(options.key);
+ });
+ };
+
+ var promises = [],
+ length = options.files.length;
+
+ for (var i = 0; i < length; i++) {
+ promises.push(load({
+ prefix: options.files[i].prefix,
+ key: options.key,
+ suffix: options.files[i].suffix
+ }));
+ }
+
+ return $q.all(promises)
+ .then(function (data) {
+ var length = data.length,
+ mergedData = {};
+
+ for (var i = 0; i < length; i++) {
+ for (var key in data[i]) {
+ mergedData[key] = data[i][key];
+ }
+ }
+
+ return mergedData;
+ });
+ };
+}
+
+$translateStaticFilesLoader.displayName = '$translateStaticFilesLoader';
+return 'pascalprecht.translate';
+
+}));
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-static-files/angular-translate-loader-static-files.min.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-static-files/angular-translate-loader-static-files.min.js
new file mode 100644
index 00000000..a66674d4
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-loader-static-files/angular-translate-loader-static-files.min.js
@@ -0,0 +1,6 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 The angular-translate team, Pascal Precht; Licensed MIT
+ */
+!function(e,i){"function"==typeof define&&define.amd?define([],function(){return i()}):"object"==typeof module&&module.exports?module.exports=i():i()}(0,function(){function e(n,a){"use strict";return function(r){if(!(r&&(angular.isArray(r.files)||angular.isString(r.prefix)&&angular.isString(r.suffix))))throw new Error("Couldn't load static files, no files and prefix or suffix specified!");r.files||(r.files=[{prefix:r.prefix,suffix:r.suffix}]);for(var e=function(e){if(!e||!angular.isString(e.prefix)||!angular.isString(e.suffix))throw new Error("Couldn't load static file, no prefix or suffix specified!");var i=[e.prefix,r.key,e.suffix].join("");return angular.isObject(r.fileMap)&&r.fileMap[i]&&(i=r.fileMap[i]),a(angular.extend({url:i,method:"GET"},r.$http)).then(function(e){return e.data},function(){return n.reject(r.key)})},i=[],t=r.files.length,f=0;f= 4) {
+ var $cookies = $injector.get('$cookies');
+ delegate = {
+ get : function (key) {
+ return $cookies.get(key);
+ },
+ put : function (key, value) {
+ $cookies.put(key, value);
+ }
+ };
+ } else {
+ var $cookieStore = $injector.get('$cookieStore');
+ delegate = {
+ get : function (key) {
+ return $cookieStore.get(key);
+ },
+ put : function (key, value) {
+ $cookieStore.put(key, value);
+ }
+ };
+ }
+
+ var $translateCookieStorage = {
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translateCookieStorage#get
+ * @methodOf pascalprecht.translate.$translateCookieStorage
+ *
+ * @description
+ * Returns an item from cookieStorage by given name.
+ *
+ * @param {string} name Item name
+ * @return {string} Value of item name
+ */
+ get : function (name) {
+ return delegate.get(name);
+ },
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translateCookieStorage#set
+ * @methodOf pascalprecht.translate.$translateCookieStorage
+ *
+ * @description
+ * Sets an item in cookieStorage by given name.
+ *
+ * @deprecated use #put
+ *
+ * @param {string} name Item name
+ * @param {string} value Item value
+ */
+ set : function (name, value) {
+ delegate.put(name, value);
+ },
+
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translateCookieStorage#put
+ * @methodOf pascalprecht.translate.$translateCookieStorage
+ *
+ * @description
+ * Sets an item in cookieStorage by given name.
+ *
+ * @param {string} name Item name
+ * @param {string} value Item value
+ */
+ put : function (name, value) {
+ delegate.put(name, value);
+ }
+ };
+
+ return $translateCookieStorage;
+}
+
+$translateCookieStorageFactory.displayName = '$translateCookieStorage';
+return 'pascalprecht.translate';
+
+}));
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-storage-cookie/angular-translate-storage-cookie.min.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-storage-cookie/angular-translate-storage-cookie.min.js
new file mode 100644
index 00000000..ee5d0770
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-storage-cookie/angular-translate-storage-cookie.min.js
@@ -0,0 +1,6 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 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(t){"use strict";var n;if(1===angular.version.major&&4<=angular.version.minor){var o=t.get("$cookies");n={get:function(t){return o.get(t)},put:function(t,e){o.put(t,e)}}}else{var r=t.get("$cookieStore");n={get:function(t){return r.get(t)},put:function(t,e){r.put(t,e)}}}return{get:function(t){return n.get(t)},set:function(t,e){n.put(t,e)},put:function(t,e){n.put(t,e)}}}return t.$inject=["$injector"],angular.module("pascalprecht.translate").factory("$translateCookieStorage",t),t.displayName="$translateCookieStorage","pascalprecht.translate"});
\ No newline at end of file
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-storage-local/angular-translate-storage-local.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-storage-local/angular-translate-storage-local.js
new file mode 100644
index 00000000..d82b52e9
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-storage-local/angular-translate-storage-local.js
@@ -0,0 +1,123 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 The angular-translate team, Pascal Precht; Licensed MIT
+ */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module unless amdModuleId is set
+ define([], function () {
+ return (factory());
+ });
+ } else if (typeof module === 'object' && module.exports) {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like environments that support module.exports,
+ // like Node.
+ module.exports = factory();
+ } else {
+ factory();
+ }
+}(this, function () {
+
+$translateLocalStorageFactory.$inject = ['$window', '$translateCookieStorage'];
+angular.module('pascalprecht.translate')
+
+/**
+ * @ngdoc object
+ * @name pascalprecht.translate.$translateLocalStorage
+ * @requires $window
+ * @requires $translateCookieStorage
+ *
+ * @description
+ * Abstraction layer for localStorage. This service is used when telling angular-translate
+ * to use localStorage as storage.
+ *
+ */
+.factory('$translateLocalStorage', $translateLocalStorageFactory);
+
+function $translateLocalStorageFactory($window, $translateCookieStorage) {
+
+ 'use strict';
+
+ // Setup adapter
+ var localStorageAdapter = (function(){
+ var langKey;
+ return {
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translateLocalStorage#get
+ * @methodOf pascalprecht.translate.$translateLocalStorage
+ *
+ * @description
+ * Returns an item from localStorage by given name.
+ *
+ * @param {string} name Item name
+ * @return {string} Value of item name
+ */
+ get: function (name) {
+ if(!langKey) {
+ langKey = $window.localStorage.getItem(name);
+ }
+
+ return langKey;
+ },
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translateLocalStorage#set
+ * @methodOf pascalprecht.translate.$translateLocalStorage
+ *
+ * @description
+ * Sets an item in localStorage by given name.
+ *
+ * @deprecated use #put
+ *
+ * @param {string} name Item name
+ * @param {string} value Item value
+ */
+ set: function (name, value) {
+ langKey=value;
+ $window.localStorage.setItem(name, value);
+ },
+ /**
+ * @ngdoc function
+ * @name pascalprecht.translate.$translateLocalStorage#put
+ * @methodOf pascalprecht.translate.$translateLocalStorage
+ *
+ * @description
+ * Sets an item in localStorage by given name.
+ *
+ * @param {string} name Item name
+ * @param {string} value Item value
+ */
+ put: function (name, value) {
+ langKey=value;
+ $window.localStorage.setItem(name, value);
+ }
+ };
+ }());
+
+ var hasLocalStorageSupport = 'localStorage' in $window;
+ if (hasLocalStorageSupport) {
+ var testKey = 'pascalprecht.translate.storageTest';
+ try {
+ // this check have to be wrapped within a try/catch because on
+ // a SecurityError: Dom Exception 18 on iOS
+ if ($window.localStorage !== null) {
+ $window.localStorage.setItem(testKey, 'foo');
+ $window.localStorage.removeItem(testKey);
+ hasLocalStorageSupport = true;
+ } else {
+ hasLocalStorageSupport = false;
+ }
+ } catch (e){
+ hasLocalStorageSupport = false;
+ }
+ }
+ var $translateLocalStorage = hasLocalStorageSupport ? localStorageAdapter : $translateCookieStorage;
+ return $translateLocalStorage;
+}
+
+$translateLocalStorageFactory.displayName = '$translateLocalStorageFactory';
+return 'pascalprecht.translate';
+
+}));
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-storage-local/angular-translate-storage-local.min.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-storage-local/angular-translate-storage-local.min.js
new file mode 100644
index 00000000..5df4d0aa
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate-storage-local/angular-translate-storage-local.min.js
@@ -0,0 +1,6 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 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(a,t){"use strict";var o,e={get:function(t){return o||(o=a.localStorage.getItem(t)),o},set:function(t,e){o=e,a.localStorage.setItem(t,e)},put:function(t,e){o=e,a.localStorage.setItem(t,e)}},r="localStorage"in a;if(r){var n="pascalprecht.translate.storageTest";try{r=null!==a.localStorage&&(a.localStorage.setItem(n,"foo"),a.localStorage.removeItem(n),!0)}catch(t){r=!1}}return r?e:t}return t.$inject=["$window","$translateCookieStorage"],angular.module("pascalprecht.translate").factory("$translateLocalStorage",t),t.displayName="$translateLocalStorageFactory","pascalprecht.translate"});
\ No newline at end of file
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate.js
new file mode 100644
index 00000000..acd0b916
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate.js
@@ -0,0 +1,3785 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 The angular-translate team, Pascal Precht; Licensed MIT
+ */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module unless amdModuleId is set
+ define([], function () {
+ return (factory());
+ });
+ } else if (typeof module === 'object' && module.exports) {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like environments that support module.exports,
+ // like Node.
+ module.exports = factory();
+ } else {
+ factory();
+ }
+}(this, function () {
+
+/**
+ * @ngdoc overview
+ * @name pascalprecht.translate
+ *
+ * @description
+ * The main module which holds everything together.
+ */
+runTranslate.$inject = ['$translate'];
+$translate.$inject = ['$STORAGE_KEY', '$windowProvider', '$translateSanitizationProvider', 'pascalprechtTranslateOverrider'];
+$translateDefaultInterpolation.$inject = ['$interpolate', '$translateSanitization'];
+translateDirective.$inject = ['$translate', '$interpolate', '$compile', '$parse', '$rootScope'];
+translateAttrDirective.$inject = ['$translate', '$rootScope'];
+translateCloakDirective.$inject = ['$translate', '$rootScope'];
+translateFilterFactory.$inject = ['$parse', '$translate'];
+$translationCache.$inject = ['$cacheFactory'];
+angular.module('pascalprecht.translate', ['ng'])
+ .run(runTranslate);
+
+function runTranslate($translate) {
+
+ 'use strict';
+
+ var key = $translate.storageKey(),
+ storage = $translate.storage();
+
+ var fallbackFromIncorrectStorageValue = function () {
+ var preferred = $translate.preferredLanguage();
+ if (angular.isString(preferred)) {
+ $translate.use(preferred);
+ // $translate.use() will also remember the language.
+ // So, we don't need to call storage.put() here.
+ } else {
+ storage.put(key, $translate.use());
+ }
+ };
+
+ fallbackFromIncorrectStorageValue.displayName = 'fallbackFromIncorrectStorageValue';
+
+ if (storage) {
+ if (!storage.get(key)) {
+ fallbackFromIncorrectStorageValue();
+ } else {
+ $translate.use(storage.get(key))['catch'](fallbackFromIncorrectStorageValue);
+ }
+ } else if (angular.isString($translate.preferredLanguage())) {
+ $translate.use($translate.preferredLanguage());
+ }
+}
+
+runTranslate.displayName = 'runTranslate';
+
+/**
+ * @ngdoc object
+ * @name pascalprecht.translate.$translateSanitizationProvider
+ *
+ * @description
+ *
+ * Configurations for $translateSanitization
+ */
+angular.module('pascalprecht.translate').provider('$translateSanitization', $translateSanitizationProvider);
+
+function $translateSanitizationProvider () {
+
+ 'use strict';
+
+ var $sanitize,
+ $sce,
+ currentStrategy = null, // TODO change to either 'sanitize', 'escape' or ['sanitize', 'escapeParameters'] in 3.0.
+ hasConfiguredStrategy = false,
+ hasShownNoStrategyConfiguredWarning = false,
+ strategies;
+
+ /**
+ * Definition of a sanitization strategy function
+ * @callback StrategyFunction
+ * @param {string|object} value - value to be sanitized (either a string or an interpolated value map)
+ * @param {string} mode - either 'text' for a string (translation) or 'params' for the interpolated params
+ * @return {string|object}
+ */
+
+ /**
+ * @ngdoc property
+ * @name strategies
+ * @propertyOf pascalprecht.translate.$translateSanitizationProvider
+ *
+ * @description
+ * Following strategies are built-in:
+ *
+ * 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.2';
+
+ // 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);
+ } else {
+ throw new TypeError('translationId must be a not empty string');
+ }
+
+ 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()).replace(/\n/g, ' ');
+
+ // 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('translate', 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';
+
+}));
diff --git a/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate.min.js b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate.min.js
new file mode 100644
index 00000000..065f7b86
--- /dev/null
+++ b/keycloak/themes/ashvin/common/resources/node_modules/angular-translate/dist/angular-translate.min.js
@@ -0,0 +1,6 @@
+/*!
+ * angular-translate - v2.18.2 - 2020-01-04
+ *
+ * Copyright (c) 2020 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 T,c,z,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