app/assets/javascripts/pdfjs_viewer/pdfjs/l10n.js in pdfjs_viewer-rails-0.0.6 vs app/assets/javascripts/pdfjs_viewer/pdfjs/l10n.js in pdfjs_viewer-rails-0.0.7
- old
+ new
@@ -22,13 +22,14 @@
/*
Additional modifications for PDF.js project:
- Disables language initialization on page loading;
- Removes consoleWarn and consoleLog and use console.log/warn directly.
- Removes window._ assignment.
+ - Remove compatibility code for OldIE.
*/
-/*jshint browser: true, devel: true, globalstrict: true */
+/*jshint browser: true, devel: true, es5: true, globalstrict: true */
'use strict';
document.webL10n = (function(window, document, undefined) {
var gL10nData = {};
var gTextData = '';
@@ -96,18 +97,18 @@
evtObject.initEvent('localized', true, false);
evtObject.language = lang;
document.dispatchEvent(evtObject);
}
- function xhrLoadText(url, onSuccess, onFailure, asynchronous) {
+ function xhrLoadText(url, onSuccess, onFailure) {
onSuccess = onSuccess || function _onSuccess(data) {};
onFailure = onFailure || function _onFailure() {
console.warn(url + ' not found.');
};
var xhr = new XMLHttpRequest();
- xhr.open('GET', url, asynchronous);
+ xhr.open('GET', url, gAsyncResourceLoading);
if (xhr.overrideMimeType) {
xhr.overrideMimeType('text/plain; charset=utf-8');
}
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
@@ -140,11 +141,11 @@
*
* @param {string} href
* URL of the l10n resource to parse.
*
* @param {string} lang
- * locale (language) to parse.
+ * locale (language) to parse. Must be a lowercase string.
*
* @param {Function} successCallback
* triggered when the l10n resource has been successully parsed.
*
* @param {Function} failureCallback
@@ -172,104 +173,127 @@
.replace(/\\"/g, '"')
.replace(/\\'/g, "'");
}
// parse *.properties text data into an l10n dictionary
- function parseProperties(text) {
- var dictionary = [];
+ // If gAsyncResourceLoading is false, then the callback will be called
+ // synchronously. Otherwise it is called asynchronously.
+ function parseProperties(text, parsedPropertiesCallback) {
+ var dictionary = {};
// token expressions
var reBlank = /^\s*|\s*$/;
var reComment = /^\s*#|^\s*$/;
var reSection = /^\s*\[(.*)\]\s*$/;
var reImport = /^\s*@import\s+url\((.*)\)\s*$/i;
var reSplit = /^([^=\s]*)\s*=\s*(.+)$/; // TODO: escape EOLs with '\'
// parse the *.properties file into an associative array
- function parseRawLines(rawText, extendedSyntax) {
+ function parseRawLines(rawText, extendedSyntax, parsedRawLinesCallback) {
var entries = rawText.replace(reBlank, '').split(/[\r\n]+/);
var currentLang = '*';
- var genericLang = lang.replace(/-[a-z]+$/i, '');
+ var genericLang = lang.split('-', 1)[0];
var skipLang = false;
var match = '';
- for (var i = 0; i < entries.length; i++) {
- var line = entries[i];
+ function nextEntry() {
+ // Use infinite loop instead of recursion to avoid reaching the
+ // maximum recursion limit for content with many lines.
+ while (true) {
+ if (!entries.length) {
+ parsedRawLinesCallback();
+ return;
+ }
+ var line = entries.shift();
- // comment or blank line?
- if (reComment.test(line))
- continue;
+ // comment or blank line?
+ if (reComment.test(line))
+ continue;
- // the extended syntax supports [lang] sections and @import rules
- if (extendedSyntax) {
- if (reSection.test(line)) { // section start?
+ // the extended syntax supports [lang] sections and @import rules
+ if (extendedSyntax) {
match = reSection.exec(line);
- currentLang = match[1];
- skipLang = (currentLang !== '*') &&
- (currentLang !== lang) && (currentLang !== genericLang);
- continue;
- } else if (skipLang) {
- continue;
- }
- if (reImport.test(line)) { // @import rule?
+ if (match) { // section start?
+ // RFC 4646, section 4.4, "All comparisons MUST be performed
+ // in a case-insensitive manner."
+
+ currentLang = match[1].toLowerCase();
+ skipLang = (currentLang !== '*') &&
+ (currentLang !== lang) && (currentLang !== genericLang);
+ continue;
+ } else if (skipLang) {
+ continue;
+ }
match = reImport.exec(line);
- loadImport(baseURL + match[1]); // load the resource synchronously
+ if (match) { // @import rule?
+ loadImport(baseURL + match[1], nextEntry);
+ return;
+ }
}
- }
- // key-value pair
- var tmp = line.match(reSplit);
- if (tmp && tmp.length == 3) {
- dictionary[tmp[1]] = evalString(tmp[2]);
+ // key-value pair
+ var tmp = line.match(reSplit);
+ if (tmp && tmp.length == 3) {
+ dictionary[tmp[1]] = evalString(tmp[2]);
+ }
}
}
+ nextEntry();
}
// import another *.properties file
- function loadImport(url) {
+ function loadImport(url, callback) {
xhrLoadText(url, function(content) {
- parseRawLines(content, false); // don't allow recursive imports
- }, null, false); // load synchronously
+ parseRawLines(content, false, callback); // don't allow recursive imports
+ }, null);
}
// fill the dictionary
- parseRawLines(text, true);
- return dictionary;
+ parseRawLines(text, true, function() {
+ parsedPropertiesCallback(dictionary);
+ });
}
// load and parse l10n data (warning: global variables are used here)
xhrLoadText(href, function(response) {
gTextData += response; // mostly for debug
// parse *.properties text data into an l10n dictionary
- var data = parseProperties(response);
+ parseProperties(response, function(data) {
- // find attribute descriptions, if any
- for (var key in data) {
- var id, prop, index = key.lastIndexOf('.');
- if (index > 0) { // an attribute has been specified
- id = key.substring(0, index);
- prop = key.substr(index + 1);
- } else { // no attribute: assuming text content by default
- id = key;
- prop = gTextProp;
+ // find attribute descriptions, if any
+ for (var key in data) {
+ var id, prop, index = key.lastIndexOf('.');
+ if (index > 0) { // an attribute has been specified
+ id = key.substring(0, index);
+ prop = key.substr(index + 1);
+ } else { // no attribute: assuming text content by default
+ id = key;
+ prop = gTextProp;
+ }
+ if (!gL10nData[id]) {
+ gL10nData[id] = {};
+ }
+ gL10nData[id][prop] = data[key];
}
- if (!gL10nData[id]) {
- gL10nData[id] = {};
- }
- gL10nData[id][prop] = data[key];
- }
- // trigger callback
- if (successCallback) {
- successCallback();
- }
- }, failureCallback, gAsyncResourceLoading);
+ // trigger callback
+ if (successCallback) {
+ successCallback();
+ }
+ });
+ }, failureCallback);
}
// load and parse all resources for the specified locale
function loadLocale(lang, callback) {
+ // RFC 4646, section 2.1 states that language tags have to be treated as
+ // case-insensitive. Convert to lowercase for case-insensitive comparisons.
+ if (lang) {
+ lang = lang.toLowerCase();
+ }
+
callback = callback || function _callback() {};
clear();
gLanguage = lang;
@@ -280,11 +304,23 @@
if (langCount === 0) {
// we might have a pre-compiled dictionary instead
var dict = getL10nDictionary();
if (dict && dict.locales && dict.default_locale) {
console.log('using the embedded JSON directory, early way out');
- gL10nData = dict.locales[lang] || dict.locales[dict.default_locale];
+ gL10nData = dict.locales[lang];
+ if (!gL10nData) {
+ var defaultLocale = dict.default_locale.toLowerCase();
+ for (var anyCaseLang in dict.locales) {
+ anyCaseLang = anyCaseLang.toLowerCase();
+ if (anyCaseLang === lang) {
+ gL10nData = dict.locales[lang];
+ break;
+ } else if (anyCaseLang === defaultLocale) {
+ gL10nData = dict.locales[defaultLocale];
+ }
+ }
+ }
callback();
} else {
console.log('no resource to load, early way out');
}
// early way out
@@ -306,28 +342,27 @@
};
// load all resource files
function L10nResourceLink(link) {
var href = link.href;
- var type = link.type;
+ // Note: If |gAsyncResourceLoading| is false, then the following callbacks
+ // are synchronously called.
this.load = function(lang, callback) {
- var applied = lang;
parseResource(href, lang, callback, function() {
console.warn(href + ' not found.');
- applied = '';
+ // lang not found, used default resource instead
+ console.warn('"' + lang + '" resource not found');
+ gLanguage = '';
+ // Resource not loaded, but we still need to call the callback.
+ callback();
});
- return applied; // return lang if found, an empty string if not found
};
}
for (var i = 0; i < langCount; i++) {
var resource = new L10nResourceLink(langLinks[i]);
- var rv = resource.load(lang, onResourceLoaded);
- if (rv != lang) { // lang not found, used default resource instead
- console.warn('"' + lang + '" resource not found');
- gLanguage = '';
- }
+ resource.load(lang, onResourceLoaded);
}
}
// clear all l10n data
function clear() {
@@ -839,32 +874,21 @@
return str;
}
// replace {{arguments}} with their values
function substArguments(str, args, key) {
- var reArgs = /\{\{\s*(.+?)\s*\}\}/;
- var match = reArgs.exec(str);
- while (match) {
- if (!match || match.length < 2)
- return str; // argument key not found
-
- var arg = match[1];
- var sub = '';
+ var reArgs = /\{\{\s*(.+?)\s*\}\}/g;
+ return str.replace(reArgs, function(matched_text, arg) {
if (args && arg in args) {
- sub = args[arg];
- } else if (arg in gL10nData) {
- sub = gL10nData[arg][gTextProp];
- } else {
- console.log('argument {{' + arg + '}} for #' + key + ' is undefined.');
- return str;
+ return args[arg];
}
-
- str = str.substring(0, match.index) + sub +
- str.substr(match.index + match[0].length);
- match = reArgs.exec(str);
- }
- return str;
+ if (arg in gL10nData) {
+ return gL10nData[arg];
+ }
+ console.log('argument {{' + arg + '}} for #' + key + ' is undefined.');
+ return matched_text;
+ });
}
// translate an HTML element
function translateElement(element) {
var l10n = getL10nAttributes(element);
@@ -940,11 +964,10 @@
// translate element itself if necessary
translateElement(element);
}
- // cross-browser API (sorry, oldIE doesn't support getters & setters)
return {
// get a localized string
get: function(key, args, fallbackString) {
var index = key.lastIndexOf('.');
var prop = gTextProp;
@@ -968,18 +991,25 @@
getData: function() { return gL10nData; },
getText: function() { return gTextData; },
// get|set the document language
getLanguage: function() { return gLanguage; },
- setLanguage: function(lang) { loadLocale(lang, translateFragment); },
+ setLanguage: function(lang, callback) {
+ loadLocale(lang, function() {
+ if (callback)
+ callback();
+ translateFragment();
+ });
+ },
// get the direction (ltr|rtl) of the current language
getDirection: function() {
// http://www.w3.org/International/questions/qa-scripts
// Arabic, Hebrew, Farsi, Pashto, Urdu
var rtlList = ['ar', 'he', 'fa', 'ps', 'ur'];
- return (rtlList.indexOf(gLanguage) >= 0) ? 'rtl' : 'ltr';
+ var shortCode = gLanguage.split('-', 1)[0];
+ return (rtlList.indexOf(shortCode) >= 0) ? 'rtl' : 'ltr';
},
// translate an element or document fragment
translate: translateFragment,
@@ -987,17 +1017,16 @@
getReadyState: function() { return gReadyState; },
ready: function(callback) {
if (!callback) {
return;
} else if (gReadyState == 'complete' || gReadyState == 'interactive') {
- window.setTimeout(callback);
+ window.setTimeout(function() {
+ callback();
+ });
} else if (document.addEventListener) {
- document.addEventListener('localized', callback);
- } else if (document.attachEvent) {
- document.documentElement.attachEvent('onpropertychange', function(e) {
- if (e.propertyName === 'localized') {
- callback();
- }
+ document.addEventListener('localized', function once() {
+ document.removeEventListener('localized', once);
+ callback();
});
}
}
};
}) (window, document);