root / drupal7 / sites / all / modules / pdf_reader / js / l10n.js @ 024de6ea
1 | 85ad3d82 | Assos Assos | /* Copyright (c) 2011-2012 Fabien Cazenave, Mozilla.
|
---|---|---|---|
2 | *
|
||
3 | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
4 | * of this software and associated documentation files (the "Software"), to
|
||
5 | * deal in the Software without restriction, including without limitation the
|
||
6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||
7 | * sell copies of the Software, and to permit persons to whom the Software is
|
||
8 | * furnished to do so, subject to the following conditions:
|
||
9 | *
|
||
10 | * The above copyright notice and this permission notice shall be included in
|
||
11 | * all copies or substantial portions of the Software.
|
||
12 | *
|
||
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||
19 | * IN THE SOFTWARE.
|
||
20 | */
|
||
21 | /*
|
||
22 | Additional modifications for PDF.js project:
|
||
23 | - Loading resources from <script type='application/l10n'>;
|
||
24 | - Disabling language initialization on page loading;
|
||
25 | - Add fallback argument to the translateString.
|
||
26 | */
|
||
27 | 'use strict';
|
||
28 | |||
29 | (function(window) {
|
||
30 | var gL10nData = {};
|
||
31 | var gTextData = ''; |
||
32 | var gLanguage = ''; |
||
33 | |||
34 | // parser
|
||
35 | |||
36 | function evalString(text) { |
||
37 | return text.replace(/\\\\/g, '\\') |
||
38 | .replace(/\\n/g, '\n') |
||
39 | .replace(/\\r/g, '\r') |
||
40 | .replace(/\\t/g, '\t') |
||
41 | .replace(/\\b/g, '\b') |
||
42 | .replace(/\\f/g, '\f') |
||
43 | .replace(/\\{/g, '{') |
||
44 | .replace(/\\}/g, '}') |
||
45 | .replace(/\\"/g, '"') |
||
46 | .replace(/\\'/g, "'"); |
||
47 | } |
||
48 | |||
49 | function parseProperties(text, lang) { |
||
50 | var reBlank = /^\s*|\s*$/; |
||
51 | var reComment = /^\s*#|^\s*$/; |
||
52 | var reSection = /^\s*\[(.*)\]\s*$/; |
||
53 | var reImport = /^\s*@import\s+url\((.*)\)\s*$/i; |
||
54 | |||
55 | // parse the *.properties file into an associative array
|
||
56 | var currentLang = '*'; |
||
57 | var supportedLang = [];
|
||
58 | var skipLang = false; |
||
59 | var data = [];
|
||
60 | var match = ''; |
||
61 | var entries = text.replace(reBlank, '').split(/[\r\n]+/); |
||
62 | for (var i = 0; i < entries.length; i++) { |
||
63 | var line = entries[i];
|
||
64 | |||
65 | // comment or blank line?
|
||
66 | if (reComment.test(line))
|
||
67 | continue;
|
||
68 | |||
69 | // section start?
|
||
70 | if (reSection.test(line)) {
|
||
71 | match = reSection.exec(line); |
||
72 | currentLang = match[1];
|
||
73 | skipLang = (currentLang != lang) && (currentLang != '*') &&
|
||
74 | (currentLang != lang.substring(0, 2)); |
||
75 | continue;
|
||
76 | } else if (skipLang) { |
||
77 | continue;
|
||
78 | } |
||
79 | |||
80 | // @import rule?
|
||
81 | if (reImport.test(line)) {
|
||
82 | match = reImport.exec(line); |
||
83 | } |
||
84 | |||
85 | // key-value pair
|
||
86 | var tmp = line.split('='); |
||
87 | if (tmp.length > 1) |
||
88 | data[tmp[0]] = evalString(tmp[1]); |
||
89 | } |
||
90 | |||
91 | // find the attribute descriptions, if any
|
||
92 | for (var key in data) { |
||
93 | var id, prop, index = key.lastIndexOf('.'); |
||
94 | if (index > 0) { // attribute |
||
95 | id = key.substring(0, index);
|
||
96 | prop = key.substr(index + 1);
|
||
97 | } else { // textContent, could be innerHTML as well |
||
98 | id = key; |
||
99 | prop = 'textContent';
|
||
100 | } |
||
101 | if (!gL10nData[id])
|
||
102 | gL10nData[id] = {}; |
||
103 | gL10nData[id][prop] = data[key]; |
||
104 | } |
||
105 | } |
||
106 | |||
107 | function parse(text, lang) { |
||
108 | gTextData += text; |
||
109 | // we only support *.properties files at the moment
|
||
110 | return parseProperties(text, lang);
|
||
111 | } |
||
112 | |||
113 | // load and parse the specified resource file
|
||
114 | function loadResource(href, lang, onSuccess, onFailure) { |
||
115 | var xhr = new XMLHttpRequest(); |
||
116 | xhr.open('GET', href, true); |
||
117 | xhr.overrideMimeType('text/plain; charset=utf-8');
|
||
118 | xhr.onreadystatechange = function() { |
||
119 | if (xhr.readyState == 4) { |
||
120 | if (xhr.status == 200 || xhr.status == 0) { |
||
121 | parse(xhr.responseText, lang); |
||
122 | if (onSuccess)
|
||
123 | onSuccess(); |
||
124 | } else {
|
||
125 | if (onFailure)
|
||
126 | onFailure(); |
||
127 | } |
||
128 | } |
||
129 | }; |
||
130 | xhr.send(null);
|
||
131 | } |
||
132 | |||
133 | // load and parse all resources for the specified locale
|
||
134 | function loadLocale(lang, callback) { |
||
135 | clear(); |
||
136 | |||
137 | // check all <link type="application/l10n" href="..." /> nodes
|
||
138 | // and load the resource files
|
||
139 | var langLinks = document.querySelectorAll('link[type="application/l10n"]'); |
||
140 | var langLinksCount = langLinks.length;
|
||
141 | var langScripts = document.querySelectorAll('script[type="application/l10n"]'); |
||
142 | var langScriptCount = langScripts.length;
|
||
143 | var langCount = langLinksCount + langScriptCount;
|
||
144 | |||
145 | // start the callback when all resources are loaded
|
||
146 | var onResourceLoaded = null; |
||
147 | var gResourceCount = 0; |
||
148 | onResourceLoaded = function() { |
||
149 | gResourceCount++; |
||
150 | if (gResourceCount >= langCount) {
|
||
151 | // execute the [optional] callback
|
||
152 | if (callback)
|
||
153 | callback(); |
||
154 | // fire a 'localized' DOM event
|
||
155 | var evtObject = document.createEvent('Event'); |
||
156 | evtObject.initEvent('localized', false, false); |
||
157 | evtObject.language = lang; |
||
158 | window.dispatchEvent(evtObject); |
||
159 | } |
||
160 | } |
||
161 | |||
162 | // load all resource files
|
||
163 | function l10nResourceLink(link) { |
||
164 | var href = link.href;
|
||
165 | var type = link.type;
|
||
166 | this.load = function(lang, callback) { |
||
167 | var applied = lang;
|
||
168 | loadResource(href, lang, callback, function() {
|
||
169 | console.warn(href + ' not found.');
|
||
170 | applied = '';
|
||
171 | }); |
||
172 | return applied; // return lang if found, an empty string if not found |
||
173 | }; |
||
174 | } |
||
175 | |||
176 | gLanguage = lang; |
||
177 | for (var i = 0; i < langLinksCount; i++) { |
||
178 | var resource = new l10nResourceLink(langLinks[i]); |
||
179 | var rv = resource.load(lang, onResourceLoaded);
|
||
180 | if (rv != lang) // lang not found, used default resource instead |
||
181 | gLanguage = '';
|
||
182 | } |
||
183 | for (var i = 0; i < langScriptCount; i++) { |
||
184 | var scriptText = langScripts[i].text;
|
||
185 | parse(scriptText, lang); |
||
186 | onResourceLoaded(); |
||
187 | } |
||
188 | } |
||
189 | |||
190 | // fetch an l10n object, warn if not found
|
||
191 | function getL10nData(key) { |
||
192 | var data = gL10nData[key];
|
||
193 | if (!data)
|
||
194 | console.warn('[l10n] #' + key + ' missing for [' + gLanguage + ']'); |
||
195 | return data;
|
||
196 | } |
||
197 | |||
198 | // replace {{arguments}} with their values
|
||
199 | function substArguments(str, args) { |
||
200 | var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/; |
||
201 | var match = reArgs.exec(str);
|
||
202 | while (match) {
|
||
203 | if (!match || match.length < 2) |
||
204 | return str; // argument key not found |
||
205 | |||
206 | var arg = match[1]; |
||
207 | var sub = ''; |
||
208 | if (arg in args) { |
||
209 | sub = args[arg]; |
||
210 | } else if (arg in gL10nData) { |
||
211 | sub = gL10nData[arg].textContent; |
||
212 | } else {
|
||
213 | console.warn('[l10n] could not find argument {{' + arg + '}}'); |
||
214 | return str;
|
||
215 | } |
||
216 | |||
217 | str = str.substring(0, match.index) + sub +
|
||
218 | str.substr(match.index + match[0].length);
|
||
219 | match = reArgs.exec(str); |
||
220 | } |
||
221 | return str;
|
||
222 | } |
||
223 | |||
224 | // translate a string
|
||
225 | function translateString(key, args, fallback) { |
||
226 | var data = getL10nData(key);
|
||
227 | if (!data && fallback)
|
||
228 | data = {textContent: fallback};
|
||
229 | if (!data)
|
||
230 | return '{{' + key + '}}'; |
||
231 | return substArguments(data.textContent, args);
|
||
232 | } |
||
233 | |||
234 | // translate an HTML element
|
||
235 | function translateElement(element) { |
||
236 | if (!element || !element.dataset)
|
||
237 | return;
|
||
238 | |||
239 | // get the related l10n object
|
||
240 | var key = element.dataset.l10nId;
|
||
241 | var data = getL10nData(key);
|
||
242 | if (!data)
|
||
243 | return;
|
||
244 | |||
245 | // get arguments (if any)
|
||
246 | // TODO: more flexible parser?
|
||
247 | var args;
|
||
248 | if (element.dataset.l10nArgs) try { |
||
249 | args = JSON.parse(element.dataset.l10nArgs); |
||
250 | } catch (e) {
|
||
251 | console.warn('[l10n] could not parse arguments for #' + key + ''); |
||
252 | } |
||
253 | |||
254 | // translate element
|
||
255 | // TODO: security check?
|
||
256 | for (var k in data) |
||
257 | element[k] = substArguments(data[k], args); |
||
258 | } |
||
259 | |||
260 | // translate an HTML subtree
|
||
261 | function translateFragment(element) { |
||
262 | element = element || document.querySelector('html');
|
||
263 | |||
264 | // check all translatable children (= w/ a `data-l10n-id' attribute)
|
||
265 | var children = element.querySelectorAll('*[data-l10n-id]'); |
||
266 | var elementCount = children.length;
|
||
267 | for (var i = 0; i < elementCount; i++) |
||
268 | translateElement(children[i]); |
||
269 | |||
270 | // translate element itself if necessary
|
||
271 | if (element.dataset.l10nId)
|
||
272 | translateElement(element); |
||
273 | } |
||
274 | |||
275 | // clear all l10n data
|
||
276 | function clear() { |
||
277 | gL10nData = {}; |
||
278 | gTextData = '';
|
||
279 | gLanguage = '';
|
||
280 | } |
||
281 | |||
282 | /*
|
||
283 | // load the default locale on startup
|
||
284 | window.addEventListener('DOMContentLoaded', function() {
|
||
285 | var lang = navigator.language;
|
||
286 | if (navigator.mozSettings) {
|
||
287 | var req = navigator.mozSettings.getLock().get('language.current');
|
||
288 | req.onsuccess = function() {
|
||
289 | loadLocale(req.result['language.current'] || lang, translateFragment);
|
||
290 | };
|
||
291 | req.onerror = function() {
|
||
292 | loadLocale(lang, translateFragment);
|
||
293 | };
|
||
294 | } else {
|
||
295 | loadLocale(lang, translateFragment);
|
||
296 | }
|
||
297 | });
|
||
298 | */
|
||
299 | |||
300 | // Public API
|
||
301 | document.mozL10n = { |
||
302 | // get a localized string
|
||
303 | get: translateString,
|
||
304 | |||
305 | // get|set the document language and direction
|
||
306 | get language() { |
||
307 | return {
|
||
308 | // get|set the document language (ISO-639-1)
|
||
309 | get code() { return gLanguage; },
|
||
310 | set code(lang) { loadLocale(lang, translateFragment); }, |
||
311 | |||
312 | // get the direction (ltr|rtl) of the current language
|
||
313 | get direction() { |
||
314 | // http://www.w3.org/International/questions/qa-scripts
|
||
315 | // Arabic, Hebrew, Farsi, Pashto, Urdu
|
||
316 | var rtlList = ['ar', 'he', 'fa', 'ps', 'ur']; |
||
317 | return (rtlList.indexOf(gLanguage) >= 0) ? 'rtl' : 'ltr'; |
||
318 | } |
||
319 | }; |
||
320 | } |
||
321 | }; |
||
322 | })(this); |