pdfjs_rails 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +37 -0
  6. data/Rakefile +1 -0
  7. data/lib/generators/pdfjs_rails/install_generator.rb +13 -0
  8. data/lib/pdfjs/build/pdf.js +7071 -0
  9. data/lib/pdfjs/build/pdf.worker.js +38687 -0
  10. data/lib/pdfjs/web/compatibility.js +471 -0
  11. data/lib/pdfjs/web/debugger.js +532 -0
  12. data/lib/pdfjs/web/images/annotation-check.svg +10 -0
  13. data/lib/pdfjs/web/images/annotation-comment.svg +15 -0
  14. data/lib/pdfjs/web/images/annotation-help.svg +25 -0
  15. data/lib/pdfjs/web/images/annotation-insert.svg +9 -0
  16. data/lib/pdfjs/web/images/annotation-key.svg +10 -0
  17. data/lib/pdfjs/web/images/annotation-newparagraph.svg +10 -0
  18. data/lib/pdfjs/web/images/annotation-note.svg +41 -0
  19. data/lib/pdfjs/web/images/annotation-paragraph.svg +15 -0
  20. data/lib/pdfjs/web/images/findbarButton-next-rtl.png +0 -0
  21. data/lib/pdfjs/web/images/findbarButton-next.png +0 -0
  22. data/lib/pdfjs/web/images/findbarButton-previous-rtl.png +0 -0
  23. data/lib/pdfjs/web/images/findbarButton-previous.png +0 -0
  24. data/lib/pdfjs/web/images/loading-icon.gif +0 -0
  25. data/lib/pdfjs/web/images/loading-small.png +0 -0
  26. data/lib/pdfjs/web/images/secondaryToolbarButton-firstPage.png +0 -0
  27. data/lib/pdfjs/web/images/secondaryToolbarButton-lastPage.png +0 -0
  28. data/lib/pdfjs/web/images/secondaryToolbarButton-rotateCcw.png +0 -0
  29. data/lib/pdfjs/web/images/secondaryToolbarButton-rotateCw.png +0 -0
  30. data/lib/pdfjs/web/images/shadow.png +0 -0
  31. data/lib/pdfjs/web/images/texture.png +0 -0
  32. data/lib/pdfjs/web/images/toolbarButton-bookmark.png +0 -0
  33. data/lib/pdfjs/web/images/toolbarButton-download.png +0 -0
  34. data/lib/pdfjs/web/images/toolbarButton-menuArrows.png +0 -0
  35. data/lib/pdfjs/web/images/toolbarButton-openFile.png +0 -0
  36. data/lib/pdfjs/web/images/toolbarButton-pageDown-rtl.png +0 -0
  37. data/lib/pdfjs/web/images/toolbarButton-pageDown.png +0 -0
  38. data/lib/pdfjs/web/images/toolbarButton-pageUp-rtl.png +0 -0
  39. data/lib/pdfjs/web/images/toolbarButton-pageUp.png +0 -0
  40. data/lib/pdfjs/web/images/toolbarButton-presentationMode.png +0 -0
  41. data/lib/pdfjs/web/images/toolbarButton-print.png +0 -0
  42. data/lib/pdfjs/web/images/toolbarButton-search.png +0 -0
  43. data/lib/pdfjs/web/images/toolbarButton-secondaryToolbarToggle.png +0 -0
  44. data/lib/pdfjs/web/images/toolbarButton-sidebarToggle.png +0 -0
  45. data/lib/pdfjs/web/images/toolbarButton-viewOutline.png +0 -0
  46. data/lib/pdfjs/web/images/toolbarButton-viewThumbnail.png +0 -0
  47. data/lib/pdfjs/web/images/toolbarButton-zoomIn.png +0 -0
  48. data/lib/pdfjs/web/images/toolbarButton-zoomOut.png +0 -0
  49. data/lib/pdfjs/web/l10n.js +922 -0
  50. data/lib/pdfjs/web/locale/ar/viewer.properties +112 -0
  51. data/lib/pdfjs/web/locale/ca/viewer.properties +131 -0
  52. data/lib/pdfjs/web/locale/cs/viewer.properties +58 -0
  53. data/lib/pdfjs/web/locale/da/viewer.properties +132 -0
  54. data/lib/pdfjs/web/locale/de/viewer.properties +128 -0
  55. data/lib/pdfjs/web/locale/el/viewer.properties +124 -0
  56. data/lib/pdfjs/web/locale/en-US/viewer.properties +134 -0
  57. data/lib/pdfjs/web/locale/es/viewer.properties +131 -0
  58. data/lib/pdfjs/web/locale/fi/viewer.properties +129 -0
  59. data/lib/pdfjs/web/locale/fr/viewer.properties +130 -0
  60. data/lib/pdfjs/web/locale/he/viewer.properties +59 -0
  61. data/lib/pdfjs/web/locale/it/viewer.properties +44 -0
  62. data/lib/pdfjs/web/locale/ja/viewer.properties +132 -0
  63. data/lib/pdfjs/web/locale/ko/viewer.properties +131 -0
  64. data/lib/pdfjs/web/locale/locale.properties +78 -0
  65. data/lib/pdfjs/web/locale/lt/viewer.properties +129 -0
  66. data/lib/pdfjs/web/locale/nl/viewer.properties +134 -0
  67. data/lib/pdfjs/web/locale/pl/viewer.properties +132 -0
  68. data/lib/pdfjs/web/locale/pt-BR/viewer.properties +44 -0
  69. data/lib/pdfjs/web/locale/ro/viewer.properties +55 -0
  70. data/lib/pdfjs/web/locale/ru/viewer.properties +62 -0
  71. data/lib/pdfjs/web/locale/sr/viewer.properties +55 -0
  72. data/lib/pdfjs/web/locale/sv/viewer.properties +134 -0
  73. data/lib/pdfjs/web/locale/tr/viewer.properties +129 -0
  74. data/lib/pdfjs/web/locale/vi/viewer.properties +131 -0
  75. data/lib/pdfjs/web/locale/zh-CN/viewer.properties +129 -0
  76. data/lib/pdfjs/web/locale/zh-TW/viewer.properties +132 -0
  77. data/lib/pdfjs/web/viewer.css +1544 -0
  78. data/lib/pdfjs/web/viewer.html +250 -0
  79. data/lib/pdfjs/web/viewer.js +4477 -0
  80. data/lib/pdfjs_rails.rb +6 -0
  81. data/lib/pdfjs_rails/railtie.rb +9 -0
  82. data/lib/pdfjs_rails/version.rb +3 -0
  83. data/lib/pdfjs_rails/view_helpers.rb +225 -0
  84. data/pdfjs_rails.gemspec +24 -0
  85. data/vender/assets/images/annotation-check.svg +10 -0
  86. data/vender/assets/images/annotation-comment.svg +15 -0
  87. data/vender/assets/images/annotation-help.svg +25 -0
  88. data/vender/assets/images/annotation-insert.svg +9 -0
  89. data/vender/assets/images/annotation-key.svg +10 -0
  90. data/vender/assets/images/annotation-newparagraph.svg +10 -0
  91. data/vender/assets/images/annotation-note.svg +41 -0
  92. data/vender/assets/images/annotation-paragraph.svg +15 -0
  93. data/vender/assets/images/findbarButton-next-rtl.png +0 -0
  94. data/vender/assets/images/findbarButton-next.png +0 -0
  95. data/vender/assets/images/findbarButton-previous-rtl.png +0 -0
  96. data/vender/assets/images/findbarButton-previous.png +0 -0
  97. data/vender/assets/images/loading-icon.gif +0 -0
  98. data/vender/assets/images/loading-small.png +0 -0
  99. data/vender/assets/images/shadow.png +0 -0
  100. data/vender/assets/images/texture.png +0 -0
  101. data/vender/assets/images/toolbarButton-bookmark.png +0 -0
  102. data/vender/assets/images/toolbarButton-download.png +0 -0
  103. data/vender/assets/images/toolbarButton-menuArrows.png +0 -0
  104. data/vender/assets/images/toolbarButton-openFile.png +0 -0
  105. data/vender/assets/images/toolbarButton-pageDown-rtl.png +0 -0
  106. data/vender/assets/images/toolbarButton-pageDown.png +0 -0
  107. data/vender/assets/images/toolbarButton-pageUp-rtl.png +0 -0
  108. data/vender/assets/images/toolbarButton-pageUp.png +0 -0
  109. data/vender/assets/images/toolbarButton-presentationMode.png +0 -0
  110. data/vender/assets/images/toolbarButton-print.png +0 -0
  111. data/vender/assets/images/toolbarButton-search.png +0 -0
  112. data/vender/assets/images/toolbarButton-sidebarToggle.png +0 -0
  113. data/vender/assets/images/toolbarButton-viewOutline.png +0 -0
  114. data/vender/assets/images/toolbarButton-viewThumbnail.png +0 -0
  115. data/vender/assets/images/toolbarButton-zoomIn.png +0 -0
  116. data/vender/assets/images/toolbarButton-zoomOut.png +0 -0
  117. data/vender/assets/javascripts/application.js +22 -0
  118. data/vender/assets/javascripts/compatibility.js +471 -0
  119. data/vender/assets/javascripts/debugger.js +532 -0
  120. data/vender/assets/javascripts/l10n.js +922 -0
  121. data/vender/assets/javascripts/pdf.js +7063 -0
  122. data/vender/assets/javascripts/pdf.worker.js +38679 -0
  123. data/vender/assets/javascripts/viewer.js +4317 -0
  124. data/vender/assets/stylesheets/viewer.css +1382 -0
  125. metadata +209 -0
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="40"
5
+ height="40">
6
+ <path
7
+ d="M 1.5006714,23.536225 6.8925879,18.994244 14.585721,26.037937 34.019683,4.5410479 38.499329,9.2235032 14.585721,35.458952 z"
8
+ id="path4"
9
+ style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.25402856;stroke-opacity:1" />
10
+ </svg>
@@ -0,0 +1,15 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ height="40"
5
+ width="40">
6
+ <rect
7
+ style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
8
+ width="33.76017"
9
+ height="33.76017"
10
+ x="3.119915"
11
+ y="3.119915" />
12
+ <path
13
+ d="m 20.677967,8.54499 c -7.342801,0 -13.295293,4.954293 -13.295293,11.065751 0,2.088793 0.3647173,3.484376 1.575539,5.150563 L 6.0267418,31.45501 13.560595,29.011117 c 2.221262,1.387962 4.125932,1.665377 7.117372,1.665377 7.3428,0 13.295291,-4.954295 13.295291,-11.065753 0,-6.111458 -5.952491,-11.065751 -13.295291,-11.065751 z"
14
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.93031836;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
15
+ </svg>
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="40"
5
+ height="40">
6
+ <g
7
+ transform="translate(0,-60)"
8
+ id="layer1">
9
+ <rect
10
+ width="36.460953"
11
+ height="34.805603"
12
+ x="1.7695236"
13
+ y="62.597198"
14
+ style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.30826771;stroke-opacity:1" />
15
+ <g
16
+ transform="matrix(0.88763677,0,0,0.88763677,2.2472646,8.9890584)">
17
+ <path
18
+ d="M 20,64.526342 C 11.454135,64.526342 4.5263421,71.454135 4.5263421,80 4.5263421,88.545865 11.454135,95.473658 20,95.473658 28.545865,95.473658 35.473658,88.545865 35.473658,80 35.473658,71.454135 28.545865,64.526342 20,64.526342 z m -0.408738,9.488564 c 3.527079,0 6.393832,2.84061 6.393832,6.335441 0,3.494831 -2.866753,6.335441 -6.393832,6.335441 -3.527079,0 -6.393832,-2.84061 -6.393832,-6.335441 0,-3.494831 2.866753,-6.335441 6.393832,-6.335441 z"
19
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.02768445;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
20
+ <path
21
+ d="m 7.2335209,71.819938 4.9702591,4.161823 c -1.679956,2.581606 -1.443939,6.069592 0.159325,8.677725 l -5.1263071,3.424463 c 0.67516,1.231452 3.0166401,3.547686 4.2331971,4.194757 l 3.907728,-4.567277 c 2.541952,1.45975 5.730694,1.392161 8.438683,-0.12614 l 3.469517,6.108336 c 1.129779,-0.44367 4.742234,-3.449633 5.416358,-5.003859 l -5.46204,-4.415541 c 1.44319,-2.424098 1.651175,-5.267515 0.557303,-7.748623 l 5.903195,-3.833951 C 33.14257,71.704996 30.616217,69.018606 29.02952,67.99296 l -4.118813,4.981678 C 22.411934,71.205099 18.900853,70.937534 16.041319,72.32916 l -3.595408,-5.322091 c -1.345962,0.579488 -4.1293881,2.921233 -5.2123901,4.812869 z m 8.1010311,3.426672 c 2.75284,-2.446266 6.769149,-2.144694 9.048998,0.420874 2.279848,2.56557 2.113919,6.596919 -0.638924,9.043185 -2.752841,2.446267 -6.775754,2.13726 -9.055604,-0.428308 -2.279851,-2.565568 -2.107313,-6.589485 0.64553,-9.035751 z"
22
+ style="fill:#000000;fill-opacity:1;stroke:none" />
23
+ </g>
24
+ </g>
25
+ </svg>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="64"
5
+ height="64">
6
+ <path
7
+ d="M 32.003143,1.4044602 57.432701,62.632577 6.5672991,62.627924 z"
8
+ style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:1.00493038;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
9
+ </svg>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="64"
5
+ height="64">
6
+ <path
7
+ d="M 25.470843,9.4933766 C 25.30219,12.141818 30.139101,14.445969 34.704831,13.529144 40.62635,12.541995 41.398833,7.3856498 35.97505,5.777863 31.400921,4.1549155 25.157674,6.5445892 25.470843,9.4933766 z M 4.5246282,17.652051 C 4.068249,11.832873 9.2742983,5.9270407 18.437379,3.0977088 29.751911,-0.87185184 45.495663,1.4008022 53.603953,7.1104009 c 9.275765,6.1889221 7.158128,16.2079421 -3.171076,21.5939521 -1.784316,1.635815 -6.380222,1.21421 -7.068351,3.186186 -1.04003,0.972427 -1.288046,2.050158 -1.232864,3.168203 1.015111,2.000108 -3.831548,1.633216 -3.270553,3.759574 0.589477,5.264544 -0.179276,10.53738 -0.362842,15.806257 -0.492006,2.184998 1.163456,4.574232 -0.734888,6.610642 -2.482919,2.325184 -7.30604,2.189143 -9.193497,-0.274767 -2.733688,-1.740626 -8.254447,-3.615254 -6.104247,-6.339626 3.468112,-1.708686 -2.116197,-3.449897 0.431242,-5.080274 5.058402,-1.39256 -2.393215,-2.304318 -0.146889,-4.334645 3.069198,-0.977415 2.056986,-2.518352 -0.219121,-3.540397 1.876567,-1.807151 1.484149,-4.868919 -2.565455,-5.942205 0.150866,-1.805474 2.905737,-4.136876 -1.679967,-5.20493 C 10.260902,27.882167 4.6872697,22.95045 4.5245945,17.652051 z"
8
+ id="path604"
9
+ style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.72665179;stroke-opacity:1" />
10
+ </svg>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="64"
5
+ height="64">
6
+ <path
7
+ d="M 32.003143,10.913072 57.432701,53.086929 6.567299,53.083723 z"
8
+ id="path2985"
9
+ style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:0.83403099;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
10
+ </svg>
@@ -0,0 +1,41 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="40"
5
+ height="40">
6
+ <rect
7
+ width="36.075428"
8
+ height="31.096582"
9
+ x="1.962286"
10
+ y="4.4517088"
11
+ id="rect4"
12
+ style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.23004246;stroke-opacity:1" />
13
+ <rect
14
+ width="27.96859"
15
+ height="1.5012145"
16
+ x="6.0157046"
17
+ y="10.285"
18
+ id="rect6"
19
+ style="fill:#000000;fill-opacity:1;stroke:none" />
20
+ <rect
21
+ width="27.96859"
22
+ height="0.85783684"
23
+ x="6.0157056"
24
+ y="23.21689"
25
+ id="rect8"
26
+ style="fill:#000000;fill-opacity:1;stroke:none" />
27
+ <rect
28
+ width="27.96859"
29
+ height="0.85783684"
30
+ x="5.8130345"
31
+ y="28.964394"
32
+ id="rect10"
33
+ style="fill:#000000;fill-opacity:1;stroke:none" />
34
+ <rect
35
+ width="27.96859"
36
+ height="0.85783684"
37
+ x="6.0157046"
38
+ y="17.426493"
39
+ id="rect12"
40
+ style="fill:#000000;fill-opacity:1;stroke:none" />
41
+ </svg>
@@ -0,0 +1,15 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="40"
5
+ height="40">
6
+ <rect
7
+ width="33.76017"
8
+ height="33.76017"
9
+ x="3.119915"
10
+ y="3.119915"
11
+ style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
12
+ <path
13
+ d="m 17.692678,34.50206 0,-16.182224 c -1.930515,-0.103225 -3.455824,-0.730383 -4.57593,-1.881473 -1.12011,-1.151067 -1.680164,-2.619596 -1.680164,-4.405591 0,-1.992435 0.621995,-3.5796849 1.865988,-4.7617553 1.243989,-1.1820288 3.06352,-1.7730536 5.458598,-1.7730764 l 9.802246,0 0,2.6789711 -2.229895,0 0,26.3251486 -2.632515,0 0,-26.3251486 -3.45324,0 0,26.3251486 z"
14
+ style="font-size:29.42051125px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.07795751;stroke-opacity:1;font-family:Arial;-inkscape-font-specification:Arial" />
15
+ </svg>
Binary file
Binary file
@@ -0,0 +1,922 @@
1
+ /** 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
+ - Disables language initialization on page loading;
24
+ - Adds fallback argument to the getL10nData;
25
+ - Removes consoleLog and simplifies consoleWarn;
26
+ - Removes window._ assignment.
27
+ */
28
+ /*jshint browser: true, devel: true, es5: true, globalstrict: true */
29
+ 'use strict';
30
+
31
+ document.webL10n = (function(window, document, undefined) {
32
+ var gL10nData = {};
33
+ var gTextData = '';
34
+ var gTextProp = 'textContent';
35
+ var gLanguage = '';
36
+ var gMacros = {};
37
+ var gReadyState = 'loading';
38
+
39
+ // read-only setting -- we recommend to load l10n resources synchronously
40
+ var gAsyncResourceLoading = true;
41
+
42
+ // debug helpers
43
+ function consoleWarn(message) {
44
+ console.log('[l10n] ' + message);
45
+ };
46
+
47
+ /**
48
+ * DOM helpers for the so-called "HTML API".
49
+ *
50
+ * These functions are written for modern browsers. For old versions of IE,
51
+ * they're overridden in the 'startup' section at the end of this file.
52
+ */
53
+
54
+ function getL10nResourceLinks() {
55
+ return document.querySelectorAll('link[type="application/l10n"]');
56
+ }
57
+
58
+ function getTranslatableChildren(element) {
59
+ return element ? element.querySelectorAll('*[data-l10n-id]') : [];
60
+ }
61
+
62
+ function getL10nAttributes(element) {
63
+ if (!element)
64
+ return {};
65
+
66
+ var l10nId = element.getAttribute('data-l10n-id');
67
+ var l10nArgs = element.getAttribute('data-l10n-args');
68
+ var args = {};
69
+ if (l10nArgs) {
70
+ try {
71
+ args = JSON.parse(l10nArgs);
72
+ } catch (e) {
73
+ consoleWarn('could not parse arguments for #' + l10nId);
74
+ }
75
+ }
76
+ return { id: l10nId, args: args };
77
+ }
78
+
79
+ function fireL10nReadyEvent(lang) {
80
+ var evtObject = document.createEvent('Event');
81
+ evtObject.initEvent('localized', false, false);
82
+ evtObject.language = lang;
83
+ window.dispatchEvent(evtObject);
84
+ }
85
+
86
+
87
+ /**
88
+ * l10n resource parser:
89
+ * - reads (async XHR) the l10n resource matching `lang';
90
+ * - imports linked resources (synchronously) when specified;
91
+ * - parses the text data (fills `gL10nData' and `gTextData');
92
+ * - triggers success/failure callbacks when done.
93
+ *
94
+ * @param {string} href
95
+ * URL of the l10n resource to parse.
96
+ *
97
+ * @param {string} lang
98
+ * locale (language) to parse.
99
+ *
100
+ * @param {Function} successCallback
101
+ * triggered when the l10n resource has been successully parsed.
102
+ *
103
+ * @param {Function} failureCallback
104
+ * triggered when the an error has occured.
105
+ *
106
+ * @return {void}
107
+ * uses the following global variables: gL10nData, gTextData, gTextProp.
108
+ */
109
+
110
+ function parseResource(href, lang, successCallback, failureCallback) {
111
+ var baseURL = href.replace(/\/[^\/]*$/, '/');
112
+
113
+ // handle escaped characters (backslashes) in a string
114
+ function evalString(text) {
115
+ if (text.lastIndexOf('\\') < 0)
116
+ return text;
117
+ return text.replace(/\\\\/g, '\\')
118
+ .replace(/\\n/g, '\n')
119
+ .replace(/\\r/g, '\r')
120
+ .replace(/\\t/g, '\t')
121
+ .replace(/\\b/g, '\b')
122
+ .replace(/\\f/g, '\f')
123
+ .replace(/\\{/g, '{')
124
+ .replace(/\\}/g, '}')
125
+ .replace(/\\"/g, '"')
126
+ .replace(/\\'/g, "'");
127
+ }
128
+
129
+ // parse *.properties text data into an l10n dictionary
130
+ function parseProperties(text) {
131
+ var dictionary = [];
132
+
133
+ // token expressions
134
+ var reBlank = /^\s*|\s*$/;
135
+ var reComment = /^\s*#|^\s*$/;
136
+ var reSection = /^\s*\[(.*)\]\s*$/;
137
+ var reImport = /^\s*@import\s+url\((.*)\)\s*$/i;
138
+ var reSplit = /^([^=\s]*)\s*=\s*(.+)$/; // TODO: escape EOLs with '\'
139
+
140
+ // parse the *.properties file into an associative array
141
+ function parseRawLines(rawText, extendedSyntax) {
142
+ var entries = rawText.replace(reBlank, '').split(/[\r\n]+/);
143
+ var currentLang = '*';
144
+ var genericLang = lang.replace(/-[a-z]+$/i, '');
145
+ var skipLang = false;
146
+ var match = '';
147
+
148
+ for (var i = 0; i < entries.length; i++) {
149
+ var line = entries[i];
150
+
151
+ // comment or blank line?
152
+ if (reComment.test(line))
153
+ continue;
154
+
155
+ // the extended syntax supports [lang] sections and @import rules
156
+ if (extendedSyntax) {
157
+ if (reSection.test(line)) { // section start?
158
+ match = reSection.exec(line);
159
+ currentLang = match[1];
160
+ skipLang = (currentLang !== '*') &&
161
+ (currentLang !== lang) && (currentLang !== genericLang);
162
+ continue;
163
+ } else if (skipLang) {
164
+ continue;
165
+ }
166
+ if (reImport.test(line)) { // @import rule?
167
+ match = reImport.exec(line);
168
+ loadImport(baseURL + match[1]); // load the resource synchronously
169
+ }
170
+ }
171
+
172
+ // key-value pair
173
+ var tmp = line.match(reSplit);
174
+ if (tmp && tmp.length == 3)
175
+ dictionary[tmp[1]] = evalString(tmp[2]);
176
+ }
177
+ }
178
+
179
+ // import another *.properties file
180
+ function loadImport(url) {
181
+ loadResource(url, function(content) {
182
+ parseRawLines(content, false); // don't allow recursive imports
183
+ }, false, false); // load synchronously
184
+ }
185
+
186
+ // fill the dictionary
187
+ parseRawLines(text, true);
188
+ return dictionary;
189
+ }
190
+
191
+ // load the specified resource file
192
+ function loadResource(url, onSuccess, onFailure, asynchronous) {
193
+ var xhr = new XMLHttpRequest();
194
+ xhr.open('GET', url, asynchronous);
195
+ if (xhr.overrideMimeType) {
196
+ xhr.overrideMimeType('text/plain; charset=utf-8');
197
+ }
198
+ xhr.onreadystatechange = function() {
199
+ if (xhr.readyState == 4) {
200
+ if (xhr.status == 200 || xhr.status === 0) {
201
+ if (onSuccess)
202
+ onSuccess(xhr.responseText);
203
+ } else {
204
+ if (onFailure)
205
+ onFailure();
206
+ }
207
+ }
208
+ };
209
+ xhr.send(null);
210
+ }
211
+
212
+ // load and parse l10n data (warning: global variables are used here)
213
+ loadResource(href, function(response) {
214
+ gTextData += response; // mostly for debug
215
+
216
+ // parse *.properties text data into an l10n dictionary
217
+ var data = parseProperties(response);
218
+
219
+ // find attribute descriptions, if any
220
+ for (var key in data) {
221
+ var id, prop, index = key.lastIndexOf('.');
222
+ if (index > 0) { // an attribute has been specified
223
+ id = key.substring(0, index);
224
+ prop = key.substr(index + 1);
225
+ } else { // no attribute: assuming text content by default
226
+ id = key;
227
+ prop = gTextProp;
228
+ }
229
+ if (!gL10nData[id]) {
230
+ gL10nData[id] = {};
231
+ }
232
+ gL10nData[id][prop] = data[key];
233
+ }
234
+
235
+ // trigger callback
236
+ if (successCallback)
237
+ successCallback();
238
+ }, failureCallback, gAsyncResourceLoading);
239
+ };
240
+
241
+ // load and parse all resources for the specified locale
242
+ function loadLocale(lang, callback) {
243
+ clear();
244
+ gLanguage = lang;
245
+
246
+ // check all <link type="application/l10n" href="..." /> nodes
247
+ // and load the resource files
248
+ var langLinks = getL10nResourceLinks();
249
+ var langCount = langLinks.length;
250
+ if (langCount == 0) {
251
+ consoleWarn('no resource to load, early way out');
252
+ fireL10nReadyEvent(lang);
253
+ gReadyState = 'complete';
254
+ return;
255
+ }
256
+
257
+ // start the callback when all resources are loaded
258
+ var onResourceLoaded = null;
259
+ var gResourceCount = 0;
260
+ onResourceLoaded = function() {
261
+ gResourceCount++;
262
+ if (gResourceCount >= langCount) {
263
+ if (callback) // execute the [optional] callback
264
+ callback();
265
+ fireL10nReadyEvent(lang);
266
+ gReadyState = 'complete';
267
+ }
268
+ };
269
+
270
+ // load all resource files
271
+ function l10nResourceLink(link) {
272
+ var href = link.href;
273
+ var type = link.type;
274
+ this.load = function(lang, callback) {
275
+ var applied = lang;
276
+ parseResource(href, lang, callback, function() {
277
+ consoleWarn(href + ' not found.');
278
+ applied = '';
279
+ });
280
+ return applied; // return lang if found, an empty string if not found
281
+ };
282
+ }
283
+
284
+ for (var i = 0; i < langCount; i++) {
285
+ var resource = new l10nResourceLink(langLinks[i]);
286
+ var rv = resource.load(lang, onResourceLoaded);
287
+ if (rv != lang) { // lang not found, used default resource instead
288
+ consoleWarn('"' + lang + '" resource not found');
289
+ gLanguage = '';
290
+ }
291
+ }
292
+ }
293
+
294
+ // clear all l10n data
295
+ function clear() {
296
+ gL10nData = {};
297
+ gTextData = '';
298
+ gLanguage = '';
299
+ // TODO: clear all non predefined macros.
300
+ // There's no such macro /yet/ but we're planning to have some...
301
+ }
302
+
303
+
304
+ /**
305
+ * Get rules for plural forms (shared with JetPack), see:
306
+ * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
307
+ * https://github.com/mozilla/addon-sdk/blob/master/python-lib/plural-rules-generator.p
308
+ *
309
+ * @param {string} lang
310
+ * locale (language) used.
311
+ *
312
+ * @return {Function}
313
+ * returns a function that gives the plural form name for a given integer:
314
+ * var fun = getPluralRules('en');
315
+ * fun(1) -> 'one'
316
+ * fun(0) -> 'other'
317
+ * fun(1000) -> 'other'.
318
+ */
319
+
320
+ function getPluralRules(lang) {
321
+ var locales2rules = {
322
+ 'af': 3,
323
+ 'ak': 4,
324
+ 'am': 4,
325
+ 'ar': 1,
326
+ 'asa': 3,
327
+ 'az': 0,
328
+ 'be': 11,
329
+ 'bem': 3,
330
+ 'bez': 3,
331
+ 'bg': 3,
332
+ 'bh': 4,
333
+ 'bm': 0,
334
+ 'bn': 3,
335
+ 'bo': 0,
336
+ 'br': 20,
337
+ 'brx': 3,
338
+ 'bs': 11,
339
+ 'ca': 3,
340
+ 'cgg': 3,
341
+ 'chr': 3,
342
+ 'cs': 12,
343
+ 'cy': 17,
344
+ 'da': 3,
345
+ 'de': 3,
346
+ 'dv': 3,
347
+ 'dz': 0,
348
+ 'ee': 3,
349
+ 'el': 3,
350
+ 'en': 3,
351
+ 'eo': 3,
352
+ 'es': 3,
353
+ 'et': 3,
354
+ 'eu': 3,
355
+ 'fa': 0,
356
+ 'ff': 5,
357
+ 'fi': 3,
358
+ 'fil': 4,
359
+ 'fo': 3,
360
+ 'fr': 5,
361
+ 'fur': 3,
362
+ 'fy': 3,
363
+ 'ga': 8,
364
+ 'gd': 24,
365
+ 'gl': 3,
366
+ 'gsw': 3,
367
+ 'gu': 3,
368
+ 'guw': 4,
369
+ 'gv': 23,
370
+ 'ha': 3,
371
+ 'haw': 3,
372
+ 'he': 2,
373
+ 'hi': 4,
374
+ 'hr': 11,
375
+ 'hu': 0,
376
+ 'id': 0,
377
+ 'ig': 0,
378
+ 'ii': 0,
379
+ 'is': 3,
380
+ 'it': 3,
381
+ 'iu': 7,
382
+ 'ja': 0,
383
+ 'jmc': 3,
384
+ 'jv': 0,
385
+ 'ka': 0,
386
+ 'kab': 5,
387
+ 'kaj': 3,
388
+ 'kcg': 3,
389
+ 'kde': 0,
390
+ 'kea': 0,
391
+ 'kk': 3,
392
+ 'kl': 3,
393
+ 'km': 0,
394
+ 'kn': 0,
395
+ 'ko': 0,
396
+ 'ksb': 3,
397
+ 'ksh': 21,
398
+ 'ku': 3,
399
+ 'kw': 7,
400
+ 'lag': 18,
401
+ 'lb': 3,
402
+ 'lg': 3,
403
+ 'ln': 4,
404
+ 'lo': 0,
405
+ 'lt': 10,
406
+ 'lv': 6,
407
+ 'mas': 3,
408
+ 'mg': 4,
409
+ 'mk': 16,
410
+ 'ml': 3,
411
+ 'mn': 3,
412
+ 'mo': 9,
413
+ 'mr': 3,
414
+ 'ms': 0,
415
+ 'mt': 15,
416
+ 'my': 0,
417
+ 'nah': 3,
418
+ 'naq': 7,
419
+ 'nb': 3,
420
+ 'nd': 3,
421
+ 'ne': 3,
422
+ 'nl': 3,
423
+ 'nn': 3,
424
+ 'no': 3,
425
+ 'nr': 3,
426
+ 'nso': 4,
427
+ 'ny': 3,
428
+ 'nyn': 3,
429
+ 'om': 3,
430
+ 'or': 3,
431
+ 'pa': 3,
432
+ 'pap': 3,
433
+ 'pl': 13,
434
+ 'ps': 3,
435
+ 'pt': 3,
436
+ 'rm': 3,
437
+ 'ro': 9,
438
+ 'rof': 3,
439
+ 'ru': 11,
440
+ 'rwk': 3,
441
+ 'sah': 0,
442
+ 'saq': 3,
443
+ 'se': 7,
444
+ 'seh': 3,
445
+ 'ses': 0,
446
+ 'sg': 0,
447
+ 'sh': 11,
448
+ 'shi': 19,
449
+ 'sk': 12,
450
+ 'sl': 14,
451
+ 'sma': 7,
452
+ 'smi': 7,
453
+ 'smj': 7,
454
+ 'smn': 7,
455
+ 'sms': 7,
456
+ 'sn': 3,
457
+ 'so': 3,
458
+ 'sq': 3,
459
+ 'sr': 11,
460
+ 'ss': 3,
461
+ 'ssy': 3,
462
+ 'st': 3,
463
+ 'sv': 3,
464
+ 'sw': 3,
465
+ 'syr': 3,
466
+ 'ta': 3,
467
+ 'te': 3,
468
+ 'teo': 3,
469
+ 'th': 0,
470
+ 'ti': 4,
471
+ 'tig': 3,
472
+ 'tk': 3,
473
+ 'tl': 4,
474
+ 'tn': 3,
475
+ 'to': 0,
476
+ 'tr': 0,
477
+ 'ts': 3,
478
+ 'tzm': 22,
479
+ 'uk': 11,
480
+ 'ur': 3,
481
+ 've': 3,
482
+ 'vi': 0,
483
+ 'vun': 3,
484
+ 'wa': 4,
485
+ 'wae': 3,
486
+ 'wo': 0,
487
+ 'xh': 3,
488
+ 'xog': 3,
489
+ 'yo': 0,
490
+ 'zh': 0,
491
+ 'zu': 3
492
+ };
493
+
494
+ // utility functions for plural rules methods
495
+ function isIn(n, list) {
496
+ return list.indexOf(n) !== -1;
497
+ }
498
+ function isBetween(n, start, end) {
499
+ return start <= n && n <= end;
500
+ }
501
+
502
+ // list of all plural rules methods:
503
+ // map an integer to the plural form name to use
504
+ var pluralRules = {
505
+ '0': function(n) {
506
+ return 'other';
507
+ },
508
+ '1': function(n) {
509
+ if ((isBetween((n % 100), 3, 10)))
510
+ return 'few';
511
+ if (n === 0)
512
+ return 'zero';
513
+ if ((isBetween((n % 100), 11, 99)))
514
+ return 'many';
515
+ if (n == 2)
516
+ return 'two';
517
+ if (n == 1)
518
+ return 'one';
519
+ return 'other';
520
+ },
521
+ '2': function(n) {
522
+ if (n !== 0 && (n % 10) === 0)
523
+ return 'many';
524
+ if (n == 2)
525
+ return 'two';
526
+ if (n == 1)
527
+ return 'one';
528
+ return 'other';
529
+ },
530
+ '3': function(n) {
531
+ if (n == 1)
532
+ return 'one';
533
+ return 'other';
534
+ },
535
+ '4': function(n) {
536
+ if ((isBetween(n, 0, 1)))
537
+ return 'one';
538
+ return 'other';
539
+ },
540
+ '5': function(n) {
541
+ if ((isBetween(n, 0, 2)) && n != 2)
542
+ return 'one';
543
+ return 'other';
544
+ },
545
+ '6': function(n) {
546
+ if (n === 0)
547
+ return 'zero';
548
+ if ((n % 10) == 1 && (n % 100) != 11)
549
+ return 'one';
550
+ return 'other';
551
+ },
552
+ '7': function(n) {
553
+ if (n == 2)
554
+ return 'two';
555
+ if (n == 1)
556
+ return 'one';
557
+ return 'other';
558
+ },
559
+ '8': function(n) {
560
+ if ((isBetween(n, 3, 6)))
561
+ return 'few';
562
+ if ((isBetween(n, 7, 10)))
563
+ return 'many';
564
+ if (n == 2)
565
+ return 'two';
566
+ if (n == 1)
567
+ return 'one';
568
+ return 'other';
569
+ },
570
+ '9': function(n) {
571
+ if (n === 0 || n != 1 && (isBetween((n % 100), 1, 19)))
572
+ return 'few';
573
+ if (n == 1)
574
+ return 'one';
575
+ return 'other';
576
+ },
577
+ '10': function(n) {
578
+ if ((isBetween((n % 10), 2, 9)) && !(isBetween((n % 100), 11, 19)))
579
+ return 'few';
580
+ if ((n % 10) == 1 && !(isBetween((n % 100), 11, 19)))
581
+ return 'one';
582
+ return 'other';
583
+ },
584
+ '11': function(n) {
585
+ if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14)))
586
+ return 'few';
587
+ if ((n % 10) === 0 ||
588
+ (isBetween((n % 10), 5, 9)) ||
589
+ (isBetween((n % 100), 11, 14)))
590
+ return 'many';
591
+ if ((n % 10) == 1 && (n % 100) != 11)
592
+ return 'one';
593
+ return 'other';
594
+ },
595
+ '12': function(n) {
596
+ if ((isBetween(n, 2, 4)))
597
+ return 'few';
598
+ if (n == 1)
599
+ return 'one';
600
+ return 'other';
601
+ },
602
+ '13': function(n) {
603
+ if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14)))
604
+ return 'few';
605
+ if (n != 1 && (isBetween((n % 10), 0, 1)) ||
606
+ (isBetween((n % 10), 5, 9)) ||
607
+ (isBetween((n % 100), 12, 14)))
608
+ return 'many';
609
+ if (n == 1)
610
+ return 'one';
611
+ return 'other';
612
+ },
613
+ '14': function(n) {
614
+ if ((isBetween((n % 100), 3, 4)))
615
+ return 'few';
616
+ if ((n % 100) == 2)
617
+ return 'two';
618
+ if ((n % 100) == 1)
619
+ return 'one';
620
+ return 'other';
621
+ },
622
+ '15': function(n) {
623
+ if (n === 0 || (isBetween((n % 100), 2, 10)))
624
+ return 'few';
625
+ if ((isBetween((n % 100), 11, 19)))
626
+ return 'many';
627
+ if (n == 1)
628
+ return 'one';
629
+ return 'other';
630
+ },
631
+ '16': function(n) {
632
+ if ((n % 10) == 1 && n != 11)
633
+ return 'one';
634
+ return 'other';
635
+ },
636
+ '17': function(n) {
637
+ if (n == 3)
638
+ return 'few';
639
+ if (n === 0)
640
+ return 'zero';
641
+ if (n == 6)
642
+ return 'many';
643
+ if (n == 2)
644
+ return 'two';
645
+ if (n == 1)
646
+ return 'one';
647
+ return 'other';
648
+ },
649
+ '18': function(n) {
650
+ if (n === 0)
651
+ return 'zero';
652
+ if ((isBetween(n, 0, 2)) && n !== 0 && n != 2)
653
+ return 'one';
654
+ return 'other';
655
+ },
656
+ '19': function(n) {
657
+ if ((isBetween(n, 2, 10)))
658
+ return 'few';
659
+ if ((isBetween(n, 0, 1)))
660
+ return 'one';
661
+ return 'other';
662
+ },
663
+ '20': function(n) {
664
+ if ((isBetween((n % 10), 3, 4) || ((n % 10) == 9)) && !(
665
+ isBetween((n % 100), 10, 19) ||
666
+ isBetween((n % 100), 70, 79) ||
667
+ isBetween((n % 100), 90, 99)
668
+ ))
669
+ return 'few';
670
+ if ((n % 1000000) === 0 && n !== 0)
671
+ return 'many';
672
+ if ((n % 10) == 2 && !isIn((n % 100), [12, 72, 92]))
673
+ return 'two';
674
+ if ((n % 10) == 1 && !isIn((n % 100), [11, 71, 91]))
675
+ return 'one';
676
+ return 'other';
677
+ },
678
+ '21': function(n) {
679
+ if (n === 0)
680
+ return 'zero';
681
+ if (n == 1)
682
+ return 'one';
683
+ return 'other';
684
+ },
685
+ '22': function(n) {
686
+ if ((isBetween(n, 0, 1)) || (isBetween(n, 11, 99)))
687
+ return 'one';
688
+ return 'other';
689
+ },
690
+ '23': function(n) {
691
+ if ((isBetween((n % 10), 1, 2)) || (n % 20) === 0)
692
+ return 'one';
693
+ return 'other';
694
+ },
695
+ '24': function(n) {
696
+ if ((isBetween(n, 3, 10) || isBetween(n, 13, 19)))
697
+ return 'few';
698
+ if (isIn(n, [2, 12]))
699
+ return 'two';
700
+ if (isIn(n, [1, 11]))
701
+ return 'one';
702
+ return 'other';
703
+ }
704
+ };
705
+
706
+ // return a function that gives the plural form name for a given integer
707
+ var index = locales2rules[lang.replace(/-.*$/, '')];
708
+ if (!(index in pluralRules)) {
709
+ consoleWarn('plural form unknown for [' + lang + ']');
710
+ return function() { return 'other'; };
711
+ }
712
+ return pluralRules[index];
713
+ }
714
+
715
+ // pre-defined 'plural' macro
716
+ gMacros.plural = function(str, param, key, prop) {
717
+ var n = parseFloat(param);
718
+ if (isNaN(n))
719
+ return str;
720
+
721
+ // TODO: support other properties (l20n still doesn't...)
722
+ if (prop != gTextProp)
723
+ return str;
724
+
725
+ // initialize _pluralRules
726
+ if (!gMacros._pluralRules)
727
+ gMacros._pluralRules = getPluralRules(gLanguage);
728
+ var index = '[' + gMacros._pluralRules(n) + ']';
729
+
730
+ // try to find a [zero|one|two] key if it's defined
731
+ if (n === 0 && (key + '[zero]') in gL10nData) {
732
+ str = gL10nData[key + '[zero]'][prop];
733
+ } else if (n == 1 && (key + '[one]') in gL10nData) {
734
+ str = gL10nData[key + '[one]'][prop];
735
+ } else if (n == 2 && (key + '[two]') in gL10nData) {
736
+ str = gL10nData[key + '[two]'][prop];
737
+ } else if ((key + index) in gL10nData) {
738
+ str = gL10nData[key + index][prop];
739
+ }
740
+
741
+ return str;
742
+ };
743
+
744
+
745
+ /**
746
+ * l10n dictionary functions
747
+ */
748
+
749
+ // fetch an l10n object, warn if not found, apply `args' if possible
750
+ function getL10nData(key, args, fallback) {
751
+ var data = gL10nData[key];
752
+ if (!data) {
753
+ consoleWarn('#' + key + ' missing for [' + gLanguage + ']');
754
+ if (!fallback) {
755
+ return null;
756
+ }
757
+ data = fallback;
758
+ }
759
+
760
+ /** This is where l10n expressions should be processed.
761
+ * The plan is to support C-style expressions from the l20n project;
762
+ * until then, only two kinds of simple expressions are supported:
763
+ * {[ index ]} and {{ arguments }}.
764
+ */
765
+ var rv = {};
766
+ for (var prop in data) {
767
+ var str = data[prop];
768
+ str = substIndexes(str, args, key, prop);
769
+ str = substArguments(str, args);
770
+ rv[prop] = str;
771
+ }
772
+ return rv;
773
+ }
774
+
775
+ // replace {[macros]} with their values
776
+ function substIndexes(str, args, key, prop) {
777
+ var reIndex = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)\s*\]\}/;
778
+ var reMatch = reIndex.exec(str);
779
+ if (!reMatch || !reMatch.length)
780
+ return str;
781
+
782
+ // an index/macro has been found
783
+ // Note: at the moment, only one parameter is supported
784
+ var macroName = reMatch[1];
785
+ var paramName = reMatch[2];
786
+ var param;
787
+ if (args && paramName in args) {
788
+ param = args[paramName];
789
+ } else if (paramName in gL10nData) {
790
+ param = gL10nData[paramName];
791
+ }
792
+
793
+ // there's no macro parser yet: it has to be defined in gMacros
794
+ if (macroName in gMacros) {
795
+ var macro = gMacros[macroName];
796
+ str = macro(str, param, key, prop);
797
+ }
798
+ return str;
799
+ }
800
+
801
+ // replace {{arguments}} with their values
802
+ function substArguments(str, args) {
803
+ var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/;
804
+ var match = reArgs.exec(str);
805
+ while (match) {
806
+ if (!match || match.length < 2)
807
+ return str; // argument key not found
808
+
809
+ var arg = match[1];
810
+ var sub = '';
811
+ if (arg in args) {
812
+ sub = args[arg];
813
+ } else if (arg in gL10nData) {
814
+ sub = gL10nData[arg][gTextProp];
815
+ } else {
816
+ consoleWarn('could not find argument {{' + arg + '}}');
817
+ return str;
818
+ }
819
+
820
+ str = str.substring(0, match.index) + sub +
821
+ str.substr(match.index + match[0].length);
822
+ match = reArgs.exec(str);
823
+ }
824
+ return str;
825
+ }
826
+
827
+ // translate an HTML element
828
+ function translateElement(element) {
829
+ var l10n = getL10nAttributes(element);
830
+ if (!l10n.id)
831
+ return;
832
+
833
+ // get the related l10n object
834
+ var data = getL10nData(l10n.id, l10n.args);
835
+ if (!data) {
836
+ consoleWarn('#' + l10n.id + ' missing for [' + gLanguage + ']');
837
+ return;
838
+ }
839
+
840
+ // translate element (TODO: security checks?)
841
+ // for the node content, replace the content of the first child textNode
842
+ // and clear other child textNodes
843
+ if (data[gTextProp]) { // XXX
844
+ if (element.children.length === 0) {
845
+ element[gTextProp] = data[gTextProp];
846
+ } else {
847
+ var children = element.childNodes,
848
+ found = false;
849
+ for (var i = 0, l = children.length; i < l; i++) {
850
+ if (children[i].nodeType === 3 &&
851
+ /\S/.test(children[i].textContent)) { // XXX
852
+ // using nodeValue seems cross-browser
853
+ if (found) {
854
+ children[i].nodeValue = '';
855
+ } else {
856
+ children[i].nodeValue = data[gTextProp];
857
+ found = true;
858
+ }
859
+ }
860
+ }
861
+ if (!found) {
862
+ consoleWarn('unexpected error, could not translate element content');
863
+ }
864
+ }
865
+ delete data[gTextProp];
866
+ }
867
+
868
+ for (var k in data) {
869
+ element[k] = data[k];
870
+ }
871
+ }
872
+
873
+ // translate an HTML subtree
874
+ function translateFragment(element) {
875
+ element = element || document.documentElement;
876
+
877
+ // check all translatable children (= w/ a `data-l10n-id' attribute)
878
+ var children = getTranslatableChildren(element);
879
+ var elementCount = children.length;
880
+ for (var i = 0; i < elementCount; i++) {
881
+ translateElement(children[i]);
882
+ }
883
+
884
+ // translate element itself if necessary
885
+ translateElement(element);
886
+ }
887
+
888
+ // cross-browser API (sorry, oldIE doesn't support getters & setters)
889
+ return {
890
+ // get a localized string
891
+ get: function(key, args, fallback) {
892
+ var data = getL10nData(key, args, {textContent: fallback});
893
+ if (data) { // XXX double-check this
894
+ return 'textContent' in data ? data.textContent : '';
895
+ }
896
+ return '{{' + key + '}}';
897
+ },
898
+
899
+ // debug
900
+ getData: function() { return gL10nData; },
901
+ getText: function() { return gTextData; },
902
+
903
+ // get|set the document language
904
+ getLanguage: function() { return gLanguage; },
905
+ setLanguage: function(lang) { loadLocale(lang, translateFragment); },
906
+
907
+ // get the direction (ltr|rtl) of the current language
908
+ getDirection: function() {
909
+ // http://www.w3.org/International/questions/qa-scripts
910
+ // Arabic, Hebrew, Farsi, Pashto, Urdu
911
+ var rtlList = ['ar', 'he', 'fa', 'ps', 'ur'];
912
+ return (rtlList.indexOf(gLanguage) >= 0) ? 'rtl' : 'ltr';
913
+ },
914
+
915
+ // translate an element or document fragment
916
+ translate: translateFragment,
917
+
918
+ // this can be used to prevent race conditions
919
+ getReadyState: function() { return gReadyState; }
920
+ };
921
+
922
+ }) (window, document);