From 5a807a74663dcf7ea1eae8f37589072bdad1f036 Mon Sep 17 00:00:00 2001 From: fschneider Date: Tue, 1 Sep 2015 11:34:36 +0200 Subject: User Interface nearly finished --- .../src/main/webapp/assets/js/jquery-lang.js | 622 +++++++++++++++++++++ 1 file changed, 622 insertions(+) create mode 100644 pdf-as-web/src/main/webapp/assets/js/jquery-lang.js (limited to 'pdf-as-web/src/main/webapp/assets/js/jquery-lang.js') diff --git a/pdf-as-web/src/main/webapp/assets/js/jquery-lang.js b/pdf-as-web/src/main/webapp/assets/js/jquery-lang.js new file mode 100644 index 00000000..7708b6a3 --- /dev/null +++ b/pdf-as-web/src/main/webapp/assets/js/jquery-lang.js @@ -0,0 +1,622 @@ +/* + The MIT License (MIT) + + Copyright (c) 2014 Irrelon Software Limited + http://www.irrelon.com + + 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, url 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. + + Source: https://github.com/coolbloke1324/jquery-lang-js + + Changelog: + Version 2.0.0 - Complete re-write. + */ +var Lang = (function () { + var Lang = function (defaultLang, currentLang, allowCookieOverride) { + var self = this, + cookieLang; + + // Enable firing events + this._fireEvents = true; + + // Allow storage of dynamic language pack data + this._dynamic = {}; + + // Store existing mutation methods so we can auto-run + // translations when new data is added to the page + this._mutationCopies = { + append: $.fn.append, + appendTo: $.fn.appendTo, + prepend: $.fn.prepend, + before: $.fn.before, + after: $.fn.after, + html: $.fn.html + }; + + // Now override the existing mutation methods with our own + $.fn.append = function () { return self._mutation(this, 'append', arguments) }; + $.fn.appendTo = function () { return self._mutation(this, 'appendTo', arguments) }; + $.fn.prepend = function () { return self._mutation(this, 'prepend', arguments) }; + $.fn.before = function () { return self._mutation(this, 'before', arguments) }; + $.fn.after = function () { return self._mutation(this, 'after', arguments) }; + $.fn.html = function () { return self._mutation(this, 'html', arguments) }; + + // Set default and current language to the default one + // to start with + this.defaultLang = defaultLang || 'en'; + this.currentLang = defaultLang || 'en'; + + // Check for cookie support when no current language is specified + if ((allowCookieOverride || !currentLang) && $.cookie) { + // Check for an existing language cookie + cookieLang = $.cookie('langCookie'); + + if (cookieLang) { + // We have a cookie language, set the current language + currentLang = cookieLang; + } + } + + $(function () { + // Setup data on the language items + self._start(); + + // Check if the current language is not the same as our default + if (currentLang && currentLang !== self.defaultLang) { + // Switch to the current language + self.change(currentLang); + } + }) + }; + + /** + * Object that holds the language packs. + * @type {{}} + */ + Lang.prototype.pack = {}; + + /** + * Array of translatable attributes to check for on elements. + * @type {string[]} + */ + Lang.prototype.attrList = [ + 'title', + 'alt', + 'placeholder' + ]; + + /** + * Defines a language pack that can be dynamically loaded and the + * path to use when doing so. + * @param {String} lang The language two-letter iso-code. + * @param {String} path The path to the language pack js file. + */ + Lang.prototype.dynamic = function (lang, path) { + if (lang !== undefined && path !== undefined) { + this._dynamic[lang] = path; + } + }; + + /** + * Loads a new language pack for the given language. + * @param {string} lang The language to load the pack for. + * @param {Function=} callback Optional callback when the file has loaded. + */ + Lang.prototype.loadPack = function (lang, callback) { + var self = this; + + if (lang && self._dynamic[lang]) { + $.ajax({ + dataType: "json", + url: self._dynamic[lang], + success: function (data) { + self.pack[lang] = data; + + // Process the regex list + if (self.pack[lang].regex) { + var packRegex = self.pack[lang].regex, + regex, + i; + + for (i = 0; i < packRegex.length; i++) { + regex = packRegex[i]; + if (regex.length === 2) { + // String, value + regex[0] = new RegExp(regex[0]); + } else if (regex.length === 3) { + // String, modifiers, value + regex[0] = new RegExp(regex[0], regex[1]); + + // Remove modifier + regex.splice(1, 1); + } + } + } + + console.log('Loaded language pack: ' + self._dynamic[lang]); + if (callback) { callback(false, lang, self._dynamic[lang]); } + }, + error: function () { + console.log('Error loading language pack' + self._dynamic[lang]); + if (callback) { callback(true, lang, self._dynamic[lang]); } + } + }); + } else { + throw('Cannot load language pack, no file path specified!'); + } + }; + + /** + * Scans the DOM for elements with [lang] selector and saves translate data + * for them for later use. + * @private + */ + Lang.prototype._start = function (selector) { + // Get the page HTML + var arr = selector !== undefined ? $(selector).find('[lang]') : $(':not(html)[lang]'), + arrCount = arr.length, + elem; + + while (arrCount--) { + elem = $(arr[arrCount]); + this._processElement(elem); + } + }; + + Lang.prototype._processElement = function (elem) { + // Only store data if the element is set to our default language + if (elem.attr('lang') === this.defaultLang) { + // Store translatable attributes + this._storeAttribs(elem); + + // Store translatable content + this._storeContent(elem); + } + }; + + /** + * Stores the translatable attribute values in their default language. + * @param {object} elem The jQuery selected element. + * @private + */ + Lang.prototype._storeAttribs = function (elem) { + var attrIndex, + attr, + attrObj; + + for (attrIndex = 0; attrIndex < this.attrList.length; attrIndex++) { + attr = this.attrList[attrIndex]; + if (elem.attr(attr)) { + // Grab the existing attribute store or create a new object + attrObj = elem.data('lang-attr') || {}; + + // Add the attribute and value to the store + attrObj[attr] = elem.attr(attr); + + // Save the attribute data to the store + elem.data('lang-attr', attrObj); + } + } + }; + + /** + * Reads the existing content from the element and stores it for + * later use in translation. + * @param elem + * @private + */ + Lang.prototype._storeContent = function (elem) { + // Check if the element is an input element + if (elem.is('input')) { + switch (elem.attr('type')) { + case 'button': + case 'submit': + case 'reset': + elem.data('lang-val', elem.val()); + break; + } + } else { + // Get the text nodes immediately inside this element + var nodes = this._getTextNodes(elem); + if (nodes) { + elem.data('lang-text', nodes); + } + } + }; + + /** + * Retrieves the text nodes from an element and returns them in array wrap into + * object with two properties: + * - node - which corespondes to text node, + * - langDefaultText - wich remember current data of text node + * @param elem + * @returns {Array|*} + * @private + */ + Lang.prototype._getTextNodes = function (elem) { + var nodes = elem.contents(), nodeObjArray = [], nodeObj = {}, + nodeArr, that = this, map = Array.prototype.map; + + $.each(nodes, function (index, node) { + if ( node.nodeType !== 3 ) { + return; + } + + nodeObj = { + node : node, + langDefaultText : node.data + }; + + nodeObjArray.push(nodeObj); + }); + + return nodeObjArray; + }; + + /** + * Sets text nodes of an element translated based on the passed language. + * @param elem + * @param {Array|*} nodes array of objecs with text node and defaultText returned from _getTextNodes + * @param lang + * @private + */ + Lang.prototype._setTextNodes = function (elem, nodes, lang) { + var index, + textNode, + defaultText, + translation, + langNotDefault = lang !== this.defaultLang; + + for (index = 0; index < nodes.length; index++) { + textNode = nodes[index]; + + if (langNotDefault) { + defaultText = $.trim(textNode.langDefaultText); + + if (defaultText) { + // Translate the langDefaultText + translation = this.translate(defaultText, lang); + + if (translation) { + try { + // Replace the text with the translated version + textNode.node.data = textNode.node.data.split($.trim(textNode.node.data)).join(translation); + } catch (e) { + + } + } else { + console.log('Translation for "' + defaultText + '" not found!'); + } + } + } else { + // Replace with original text + try { + textNode.node.data = textNode.langDefaultText; + } catch (e) { + + } + } + } + }; + + /** + * Translates and sets the attributes of an element to the passed language. + * @param elem + * @param lang + * @private + */ + Lang.prototype._translateAttribs = function (elem, lang) { + var attr, + attrObj = elem.data('lang-attr') || {}, + translation; + + for (attr in attrObj) { + if (attrObj.hasOwnProperty(attr)) { + // Check the element still has the attribute + if (elem.attr(attr)) { + if (lang !== this.defaultLang) { + // Get the translated value + translation = this.translate(attrObj[attr], lang); + + // Check we actually HAVE a translation + if (translation) { + // Change the attribute to the translated value + elem.attr(attr, translation); + } + } else { + // Set default language value + elem.attr(attr, attrObj[attr]); + } + } + } + } + }; + + /** + * Translates and sets the contents of an element to the passed language. + * @param elem + * @param lang + * @private + */ + Lang.prototype._translateContent = function (elem, lang) { + var langNotDefault = lang !== this.defaultLang, + translation, + nodes; + + // Check if the element is an input element + if (elem.is('input')) { + switch (elem.attr('type')) { + case 'button': + case 'submit': + case 'reset': + if (langNotDefault) { + // Get the translated value + translation = this.translate(elem.data('lang-val'), lang); + + // Check we actually HAVE a translation + if (translation) { + // Set translated value + elem.val(translation); + } + } else { + // Set default language value + elem.val(elem.data('lang-val')); + } + break; + } + } else { + // Set text node translated text + nodes = elem.data('lang-text'); + if (nodes) { + this._setTextNodes(elem, nodes, lang); + } + } + }; + + /** + * Call this to change the current language on the page. + * @param {String} lang The new two-letter language code to change to. + * @param {String=} selector Optional selector to find language-based + * elements for updating. + * @param {Function=} callback Optional callback function that will be + * called once the language change has been successfully processed. This + * is especially useful if you are using dynamic language pack loading + * since you will get a callback once it has been loaded and changed. + * Your callback will be passed three arguments, a boolean to denote if + * there was an error (true if error), the second will be the language + * you passed in the change call (the lang argument) and the third will + * be the selector used in the change update. + */ + Lang.prototype.change = function (lang, selector, callback) { + var self = this; + + if (lang === this.defaultLang || this.pack[lang] || this._dynamic[lang]) { + // Check if the language pack is currently loaded + if (lang !== this.defaultLang) { + if (!this.pack[lang] && this._dynamic[lang]) { + // The language pack needs loading first + console.log('Loading dynamic language pack: ' + this._dynamic[lang] + '...'); + this.loadPack(lang, function (err, loadingLang, fromUrl) { + if (!err) { + // Process the change language request + self.change.call(self, lang, selector, callback); + } else { + // Call the callback with the error + if (callback) { callback('Language pack could not load from: ' + fromUrl, lang, selector); } + } + }); + + return; + } else if (!this.pack[lang] && !this._dynamic[lang]) { + // Pack not loaded and no dynamic entry + console.log('Could not change language to ' + lang + ' because no language pack for this language exists!'); + if (callback) { callback('Language pack not defined for: ' + lang, lang, selector); } + } + } + + var fireAfterUpdate = false, + currLang = this.currentLang; + + if (this.currentLang != lang) { + this.beforeUpdate(currLang, lang); + fireAfterUpdate = true; + } + + this.currentLang = lang; + + // Get the page HTML + var arr = selector !== undefined ? $(selector).find('[lang]') : $(':not(html)[lang]'), + arrCount = arr.length, + elem; + + while (arrCount--) { + elem = $(arr[arrCount]); + + if (elem.attr('lang') !== lang) { + this._translateElement(elem, lang); + } + } + + if (fireAfterUpdate) { + this.afterUpdate(currLang, lang); + } + + // Check for cookie support + if ($.cookie) { + // Set a cookie to remember this language setting with 1 year expiry + $.cookie('langCookie', lang, { + expires: 365, + path: '/' + }); + } + + if (callback) { callback(false, lang, selector); } + } else { + console.log('Attempt to change language to "' + lang + '" but no language pack for that language is loaded!'); + if (callback) { callback('No language pack defined for: ' + lang, lang, selector); } + } + }; + + Lang.prototype._translateElement = function (elem, lang) { + // Translate attributes + this._translateAttribs(elem, lang); + + // Translate content + if (elem.attr('data-lang-content') != 'false') { + this._translateContent(elem, lang); + } + + // Update the element's current language + elem.attr('lang', lang); + }; + + /** + * Translates text from the default language into the passed language. + * @param {String} text The text to translate. + * @param {String} lang The two-letter language code to translate to. + * @returns {*} + */ + Lang.prototype.translate = function (text, lang) { + lang = lang || this.currentLang; + + if (this.pack[lang]) { + var translation = ''; + + if (lang != this.defaultLang) { + // Check for a direct token translation + translation = this.pack[lang].token[text]; + + if (!translation) { + // No token translation was found, test for regex match + translation = this._regexMatch(text, lang); + } + + if (!translation) { + console.log('Translation for "' + text + '" not found in language pack: ' + lang); + } + + return translation || text; + } else { + return text; + } + } else { + return text; + } + }; + + /** + * Checks the regex items for a match against the passed text and + * if a match is made, translates to the given replacement. + * @param {String} text The text to test regex matches against. + * @param {String} lang The two-letter language code to translate to. + * @returns {string} + * @private + */ + Lang.prototype._regexMatch = function (text, lang) { + // Loop the regex array and test them against the text + var arr, + arrCount, + arrIndex, + item, + regex, + expressionResult; + + arr = this.pack[lang].regex; + + if (arr) { + arrCount = arr.length; + + for (arrIndex = 0; arrIndex < arrCount; arrIndex++) { + item = arr[arrIndex]; + regex = item[0]; + + // Test regex + expressionResult = regex.exec(text); + + if (expressionResult && expressionResult[0]) { + return text.split(expressionResult[0]).join(item[1]); + } + } + } + + return ''; + }; + + Lang.prototype.beforeUpdate = function (currentLang, newLang) { + if (this._fireEvents) { + $(this).triggerHandler('beforeUpdate', [currentLang, newLang, this.pack[currentLang], this.pack[newLang]]); + } + }; + + Lang.prototype.afterUpdate = function (currentLang, newLang) { + if (this._fireEvents) { + $(this).triggerHandler('afterUpdate', [currentLang, newLang, this.pack[currentLang], this.pack[newLang]]); + } + }; + + Lang.prototype.refresh = function () { + // Process refresh on the page + this._fireEvents = false; + this.change(this.currentLang); + this._fireEvents = true; + }; + + //////////////////////////////////////////////////// + // Mutation overrides + //////////////////////////////////////////////////// + Lang.prototype._mutation = function (context, method, args) { + var result = this._mutationCopies[method].apply(context, args), + currLang = this.currentLang, + rootElem = $(context); + + if (rootElem.attr('lang')) { + // Switch off events for the moment + this._fireEvents = false; + + // Check if the root element is currently set to another language from current + //if (rootElem.attr('lang') !== this.currentLang) { + this._translateElement(rootElem, this.defaultLang); + this.change(this.defaultLang, rootElem); + + // Calling change above sets the global currentLang but this is supposed to be + // an isolated change so reset the global value back to what it was before + this.currentLang = currLang; + + // Record data on the default language from the root element + this._processElement(rootElem); + + // Translate the root element + this._translateElement(rootElem, this.currentLang); + //} + } + + // Record data on the default language from the root's children + this._start(rootElem); + + // Process translation on any child elements of this element + this.change(this.currentLang, rootElem); + + // Switch events back on + this._fireEvents = true; + + return result; + }; + + return Lang; +})(); \ No newline at end of file -- cgit v1.2.3