kalei 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (211) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +34 -0
  3. data/Rakefile +39 -0
  4. data/lib/kalei.rb +3 -0
  5. data/lib/kalei/app.rb +9 -0
  6. data/lib/kalei/engine.rb +7 -0
  7. data/lib/kalei/public/css/buttons.css +251 -0
  8. data/lib/kalei/public/css/demo-styleDoccos.css +360 -0
  9. data/lib/kalei/public/css/fixie-demo.css +39 -0
  10. data/lib/kalei/public/css/highlight/LICENSE +24 -0
  11. data/lib/kalei/public/css/highlight/README.md +120 -0
  12. data/lib/kalei/public/css/highlight/README.ru.md +125 -0
  13. data/lib/kalei/public/css/highlight/classref.txt +544 -0
  14. data/lib/kalei/public/css/highlight/default.min.css +1 -0
  15. data/lib/kalei/public/css/highlight/highlight.pack.js +1 -0
  16. data/lib/kalei/public/css/highlight/styles/arta.css +153 -0
  17. data/lib/kalei/public/css/highlight/styles/ascetic.css +49 -0
  18. data/lib/kalei/public/css/highlight/styles/brown_paper.css +104 -0
  19. data/lib/kalei/public/css/highlight/styles/brown_papersq.png +0 -0
  20. data/lib/kalei/public/css/highlight/styles/dark.css +103 -0
  21. data/lib/kalei/public/css/highlight/styles/default.css +133 -0
  22. data/lib/kalei/public/css/highlight/styles/far.css +110 -0
  23. data/lib/kalei/public/css/highlight/styles/github.css +133 -0
  24. data/lib/kalei/public/css/highlight/styles/googlecode.css +143 -0
  25. data/lib/kalei/public/css/highlight/styles/idea.css +122 -0
  26. data/lib/kalei/public/css/highlight/styles/ir_black.css +99 -0
  27. data/lib/kalei/public/css/highlight/styles/magula.css +120 -0
  28. data/lib/kalei/public/css/highlight/styles/monokai.css +116 -0
  29. data/lib/kalei/public/css/highlight/styles/pojoaque.css +106 -0
  30. data/lib/kalei/public/css/highlight/styles/pojoaque.jpg +0 -0
  31. data/lib/kalei/public/css/highlight/styles/school_book.css +111 -0
  32. data/lib/kalei/public/css/highlight/styles/school_book.png +0 -0
  33. data/lib/kalei/public/css/highlight/styles/solarized_dark.css +90 -0
  34. data/lib/kalei/public/css/highlight/styles/solarized_light.css +90 -0
  35. data/lib/kalei/public/css/highlight/styles/sunburst.css +149 -0
  36. data/lib/kalei/public/css/highlight/styles/vs.css +85 -0
  37. data/lib/kalei/public/css/highlight/styles/xcode.css +153 -0
  38. data/lib/kalei/public/css/highlight/styles/zenburn.css +115 -0
  39. data/lib/kalei/public/css/styles.css +95 -0
  40. data/lib/kalei/public/css/theme.css +259 -0
  41. data/lib/kalei/public/css/theme2.css +51 -0
  42. data/lib/kalei/public/js/boilerplate.js +9 -0
  43. data/lib/kalei/public/js/config.js +8 -0
  44. data/lib/kalei/public/js/events.js +8 -0
  45. data/lib/kalei/public/js/libs/backbone/backbone-min.js +37 -0
  46. data/lib/kalei/public/js/libs/backbone/backbone-optamd3-min.js +37 -0
  47. data/lib/kalei/public/js/libs/cssbeautify/cssbeautify.js +198 -0
  48. data/lib/kalei/public/js/libs/fixie/fixie.js +1 -0
  49. data/lib/kalei/public/js/libs/highlight/highlight.js +1 -0
  50. data/lib/kalei/public/js/libs/highlight/highlight.pack.js +1 -0
  51. data/lib/kalei/public/js/libs/highlighter/shBrushCss.js +90 -0
  52. data/lib/kalei/public/js/libs/highlighter/shCore.js +2371 -0
  53. data/lib/kalei/public/js/libs/jquery/jquery-min.js +4 -0
  54. data/lib/kalei/public/js/libs/jscssp/jscssp.js +5431 -0
  55. data/lib/kalei/public/js/libs/marked/marked.js +793 -0
  56. data/lib/kalei/public/js/libs/pagedown/converter.js +1335 -0
  57. data/lib/kalei/public/js/libs/pagedown/pagedown +1332 -0
  58. data/lib/kalei/public/js/libs/pagedown/sanitizer.js +108 -0
  59. data/lib/kalei/public/js/libs/parseuri/parseuri.js +32 -0
  60. data/lib/kalei/public/js/libs/require/order.js +1 -0
  61. data/lib/kalei/public/js/libs/require/require.js +31 -0
  62. data/lib/kalei/public/js/libs/require/text.js +11 -0
  63. data/lib/kalei/public/js/libs/showdown/showdown.js +1344 -0
  64. data/lib/kalei/public/js/libs/underscore/underscore-min.js +31 -0
  65. data/lib/kalei/public/js/main.js +39 -0
  66. data/lib/kalei/public/js/router.js +39 -0
  67. data/lib/kalei/public/js/views/app.js +24 -0
  68. data/lib/kalei/public/js/views/style/menu.js +77 -0
  69. data/lib/kalei/public/js/views/style/page.js +100 -0
  70. data/lib/kalei/public/js/vm.js +33 -0
  71. data/lib/kalei/public/templates/layout.html +4 -0
  72. data/lib/kalei/public/templates/style/menu.html +22 -0
  73. data/lib/kalei/public/templates/style/page.html +15 -0
  74. data/lib/kalei/views/index.erb +18 -0
  75. data/test/dummy/README.rdoc +261 -0
  76. data/test/dummy/Rakefile +7 -0
  77. data/test/dummy/app/assets/javascripts/application.js +15 -0
  78. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  79. data/test/dummy/app/assets/stylesheets/example.css +251 -0
  80. data/test/dummy/app/controllers/application_controller.rb +3 -0
  81. data/test/dummy/app/helpers/application_helper.rb +2 -0
  82. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  83. data/test/dummy/config.ru +4 -0
  84. data/test/dummy/config/application.rb +65 -0
  85. data/test/dummy/config/boot.rb +10 -0
  86. data/test/dummy/config/environment.rb +5 -0
  87. data/test/dummy/config/environments/development.rb +31 -0
  88. data/test/dummy/config/environments/production.rb +64 -0
  89. data/test/dummy/config/environments/test.rb +35 -0
  90. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  91. data/test/dummy/config/initializers/inflections.rb +15 -0
  92. data/test/dummy/config/initializers/mime_types.rb +5 -0
  93. data/test/dummy/config/initializers/secret_token.rb +7 -0
  94. data/test/dummy/config/initializers/session_store.rb +8 -0
  95. data/test/dummy/config/initializers/wrap_parameters.rb +10 -0
  96. data/test/dummy/config/locales/en.yml +5 -0
  97. data/test/dummy/config/routes.rb +4 -0
  98. data/test/dummy/log/development (MacBook Air's conflicted copy 2012-11-07).log +12900 -0
  99. data/test/dummy/log/development (Work iMac's conflicted copy 2012-11-06).log +12539 -0
  100. data/test/dummy/log/development.log +16426 -0
  101. data/test/dummy/public/404.html +26 -0
  102. data/test/dummy/public/422.html +26 -0
  103. data/test/dummy/public/500.html +25 -0
  104. data/test/dummy/public/favicon.ico +0 -0
  105. data/test/dummy/script/rails +6 -0
  106. data/test/dummy/tmp/cache/assets/BBF/D60/sprockets%2Fc81500801e644b9c5335793957200428 +0 -0
  107. data/test/dummy/tmp/cache/assets/BE2/9B0/sprockets%2F4097e8869406a33602004190d03272ad +0 -0
  108. data/test/dummy/tmp/cache/assets/C59/B10/sprockets%2Fc6a54e0f5352e41a92c1451976584894 +0 -0
  109. data/test/dummy/tmp/cache/assets/C5F/F60/sprockets%2F661d2795e99d0034ce6873925e0256c4 +0 -0
  110. data/test/dummy/tmp/cache/assets/C64/8C0/sprockets%2F8996c378630075350fb258019546feef +0 -0
  111. data/test/dummy/tmp/cache/assets/C94/F80/sprockets%2F5169b582f529b8d582a59c77c247405f +0 -0
  112. data/test/dummy/tmp/cache/assets/CA2/420/sprockets%2Fd6a388375c983c17d367a676398f8c19 +0 -0
  113. data/test/dummy/tmp/cache/assets/CB0/0B0/sprockets%2F79332476368821ab45ad05e0ae798ad1 +0 -0
  114. data/test/dummy/tmp/cache/assets/CB1/C70/sprockets%2F7022bb23d88717cc1625b47988f0af61 +0 -0
  115. data/test/dummy/tmp/cache/assets/CB5/DB0/sprockets%2F3c20130f1945e66bbc9726cb8925f647 +0 -0
  116. data/test/dummy/tmp/cache/assets/CB6/DF0/sprockets%2F3d4ce8bf5883f171628c030851a5a669 +0 -0
  117. data/test/dummy/tmp/cache/assets/CC1/B70/sprockets%2F2451c355722fdc5979cd365f9f9705a3 +0 -0
  118. data/test/dummy/tmp/cache/assets/CC2/550/sprockets%2F07be9de6d83634f4e056ba2982578725 +0 -0
  119. data/test/dummy/tmp/cache/assets/CC5/8D0/sprockets%2F31f92d93c1650884a626e941c949bff8 +0 -0
  120. data/test/dummy/tmp/cache/assets/CC5/D90/sprockets%2F1b9f99d72878209259dce90e419a04d5 +0 -0
  121. data/test/dummy/tmp/cache/assets/CC8/780/sprockets%2F8578d679f76df60299b502a4c762a62f +0 -0
  122. data/test/dummy/tmp/cache/assets/CC8/C10/sprockets%2F69e29270f13a4c8cd925ac79608878d6 +0 -0
  123. data/test/dummy/tmp/cache/assets/CD1/700/sprockets%2F4a8e02ffcb8924669105305f0b107c1b +0 -0
  124. data/test/dummy/tmp/cache/assets/CD5/DC0/sprockets%2F92fa0519b445f0f1027d42bd9a4165e4 +0 -0
  125. data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  126. data/test/dummy/tmp/cache/assets/CDB/DC0/sprockets%2F58518572a25128ada56ed9156aac53c4 +0 -0
  127. data/test/dummy/tmp/cache/assets/CDE/C90/sprockets%2F2b20d9775c811fdb6a466e86711d15c3 +0 -0
  128. data/test/dummy/tmp/cache/assets/CE3/2A0/sprockets%2F334b4fc16f7e765755c1be56c9010e28 +0 -0
  129. data/test/dummy/tmp/cache/assets/CE5/FF0/sprockets%2F6113d3e443309efb13b899b3a94b578e +0 -0
  130. data/test/dummy/tmp/cache/assets/CF2/7D0/sprockets%2F7c50d8877e9a4ee1553dc6626c373d38 +0 -0
  131. data/test/dummy/tmp/cache/assets/CF3/C60/sprockets%2Fdfc0395a5019a0a24b34c170a36424de +0 -0
  132. data/test/dummy/tmp/cache/assets/CFC/480/sprockets%2F9d5ab28532957b83f7f9fcbd78021699 +0 -0
  133. data/test/dummy/tmp/cache/assets/CFF/C60/sprockets%2F4ed7a36bde489e10f97698475493ae57 +0 -0
  134. data/test/dummy/tmp/cache/assets/D00/B00/sprockets%2Fd670c548ea2643bd95a65504c11cc13b +0 -0
  135. data/test/dummy/tmp/cache/assets/D0A/CB0/sprockets%2Fed8ba102e7d5015265f1e51a89b8d355 +0 -0
  136. data/test/dummy/tmp/cache/assets/D10/2B0/sprockets%2F1941a534f48d6fa7b22e23b4ca397b69 +0 -0
  137. data/test/dummy/tmp/cache/assets/D17/2A0/sprockets%2Fc094a0ad0b3abb1b92850a86f2000b61 +0 -0
  138. data/test/dummy/tmp/cache/assets/D17/920/sprockets%2Fa6ea2beb942f969acce6231944871652 +0 -0
  139. data/test/dummy/tmp/cache/assets/D19/7C0/sprockets%2F6bd121745f94b781ed36dd0ffa473856 +0 -0
  140. data/test/dummy/tmp/cache/assets/D1A/6F0/sprockets%2F41dd836dc3f691e237833bc1b6f76d93 +0 -0
  141. data/test/dummy/tmp/cache/assets/D1A/770/sprockets%2Fcfad959e3f5eb529a1310d85483b9419 +0 -0
  142. data/test/dummy/tmp/cache/assets/D1F/000/sprockets%2Fdecbf7d6343c0d9638d301c9982298c6 +0 -0
  143. data/test/dummy/tmp/cache/assets/D1F/4D0/sprockets%2F860075c377f26472f83ef56a7bb89aad +0 -0
  144. data/test/dummy/tmp/cache/assets/D20/7D0/sprockets%2F1f7e984b4c793c57d666af84ab414d53 +0 -0
  145. data/test/dummy/tmp/cache/assets/D23/D20/sprockets%2F746c5fb7c744dd87e61f34c8644183fb +0 -0
  146. data/test/dummy/tmp/cache/assets/D29/080/sprockets%2F6b81d746679c6151bc66da896aa97de9 +0 -0
  147. data/test/dummy/tmp/cache/assets/D29/890/sprockets%2Fb5d9c167884139e0f6b8d53816de6e9d +0 -0
  148. data/test/dummy/tmp/cache/assets/D2B/2D0/sprockets%2Fbaf7beb2c3603cfa100b276923a86543 +0 -0
  149. data/test/dummy/tmp/cache/assets/D2B/3F0/sprockets%2Fd6c1fabe78df17a601309081130ca90d +0 -0
  150. data/test/dummy/tmp/cache/assets/D2E/700/sprockets%2F9d6beb99735c03f375e19c5857ccf955 +0 -0
  151. data/test/dummy/tmp/cache/assets/D31/DF0/sprockets%2Fb9ca3ac15e18a4d032f61278565b7bd0 +0 -0
  152. data/test/dummy/tmp/cache/assets/D32/980/sprockets%2F65a361c15a5ff036ce119cb1d2296e4c +0 -0
  153. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  154. data/test/dummy/tmp/cache/assets/D32/CA0/sprockets%2F05becf082d0b555622d944ac6e7202cf +0 -0
  155. data/test/dummy/tmp/cache/assets/D36/570/sprockets%2Fadfccb7c2436e006496eae16d6901523 +0 -0
  156. data/test/dummy/tmp/cache/assets/D3F/080/sprockets%2Fb5d1e36ac49d4c9e11728239ecf104e6 +0 -0
  157. data/test/dummy/tmp/cache/assets/D3F/630/sprockets%2F9093e471e526d13234feda4a9cc80fc5 +0 -0
  158. data/test/dummy/tmp/cache/assets/D41/3D0/sprockets%2Fd570b46455f776a30cbb729b442fbbf8 +0 -0
  159. data/test/dummy/tmp/cache/assets/D43/500/sprockets%2F91261e8d0df721df2c19e7c8cc6872b0 +0 -0
  160. data/test/dummy/tmp/cache/assets/D44/2F0/sprockets%2F8abebce08d92187dd84464b40264fc73 +0 -0
  161. data/test/dummy/tmp/cache/assets/D46/350/sprockets%2Fd8bcb0686002ee32c4bf49887f09be36 +0 -0
  162. data/test/dummy/tmp/cache/assets/D4A/F50/sprockets%2F99df6af7ff98150e1255d6d9110b80ea +0 -0
  163. data/test/dummy/tmp/cache/assets/D50/480/sprockets%2F6e151e3dec739c7a338a877ef65fc174 +0 -0
  164. data/test/dummy/tmp/cache/assets/D56/AE0/sprockets%2F68f98777bb22b8e8a78097ba0c86be0f +0 -0
  165. data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  166. data/test/dummy/tmp/cache/assets/D5B/E00/sprockets%2Ffea8965ad9d35e43675fc5b948e0e934 +0 -0
  167. data/test/dummy/tmp/cache/assets/D68/860/sprockets%2Fafacb887390255dae680441eaf00ff52 +0 -0
  168. data/test/dummy/tmp/cache/assets/D6A/BD0/sprockets%2Fada1b7fa3ff025849aec28559a500c76 +0 -0
  169. data/test/dummy/tmp/cache/assets/D6D/F20/sprockets%2F3c1e635c4ba90a4512849be74fcfee07 +0 -0
  170. data/test/dummy/tmp/cache/assets/D6E/540/sprockets%2F4ea3314d23fc25fc9beec6262272f7f5 +0 -0
  171. data/test/dummy/tmp/cache/assets/D73/690/sprockets%2F356bd79cadb47fd8d6836c17d21318ab +0 -0
  172. data/test/dummy/tmp/cache/assets/D73/7D0/sprockets%2Ff89249a45e22e86c35f227bd340adedd +0 -0
  173. data/test/dummy/tmp/cache/assets/D75/000/sprockets%2Fdfae73207596c9fcc854a2aa1e4f7661 +0 -0
  174. data/test/dummy/tmp/cache/assets/D7A/340/sprockets%2Fc880ec2dd9f7c964cf13a12b9674c63d +0 -0
  175. data/test/dummy/tmp/cache/assets/D7B/6F0/sprockets%2Fc16bfd961e80bb761c67cd760179df8c +0 -0
  176. data/test/dummy/tmp/cache/assets/D7F/D20/sprockets%2Fe4293aa867945ef5c6eb5b26e69c6aa5 +0 -0
  177. data/test/dummy/tmp/cache/assets/D81/180/sprockets%2Fd666d9ef622f0e8cb708f5d8a6b2077f +0 -0
  178. data/test/dummy/tmp/cache/assets/D81/240/sprockets%2F1d0f3cd232c07902529b25abd0bd4ccd +0 -0
  179. data/test/dummy/tmp/cache/assets/D83/560/sprockets%2F9d807a49bc60df8ffb65e99292f3c0a6 +0 -0
  180. data/test/dummy/tmp/cache/assets/D85/D50/sprockets%2Feb82054976bcf4d46c573eafed819f58 +0 -0
  181. data/test/dummy/tmp/cache/assets/D8F/870/sprockets%2F696f6648b2fcbceb8e2686875e6cd6b6 +0 -0
  182. data/test/dummy/tmp/cache/assets/D94/9D0/sprockets%2Ff1c6747b2a961ab3ef92a5a1abe635c4 +0 -0
  183. data/test/dummy/tmp/cache/assets/D96/E00/sprockets%2F7f345bc6ada58cac0203458fc076f1ed +0 -0
  184. data/test/dummy/tmp/cache/assets/D98/DE0/sprockets%2Fbeac47000d16b66f5757aa626f5e8bda +0 -0
  185. data/test/dummy/tmp/cache/assets/D9D/BD0/sprockets%2F1f338b9df8d5a3ffe0b85591db253ab0 +0 -0
  186. data/test/dummy/tmp/cache/assets/DA0/0A0/sprockets%2F61e5c9d0b209a8ce5c8a9aa719ad564d +0 -0
  187. data/test/dummy/tmp/cache/assets/DB1/A10/sprockets%2Fde67190aff966baf9a57f6641fda292e +0 -0
  188. data/test/dummy/tmp/cache/assets/DBE/950/sprockets%2Fa3fbb592cacd368796899f89b95cde1d +0 -0
  189. data/test/dummy/tmp/cache/assets/DC3/950/sprockets%2F12be1e5b8e749be4b42e4ccbb24e1a84 +0 -0
  190. data/test/dummy/tmp/cache/assets/DC5/E10/sprockets%2Fd10332dee1b2ee73913f7f01e6bffc2f +0 -0
  191. data/test/dummy/tmp/cache/assets/DC7/FF0/sprockets%2F4d3d0db28baaafb745e1b4c5f564d891 +0 -0
  192. data/test/dummy/tmp/cache/assets/DCC/810/sprockets%2F8d18398a60db7e9e61ae42bea18b2aeb +0 -0
  193. data/test/dummy/tmp/cache/assets/DE5/A10/sprockets%2F1aa8db45a4cf7ab09d460c22ed063adb +0 -0
  194. data/test/dummy/tmp/cache/assets/DEB/9E0/sprockets%2F1afb4b1d001bc3c1b7cf8cc548bd73e7 +0 -0
  195. data/test/dummy/tmp/cache/assets/DF3/AB0/sprockets%2Fa2fbea7b8e275e2e4b610cd1d8fed006 +0 -0
  196. data/test/dummy/tmp/cache/assets/DF4/A90/sprockets%2Fc6d9482adadab4569aacdf12a224af49 +0 -0
  197. data/test/dummy/tmp/cache/assets/DF5/520/sprockets%2F0cb7052285cefb59b10efc3ed1c6f5cd +0 -0
  198. data/test/dummy/tmp/cache/assets/DF8/000/sprockets%2Fb2bc6fbabfb2c270896a8a44da66fa47 +0 -0
  199. data/test/dummy/tmp/cache/assets/DFB/2D0/sprockets%2F0e88b08c47faeae40d7682dc28b1fbad +0 -0
  200. data/test/dummy/tmp/cache/assets/DFB/9A0/sprockets%2Fb9a29f57d1ebf045452ed6cbd62eae1d +0 -0
  201. data/test/dummy/tmp/cache/assets/E02/D20/sprockets%2Fbd7f5fd245b9c9fc534cad60ef038d4a +0 -0
  202. data/test/dummy/tmp/cache/assets/E35/0A0/sprockets%2F9e0f5c61a97a72fc952a7dbbcefb7cb6 +0 -0
  203. data/test/dummy/tmp/cache/assets/E38/BA0/sprockets%2Fbee7ee5afe77332bcc53bc4e29c77a7f +0 -0
  204. data/test/dummy/tmp/cache/assets/E55/740/sprockets%2Fd51ee7ad0adc4b323ce1a9b88ed4a9af +0 -0
  205. data/test/dummy/tmp/cache/assets/E5C/410/sprockets%2F1bec976eb7fc54612519dcaccae6ccbe +0 -0
  206. data/test/dummy/tmp/cache/assets/EC5/D20/sprockets%2Fc1cf4676f1d3ab9dc9fcbaae59fbe9da +0 -0
  207. data/test/functional/kalei/index_controller_test.rb +11 -0
  208. data/test/integration/navigation_test.rb +9 -0
  209. data/test/kalei_test.rb +7 -0
  210. data/test/test_helper.rb +15 -0
  211. metadata +414 -0
@@ -0,0 +1,2371 @@
1
+ //
2
+ // Begin anonymous function. This is used to contain local scope variables without polutting global scope.
3
+ //
4
+ // XRegExp 1.5.1
5
+ // (c) 2007-2012 Steven Levithan
6
+ // MIT License
7
+ // <http://xregexp.com>
8
+ // Provides an augmented, extensible, cross-browser implementation of regular expressions,
9
+ // including support for additional syntax, flags, and methods
10
+
11
+ var XRegExp;
12
+
13
+ if (XRegExp) {
14
+ // Avoid running twice, since that would break references to native globals
15
+ throw Error("can't load XRegExp twice in the same frame");
16
+ }
17
+
18
+ // Run within an anonymous function to protect variables and avoid new globals
19
+ (function (undefined) {
20
+
21
+ //---------------------------------
22
+ // Constructor
23
+ //---------------------------------
24
+
25
+ // Accepts a pattern and flags; returns a new, extended `RegExp` object. Differs from a native
26
+ // regular expression in that additional syntax and flags are supported and cross-browser
27
+ // syntax inconsistencies are ameliorated. `XRegExp(/regex/)` clones an existing regex and
28
+ // converts to type XRegExp
29
+ XRegExp = function (pattern, flags) {
30
+ var output = [],
31
+ currScope = XRegExp.OUTSIDE_CLASS,
32
+ pos = 0,
33
+ context, tokenResult, match, chr, regex;
34
+
35
+ if (XRegExp.isRegExp(pattern)) {
36
+ if (flags !== undefined)
37
+ throw TypeError("can't supply flags when constructing one RegExp from another");
38
+ return clone(pattern);
39
+ }
40
+ // Tokens become part of the regex construction process, so protect against infinite
41
+ // recursion when an XRegExp is constructed within a token handler or trigger
42
+ if (isInsideConstructor)
43
+ throw Error("can't call the XRegExp constructor within token definition functions");
44
+
45
+ flags = flags || "";
46
+ context = { // `this` object for custom tokens
47
+ hasNamedCapture: false,
48
+ captureNames: [],
49
+ hasFlag: function (flag) {return flags.indexOf(flag) > -1;},
50
+ setFlag: function (flag) {flags += flag;}
51
+ };
52
+
53
+ while (pos < pattern.length) {
54
+ // Check for custom tokens at the current position
55
+ tokenResult = runTokens(pattern, pos, currScope, context);
56
+
57
+ if (tokenResult) {
58
+ output.push(tokenResult.output);
59
+ pos += (tokenResult.match[0].length || 1);
60
+ } else {
61
+ // Check for native multicharacter metasequences (excluding character classes) at
62
+ // the current position
63
+ if (match = nativ.exec.call(nativeTokens[currScope], pattern.slice(pos))) {
64
+ output.push(match[0]);
65
+ pos += match[0].length;
66
+ } else {
67
+ chr = pattern.charAt(pos);
68
+ if (chr === "[")
69
+ currScope = XRegExp.INSIDE_CLASS;
70
+ else if (chr === "]")
71
+ currScope = XRegExp.OUTSIDE_CLASS;
72
+ // Advance position one character
73
+ output.push(chr);
74
+ pos++;
75
+ }
76
+ }
77
+ }
78
+
79
+ regex = RegExp(output.join(""), nativ.replace.call(flags, flagClip, ""));
80
+ regex._xregexp = {
81
+ source: pattern,
82
+ captureNames: context.hasNamedCapture ? context.captureNames : null
83
+ };
84
+ return regex;
85
+ };
86
+
87
+
88
+ //---------------------------------
89
+ // Public properties
90
+ //---------------------------------
91
+
92
+ XRegExp.version = "1.5.1";
93
+
94
+ // Token scope bitflags
95
+ XRegExp.INSIDE_CLASS = 1;
96
+ XRegExp.OUTSIDE_CLASS = 2;
97
+
98
+
99
+ //---------------------------------
100
+ // Private variables
101
+ //---------------------------------
102
+
103
+ var replacementToken = /\$(?:(\d\d?|[$&`'])|{([$\w]+)})/g,
104
+ flagClip = /[^gimy]+|([\s\S])(?=[\s\S]*\1)/g, // Nonnative and duplicate flags
105
+ quantifier = /^(?:[?*+]|{\d+(?:,\d*)?})\??/,
106
+ isInsideConstructor = false,
107
+ tokens = [],
108
+ // Copy native globals for reference ("native" is an ES3 reserved keyword)
109
+ nativ = {
110
+ exec: RegExp.prototype.exec,
111
+ test: RegExp.prototype.test,
112
+ match: String.prototype.match,
113
+ replace: String.prototype.replace,
114
+ split: String.prototype.split
115
+ },
116
+ compliantExecNpcg = nativ.exec.call(/()??/, "")[1] === undefined, // check `exec` handling of nonparticipating capturing groups
117
+ compliantLastIndexIncrement = function () {
118
+ var x = /^/g;
119
+ nativ.test.call(x, "");
120
+ return !x.lastIndex;
121
+ }(),
122
+ hasNativeY = RegExp.prototype.sticky !== undefined,
123
+ nativeTokens = {};
124
+
125
+ // `nativeTokens` match native multicharacter metasequences only (including deprecated octals,
126
+ // excluding character classes)
127
+ nativeTokens[XRegExp.INSIDE_CLASS] = /^(?:\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S]))/;
128
+ nativeTokens[XRegExp.OUTSIDE_CLASS] = /^(?:\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|\(\?[:=!]|[?*+]\?|{\d+(?:,\d*)?}\??)/;
129
+
130
+
131
+ //---------------------------------
132
+ // Public methods
133
+ //---------------------------------
134
+
135
+ // Lets you extend or change XRegExp syntax and create custom flags. This is used internally by
136
+ // the XRegExp library and can be used to create XRegExp plugins. This function is intended for
137
+ // users with advanced knowledge of JavaScript's regular expression syntax and behavior. It can
138
+ // be disabled by `XRegExp.freezeTokens`
139
+ XRegExp.addToken = function (regex, handler, scope, trigger) {
140
+ tokens.push({
141
+ pattern: clone(regex, "g" + (hasNativeY ? "y" : "")),
142
+ handler: handler,
143
+ scope: scope || XRegExp.OUTSIDE_CLASS,
144
+ trigger: trigger || null
145
+ });
146
+ };
147
+
148
+ // Accepts a pattern and flags; returns an extended `RegExp` object. If the pattern and flag
149
+ // combination has previously been cached, the cached copy is returned; otherwise the newly
150
+ // created regex is cached
151
+ XRegExp.cache = function (pattern, flags) {
152
+ var key = pattern + "/" + (flags || "");
153
+ return XRegExp.cache[key] || (XRegExp.cache[key] = XRegExp(pattern, flags));
154
+ };
155
+
156
+ // Accepts a `RegExp` instance; returns a copy with the `/g` flag set. The copy has a fresh
157
+ // `lastIndex` (set to zero). If you want to copy a regex without forcing the `global`
158
+ // property, use `XRegExp(regex)`. Do not use `RegExp(regex)` because it will not preserve
159
+ // special properties required for named capture
160
+ XRegExp.copyAsGlobal = function (regex) {
161
+ return clone(regex, "g");
162
+ };
163
+
164
+ // Accepts a string; returns the string with regex metacharacters escaped. The returned string
165
+ // can safely be used at any point within a regex to match the provided literal string. Escaped
166
+ // characters are [ ] { } ( ) * + ? - . , \ ^ $ | # and whitespace
167
+ XRegExp.escape = function (str) {
168
+ return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
169
+ };
170
+
171
+ // Accepts a string to search, regex to search with, position to start the search within the
172
+ // string (default: 0), and an optional Boolean indicating whether matches must start at-or-
173
+ // after the position or at the specified position only. This function ignores the `lastIndex`
174
+ // of the provided regex in its own handling, but updates the property for compatibility
175
+ XRegExp.execAt = function (str, regex, pos, anchored) {
176
+ var r2 = clone(regex, "g" + ((anchored && hasNativeY) ? "y" : "")),
177
+ match;
178
+ r2.lastIndex = pos = pos || 0;
179
+ match = r2.exec(str); // Run the altered `exec` (required for `lastIndex` fix, etc.)
180
+ if (anchored && match && match.index !== pos)
181
+ match = null;
182
+ if (regex.global)
183
+ regex.lastIndex = match ? r2.lastIndex : 0;
184
+ return match;
185
+ };
186
+
187
+ // Breaks the unrestorable link to XRegExp's private list of tokens, thereby preventing
188
+ // syntax and flag changes. Should be run after XRegExp and any plugins are loaded
189
+ XRegExp.freezeTokens = function () {
190
+ XRegExp.addToken = function () {
191
+ throw Error("can't run addToken after freezeTokens");
192
+ };
193
+ };
194
+
195
+ // Accepts any value; returns a Boolean indicating whether the argument is a `RegExp` object.
196
+ // Note that this is also `true` for regex literals and regexes created by the `XRegExp`
197
+ // constructor. This works correctly for variables created in another frame, when `instanceof`
198
+ // and `constructor` checks would fail to work as intended
199
+ XRegExp.isRegExp = function (o) {
200
+ return Object.prototype.toString.call(o) === "[object RegExp]";
201
+ };
202
+
203
+ // Executes `callback` once per match within `str`. Provides a simpler and cleaner way to
204
+ // iterate over regex matches compared to the traditional approaches of subverting
205
+ // `String.prototype.replace` or repeatedly calling `exec` within a `while` loop
206
+ XRegExp.iterate = function (str, regex, callback, context) {
207
+ var r2 = clone(regex, "g"),
208
+ i = -1, match;
209
+ while (match = r2.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.)
210
+ if (regex.global)
211
+ regex.lastIndex = r2.lastIndex; // Doing this to follow expectations if `lastIndex` is checked within `callback`
212
+ callback.call(context, match, ++i, str, regex);
213
+ if (r2.lastIndex === match.index)
214
+ r2.lastIndex++;
215
+ }
216
+ if (regex.global)
217
+ regex.lastIndex = 0;
218
+ };
219
+
220
+ // Accepts a string and an array of regexes; returns the result of using each successive regex
221
+ // to search within the matches of the previous regex. The array of regexes can also contain
222
+ // objects with `regex` and `backref` properties, in which case the named or numbered back-
223
+ // references specified are passed forward to the next regex or returned. E.g.:
224
+ // var xregexpImgFileNames = XRegExp.matchChain(html, [
225
+ // {regex: /<img\b([^>]+)>/i, backref: 1}, // <img> tag attributes
226
+ // {regex: XRegExp('(?ix) \\s src=" (?<src> [^"]+ )'), backref: "src"}, // src attribute values
227
+ // {regex: XRegExp("^http://xregexp\\.com(/[^#?]+)", "i"), backref: 1}, // xregexp.com paths
228
+ // /[^\/]+$/ // filenames (strip directory paths)
229
+ // ]);
230
+ XRegExp.matchChain = function (str, chain) {
231
+ return function recurseChain (values, level) {
232
+ var item = chain[level].regex ? chain[level] : {regex: chain[level]},
233
+ regex = clone(item.regex, "g"),
234
+ matches = [], i;
235
+ for (i = 0; i < values.length; i++) {
236
+ XRegExp.iterate(values[i], regex, function (match) {
237
+ matches.push(item.backref ? (match[item.backref] || "") : match[0]);
238
+ });
239
+ }
240
+ return ((level === chain.length - 1) || !matches.length) ?
241
+ matches : recurseChain(matches, level + 1);
242
+ }([str], 0);
243
+ };
244
+
245
+
246
+ //---------------------------------
247
+ // New RegExp prototype methods
248
+ //---------------------------------
249
+
250
+ // Accepts a context object and arguments array; returns the result of calling `exec` with the
251
+ // first value in the arguments array. the context is ignored but is accepted for congruity
252
+ // with `Function.prototype.apply`
253
+ RegExp.prototype.apply = function (context, args) {
254
+ return this.exec(args[0]);
255
+ };
256
+
257
+ // Accepts a context object and string; returns the result of calling `exec` with the provided
258
+ // string. the context is ignored but is accepted for congruity with `Function.prototype.call`
259
+ RegExp.prototype.call = function (context, str) {
260
+ return this.exec(str);
261
+ };
262
+
263
+
264
+ //---------------------------------
265
+ // Overriden native methods
266
+ //---------------------------------
267
+
268
+ // Adds named capture support (with backreferences returned as `result.name`), and fixes two
269
+ // cross-browser issues per ES3:
270
+ // - Captured values for nonparticipating capturing groups should be returned as `undefined`,
271
+ // rather than the empty string.
272
+ // - `lastIndex` should not be incremented after zero-length matches.
273
+ RegExp.prototype.exec = function (str) {
274
+ var match, name, r2, origLastIndex;
275
+ if (!this.global)
276
+ origLastIndex = this.lastIndex;
277
+ match = nativ.exec.apply(this, arguments);
278
+ if (match) {
279
+ // Fix browsers whose `exec` methods don't consistently return `undefined` for
280
+ // nonparticipating capturing groups
281
+ if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) {
282
+ r2 = RegExp(this.source, nativ.replace.call(getNativeFlags(this), "g", ""));
283
+ // Using `str.slice(match.index)` rather than `match[0]` in case lookahead allowed
284
+ // matching due to characters outside the match
285
+ nativ.replace.call((str + "").slice(match.index), r2, function () {
286
+ for (var i = 1; i < arguments.length - 2; i++) {
287
+ if (arguments[i] === undefined)
288
+ match[i] = undefined;
289
+ }
290
+ });
291
+ }
292
+ // Attach named capture properties
293
+ if (this._xregexp && this._xregexp.captureNames) {
294
+ for (var i = 1; i < match.length; i++) {
295
+ name = this._xregexp.captureNames[i - 1];
296
+ if (name)
297
+ match[name] = match[i];
298
+ }
299
+ }
300
+ // Fix browsers that increment `lastIndex` after zero-length matches
301
+ if (!compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index))
302
+ this.lastIndex--;
303
+ }
304
+ if (!this.global)
305
+ this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows)
306
+ return match;
307
+ };
308
+
309
+ // Fix browser bugs in native method
310
+ RegExp.prototype.test = function (str) {
311
+ // Use the native `exec` to skip some processing overhead, even though the altered
312
+ // `exec` would take care of the `lastIndex` fixes
313
+ var match, origLastIndex;
314
+ if (!this.global)
315
+ origLastIndex = this.lastIndex;
316
+ match = nativ.exec.call(this, str);
317
+ // Fix browsers that increment `lastIndex` after zero-length matches
318
+ if (match && !compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index))
319
+ this.lastIndex--;
320
+ if (!this.global)
321
+ this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows)
322
+ return !!match;
323
+ };
324
+
325
+ // Adds named capture support and fixes browser bugs in native method
326
+ String.prototype.match = function (regex) {
327
+ if (!XRegExp.isRegExp(regex))
328
+ regex = RegExp(regex); // Native `RegExp`
329
+ if (regex.global) {
330
+ var result = nativ.match.apply(this, arguments);
331
+ regex.lastIndex = 0; // Fix IE bug
332
+ return result;
333
+ }
334
+ return regex.exec(this); // Run the altered `exec`
335
+ };
336
+
337
+ // Adds support for `${n}` tokens for named and numbered backreferences in replacement text,
338
+ // and provides named backreferences to replacement functions as `arguments[0].name`. Also
339
+ // fixes cross-browser differences in replacement text syntax when performing a replacement
340
+ // using a nonregex search value, and the value of replacement regexes' `lastIndex` property
341
+ // during replacement iterations. Note that this doesn't support SpiderMonkey's proprietary
342
+ // third (`flags`) parameter
343
+ String.prototype.replace = function (search, replacement) {
344
+ var isRegex = XRegExp.isRegExp(search),
345
+ captureNames, result, str, origLastIndex;
346
+
347
+ // There are too many combinations of search/replacement types/values and browser bugs that
348
+ // preclude passing to native `replace`, so don't try
349
+ //if (...)
350
+ // return nativ.replace.apply(this, arguments);
351
+
352
+ if (isRegex) {
353
+ if (search._xregexp)
354
+ captureNames = search._xregexp.captureNames; // Array or `null`
355
+ if (!search.global)
356
+ origLastIndex = search.lastIndex;
357
+ } else {
358
+ search = search + ""; // Type conversion
359
+ }
360
+
361
+ if (Object.prototype.toString.call(replacement) === "[object Function]") {
362
+ result = nativ.replace.call(this + "", search, function () {
363
+ if (captureNames) {
364
+ // Change the `arguments[0]` string primitive to a String object which can store properties
365
+ arguments[0] = new String(arguments[0]);
366
+ // Store named backreferences on `arguments[0]`
367
+ for (var i = 0; i < captureNames.length; i++) {
368
+ if (captureNames[i])
369
+ arguments[0][captureNames[i]] = arguments[i + 1];
370
+ }
371
+ }
372
+ // Update `lastIndex` before calling `replacement` (fix browsers)
373
+ if (isRegex && search.global)
374
+ search.lastIndex = arguments[arguments.length - 2] + arguments[0].length;
375
+ return replacement.apply(null, arguments);
376
+ });
377
+ } else {
378
+ str = this + ""; // Type conversion, so `args[args.length - 1]` will be a string (given nonstring `this`)
379
+ result = nativ.replace.call(str, search, function () {
380
+ var args = arguments; // Keep this function's `arguments` available through closure
381
+ return nativ.replace.call(replacement + "", replacementToken, function ($0, $1, $2) {
382
+ // Numbered backreference (without delimiters) or special variable
383
+ if ($1) {
384
+ switch ($1) {
385
+ case "$": return "$";
386
+ case "&": return args[0];
387
+ case "`": return args[args.length - 1].slice(0, args[args.length - 2]);
388
+ case "'": return args[args.length - 1].slice(args[args.length - 2] + args[0].length);
389
+ // Numbered backreference
390
+ default:
391
+ // What does "$10" mean?
392
+ // - Backreference 10, if 10 or more capturing groups exist
393
+ // - Backreference 1 followed by "0", if 1-9 capturing groups exist
394
+ // - Otherwise, it's the string "$10"
395
+ // Also note:
396
+ // - Backreferences cannot be more than two digits (enforced by `replacementToken`)
397
+ // - "$01" is equivalent to "$1" if a capturing group exists, otherwise it's the string "$01"
398
+ // - There is no "$0" token ("$&" is the entire match)
399
+ var literalNumbers = "";
400
+ $1 = +$1; // Type conversion; drop leading zero
401
+ if (!$1) // `$1` was "0" or "00"
402
+ return $0;
403
+ while ($1 > args.length - 3) {
404
+ literalNumbers = String.prototype.slice.call($1, -1) + literalNumbers;
405
+ $1 = Math.floor($1 / 10); // Drop the last digit
406
+ }
407
+ return ($1 ? args[$1] || "" : "$") + literalNumbers;
408
+ }
409
+ // Named backreference or delimited numbered backreference
410
+ } else {
411
+ // What does "${n}" mean?
412
+ // - Backreference to numbered capture n. Two differences from "$n":
413
+ // - n can be more than two digits
414
+ // - Backreference 0 is allowed, and is the entire match
415
+ // - Backreference to named capture n, if it exists and is not a number overridden by numbered capture
416
+ // - Otherwise, it's the string "${n}"
417
+ var n = +$2; // Type conversion; drop leading zeros
418
+ if (n <= args.length - 3)
419
+ return args[n];
420
+ n = captureNames ? indexOf(captureNames, $2) : -1;
421
+ return n > -1 ? args[n + 1] : $0;
422
+ }
423
+ });
424
+ });
425
+ }
426
+
427
+ if (isRegex) {
428
+ if (search.global)
429
+ search.lastIndex = 0; // Fix IE, Safari bug (last tested IE 9.0.5, Safari 5.1.2 on Windows)
430
+ else
431
+ search.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows)
432
+ }
433
+
434
+ return result;
435
+ };
436
+
437
+ // A consistent cross-browser, ES3 compliant `split`
438
+ String.prototype.split = function (s /* separator */, limit) {
439
+ // If separator `s` is not a regex, use the native `split`
440
+ if (!XRegExp.isRegExp(s))
441
+ return nativ.split.apply(this, arguments);
442
+
443
+ var str = this + "", // Type conversion
444
+ output = [],
445
+ lastLastIndex = 0,
446
+ match, lastLength;
447
+
448
+ // Behavior for `limit`: if it's...
449
+ // - `undefined`: No limit
450
+ // - `NaN` or zero: Return an empty array
451
+ // - A positive number: Use `Math.floor(limit)`
452
+ // - A negative number: No limit
453
+ // - Other: Type-convert, then use the above rules
454
+ if (limit === undefined || +limit < 0) {
455
+ limit = Infinity;
456
+ } else {
457
+ limit = Math.floor(+limit);
458
+ if (!limit)
459
+ return [];
460
+ }
461
+
462
+ // This is required if not `s.global`, and it avoids needing to set `s.lastIndex` to zero
463
+ // and restore it to its original value when we're done using the regex
464
+ s = XRegExp.copyAsGlobal(s);
465
+
466
+ while (match = s.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.)
467
+ if (s.lastIndex > lastLastIndex) {
468
+ output.push(str.slice(lastLastIndex, match.index));
469
+
470
+ if (match.length > 1 && match.index < str.length)
471
+ Array.prototype.push.apply(output, match.slice(1));
472
+
473
+ lastLength = match[0].length;
474
+ lastLastIndex = s.lastIndex;
475
+
476
+ if (output.length >= limit)
477
+ break;
478
+ }
479
+
480
+ if (s.lastIndex === match.index)
481
+ s.lastIndex++;
482
+ }
483
+
484
+ if (lastLastIndex === str.length) {
485
+ if (!nativ.test.call(s, "") || lastLength)
486
+ output.push("");
487
+ } else {
488
+ output.push(str.slice(lastLastIndex));
489
+ }
490
+
491
+ return output.length > limit ? output.slice(0, limit) : output;
492
+ };
493
+
494
+
495
+ //---------------------------------
496
+ // Private helper functions
497
+ //---------------------------------
498
+
499
+ // Supporting function for `XRegExp`, `XRegExp.copyAsGlobal`, etc. Returns a copy of a `RegExp`
500
+ // instance with a fresh `lastIndex` (set to zero), preserving properties required for named
501
+ // capture. Also allows adding new flags in the process of copying the regex
502
+ function clone (regex, additionalFlags) {
503
+ if (!XRegExp.isRegExp(regex))
504
+ throw TypeError("type RegExp expected");
505
+ var x = regex._xregexp;
506
+ regex = XRegExp(regex.source, getNativeFlags(regex) + (additionalFlags || ""));
507
+ if (x) {
508
+ regex._xregexp = {
509
+ source: x.source,
510
+ captureNames: x.captureNames ? x.captureNames.slice(0) : null
511
+ };
512
+ }
513
+ return regex;
514
+ }
515
+
516
+ function getNativeFlags (regex) {
517
+ return (regex.global ? "g" : "") +
518
+ (regex.ignoreCase ? "i" : "") +
519
+ (regex.multiline ? "m" : "") +
520
+ (regex.extended ? "x" : "") + // Proposed for ES4; included in AS3
521
+ (regex.sticky ? "y" : "");
522
+ }
523
+
524
+ function runTokens (pattern, index, scope, context) {
525
+ var i = tokens.length,
526
+ result, match, t;
527
+ // Protect against constructing XRegExps within token handler and trigger functions
528
+ isInsideConstructor = true;
529
+ // Must reset `isInsideConstructor`, even if a `trigger` or `handler` throws
530
+ try {
531
+ while (i--) { // Run in reverse order
532
+ t = tokens[i];
533
+ if ((scope & t.scope) && (!t.trigger || t.trigger.call(context))) {
534
+ t.pattern.lastIndex = index;
535
+ match = t.pattern.exec(pattern); // Running the altered `exec` here allows use of named backreferences, etc.
536
+ if (match && match.index === index) {
537
+ result = {
538
+ output: t.handler.call(context, match, scope),
539
+ match: match
540
+ };
541
+ break;
542
+ }
543
+ }
544
+ }
545
+ } catch (err) {
546
+ throw err;
547
+ } finally {
548
+ isInsideConstructor = false;
549
+ }
550
+ return result;
551
+ }
552
+
553
+ function indexOf (array, item, from) {
554
+ if (Array.prototype.indexOf) // Use the native array method if available
555
+ return array.indexOf(item, from);
556
+ for (var i = from || 0; i < array.length; i++) {
557
+ if (array[i] === item)
558
+ return i;
559
+ }
560
+ return -1;
561
+ }
562
+
563
+
564
+ //---------------------------------
565
+ // Built-in tokens
566
+ //---------------------------------
567
+
568
+ // Augment XRegExp's regular expression syntax and flags. Note that when adding tokens, the
569
+ // third (`scope`) argument defaults to `XRegExp.OUTSIDE_CLASS`
570
+
571
+ // Comment pattern: (?# )
572
+ XRegExp.addToken(
573
+ /\(\?#[^)]*\)/,
574
+ function (match) {
575
+ // Keep tokens separated unless the following token is a quantifier
576
+ return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)";
577
+ }
578
+ );
579
+
580
+ // Capturing group (match the opening parenthesis only).
581
+ // Required for support of named capturing groups
582
+ XRegExp.addToken(
583
+ /\((?!\?)/,
584
+ function () {
585
+ this.captureNames.push(null);
586
+ return "(";
587
+ }
588
+ );
589
+
590
+ // Named capturing group (match the opening delimiter only): (?<name>
591
+ XRegExp.addToken(
592
+ /\(\?<([$\w]+)>/,
593
+ function (match) {
594
+ this.captureNames.push(match[1]);
595
+ this.hasNamedCapture = true;
596
+ return "(";
597
+ }
598
+ );
599
+
600
+ // Named backreference: \k<name>
601
+ XRegExp.addToken(
602
+ /\\k<([\w$]+)>/,
603
+ function (match) {
604
+ var index = indexOf(this.captureNames, match[1]);
605
+ // Keep backreferences separate from subsequent literal numbers. Preserve back-
606
+ // references to named groups that are undefined at this point as literal strings
607
+ return index > -1 ?
608
+ "\\" + (index + 1) + (isNaN(match.input.charAt(match.index + match[0].length)) ? "" : "(?:)") :
609
+ match[0];
610
+ }
611
+ );
612
+
613
+ // Empty character class: [] or [^]
614
+ XRegExp.addToken(
615
+ /\[\^?]/,
616
+ function (match) {
617
+ // For cross-browser compatibility with ES3, convert [] to \b\B and [^] to [\s\S].
618
+ // (?!) should work like \b\B, but is unreliable in Firefox
619
+ return match[0] === "[]" ? "\\b\\B" : "[\\s\\S]";
620
+ }
621
+ );
622
+
623
+ // Mode modifier at the start of the pattern only, with any combination of flags imsx: (?imsx)
624
+ // Does not support x(?i), (?-i), (?i-m), (?i: ), (?i)(?m), etc.
625
+ XRegExp.addToken(
626
+ /^\(\?([imsx]+)\)/,
627
+ function (match) {
628
+ this.setFlag(match[1]);
629
+ return "";
630
+ }
631
+ );
632
+
633
+ // Whitespace and comments, in free-spacing (aka extended) mode only
634
+ XRegExp.addToken(
635
+ /(?:\s+|#.*)+/,
636
+ function (match) {
637
+ // Keep tokens separated unless the following token is a quantifier
638
+ return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)";
639
+ },
640
+ XRegExp.OUTSIDE_CLASS,
641
+ function () {return this.hasFlag("x");}
642
+ );
643
+
644
+ // Dot, in dotall (aka singleline) mode only
645
+ XRegExp.addToken(
646
+ /\./,
647
+ function () {return "[\\s\\S]";},
648
+ XRegExp.OUTSIDE_CLASS,
649
+ function () {return this.hasFlag("s");}
650
+ );
651
+
652
+
653
+ //---------------------------------
654
+ // Backward compatibility
655
+ //---------------------------------
656
+
657
+ // Uncomment the following block for compatibility with XRegExp 1.0-1.2:
658
+ /*
659
+ XRegExp.matchWithinChain = XRegExp.matchChain;
660
+ RegExp.prototype.addFlags = function (s) {return clone(this, s);};
661
+ RegExp.prototype.execAll = function (s) {var r = []; XRegExp.iterate(s, this, function (m) {r.push(m);}); return r;};
662
+ RegExp.prototype.forEachExec = function (s, f, c) {return XRegExp.iterate(s, this, f, c);};
663
+ RegExp.prototype.validate = function (s) {var r = RegExp("^(?:" + this.source + ")$(?!\\s)", getNativeFlags(this)); if (this.global) this.lastIndex = 0; return s.search(r) === 0;};
664
+ */
665
+
666
+ })();
667
+ if (typeof(SyntaxHighlighter) == 'undefined') var SyntaxHighlighter = function() {
668
+
669
+
670
+ // Shortcut object which will be assigned to the SyntaxHighlighter variable.
671
+ // This is a shorthand for local reference in order to avoid long namespace
672
+ // references to SyntaxHighlighter.whatever...
673
+ var sh = {
674
+ defaults : {
675
+ /** Additional CSS class names to be added to highlighter elements. */
676
+ 'class-name' : '',
677
+
678
+ /** First line number. */
679
+ 'first-line' : 1,
680
+
681
+ /**
682
+ * Pads line numbers. Possible values are:
683
+ *
684
+ * false - don't pad line numbers.
685
+ * true - automaticaly pad numbers with minimum required number of leading zeroes.
686
+ * [int] - length up to which pad line numbers.
687
+ */
688
+ 'pad-line-numbers' : false,
689
+
690
+ /** Lines to highlight. */
691
+ 'highlight' : null,
692
+
693
+ /** Title to be displayed above the code block. */
694
+ 'title' : null,
695
+
696
+ /** Enables or disables smart tabs. */
697
+ 'smart-tabs' : true,
698
+
699
+ /** Gets or sets tab size. */
700
+ 'tab-size' : 4,
701
+
702
+ /** Enables or disables gutter. */
703
+ 'gutter' : true,
704
+
705
+ /** Enables or disables toolbar. */
706
+ 'toolbar' : true,
707
+
708
+ /** Enables quick code copy and paste from double click. */
709
+ 'quick-code' : true,
710
+
711
+ /** Forces code view to be collapsed. */
712
+ 'collapse' : false,
713
+
714
+ /** Enables or disables automatic links. */
715
+ 'auto-links' : true,
716
+
717
+ /** Gets or sets light mode. Equavalent to turning off gutter and toolbar. */
718
+ 'light' : false,
719
+
720
+ 'unindent' : true,
721
+
722
+ 'html-script' : false
723
+ },
724
+
725
+ config : {
726
+ space : '&nbsp;',
727
+
728
+ /** Enables use of <SCRIPT type="syntaxhighlighter" /> tags. */
729
+ useScriptTags : true,
730
+
731
+ /** Blogger mode flag. */
732
+ bloggerMode : false,
733
+
734
+ stripBrs : false,
735
+
736
+ /** Name of the tag that SyntaxHighlighter will automatically look for. */
737
+ tagName : 'pre',
738
+
739
+ strings : {
740
+ expandSource : 'expand source',
741
+ help : '?',
742
+ alert: 'SyntaxHighlighter\n\n',
743
+ noBrush : 'Can\'t find brush for: ',
744
+ brushNotHtmlScript : 'Brush wasn\'t configured for html-script option: ',
745
+
746
+ // this is populated by the build script
747
+ aboutDialog : '@ABOUT@'
748
+ }
749
+ },
750
+
751
+ /** Internal 'global' variables. */
752
+ vars : {
753
+ discoveredBrushes : null,
754
+ highlighters : {}
755
+ },
756
+
757
+ /** This object is populated by user included external brush files. */
758
+ brushes : {},
759
+
760
+ /** Common regular expressions. */
761
+ regexLib : {
762
+ multiLineCComments : /\/\*[\s\S]*?\*\//gm,
763
+ singleLineCComments : /\/\/.*$/gm,
764
+ singleLinePerlComments : /#.*$/gm,
765
+ doubleQuotedString : /"([^\\"\n]|\\.)*"/g,
766
+ singleQuotedString : /'([^\\'\n]|\\.)*'/g,
767
+ multiLineDoubleQuotedString : new XRegExp('"([^\\\\"]|\\\\.)*"', 'gs'),
768
+ multiLineSingleQuotedString : new XRegExp("'([^\\\\']|\\\\.)*'", 'gs'),
769
+ xmlComments : /(&lt;|<)!--[\s\S]*?--(&gt;|>)/gm,
770
+ url : /\w+:\/\/[\w-.\/?%&=:@;#]*/g,
771
+
772
+ /** <?= ?> tags. */
773
+ phpScriptTags : { left: /(&lt;|<)\?(?:=|php)?/g, right: /\?(&gt;|>)/g, 'eof' : true },
774
+
775
+ /** <%= %> tags. */
776
+ aspScriptTags : { left: /(&lt;|<)%=?/g, right: /%(&gt;|>)/g },
777
+
778
+ /** <script> tags. */
779
+ scriptScriptTags : { left: /(&lt;|<)\s*script.*?(&gt;|>)/gi, right: /(&lt;|<)\/\s*script\s*(&gt;|>)/gi }
780
+ },
781
+
782
+ toolbar: {
783
+ /**
784
+ * Generates HTML markup for the toolbar.
785
+ * @param {Highlighter} highlighter Highlighter instance.
786
+ * @return {String} Returns HTML markup.
787
+ */
788
+ getHtml: function(highlighter)
789
+ {
790
+ var html = '<div class="toolbar">',
791
+ items = sh.toolbar.items,
792
+ list = items.list
793
+ ;
794
+
795
+ function defaultGetHtml(highlighter, name)
796
+ {
797
+ return sh.toolbar.getButtonHtml(highlighter, name, sh.config.strings[name]);
798
+ };
799
+
800
+ for (var i = 0; i < list.length; i++)
801
+ html += (items[list[i]].getHtml || defaultGetHtml)(highlighter, list[i]);
802
+
803
+ html += '</div>';
804
+
805
+ return html;
806
+ },
807
+
808
+ /**
809
+ * Generates HTML markup for a regular button in the toolbar.
810
+ * @param {Highlighter} highlighter Highlighter instance.
811
+ * @param {String} commandName Command name that would be executed.
812
+ * @param {String} label Label text to display.
813
+ * @return {String} Returns HTML markup.
814
+ */
815
+ getButtonHtml: function(highlighter, commandName, label)
816
+ {
817
+ return '<span><a href="#" class="toolbar_item'
818
+ + ' command_' + commandName
819
+ + ' ' + commandName
820
+ + '">' + label + '</a></span>'
821
+ ;
822
+ },
823
+
824
+ /**
825
+ * Event handler for a toolbar anchor.
826
+ */
827
+ handler: function(e)
828
+ {
829
+ var target = e.target,
830
+ className = target.className || ''
831
+ ;
832
+
833
+ function getValue(name)
834
+ {
835
+ var r = new RegExp(name + '_(\\w+)'),
836
+ match = r.exec(className)
837
+ ;
838
+
839
+ return match ? match[1] : null;
840
+ };
841
+
842
+ var highlighter = getHighlighterById(findParentElement(target, '.syntaxhighlighter').id),
843
+ commandName = getValue('command')
844
+ ;
845
+
846
+ // execute the toolbar command
847
+ if (highlighter && commandName)
848
+ sh.toolbar.items[commandName].execute(highlighter);
849
+
850
+ // disable default A click behaviour
851
+ e.preventDefault();
852
+ },
853
+
854
+ /** Collection of toolbar items. */
855
+ items : {
856
+ // Ordered lis of items in the toolbar. Can't expect `for (var n in items)` to be consistent.
857
+ list: ['expandSource', 'help'],
858
+
859
+ expandSource: {
860
+ getHtml: function(highlighter)
861
+ {
862
+ if (highlighter.getParam('collapse') != true)
863
+ return '';
864
+
865
+ var title = highlighter.getParam('title');
866
+ return sh.toolbar.getButtonHtml(highlighter, 'expandSource', title ? title : sh.config.strings.expandSource);
867
+ },
868
+
869
+ execute: function(highlighter)
870
+ {
871
+ var div = getHighlighterDivById(highlighter.id);
872
+ removeClass(div, 'collapsed');
873
+ }
874
+ },
875
+
876
+ /** Command to display the about dialog window. */
877
+ help: {
878
+ execute: function(highlighter)
879
+ {
880
+ var wnd = popup('', '_blank', 500, 250, 'scrollbars=0'),
881
+ doc = wnd.document
882
+ ;
883
+
884
+ doc.write(sh.config.strings.aboutDialog);
885
+ doc.close();
886
+ wnd.focus();
887
+ }
888
+ }
889
+ }
890
+ },
891
+
892
+ /**
893
+ * Finds all elements on the page which should be processes by SyntaxHighlighter.
894
+ *
895
+ * @param {Object} globalParams Optional parameters which override element's
896
+ * parameters. Only used if element is specified.
897
+ *
898
+ * @param {Object} element Optional element to highlight. If none is
899
+ * provided, all elements in the current document
900
+ * are returned which qualify.
901
+ *
902
+ * @return {Array} Returns list of <code>{ target: DOMElement, params: Object }</code> objects.
903
+ */
904
+ findElements: function(globalParams, element)
905
+ {
906
+ var elements = element ? [element] : toArray(document.getElementsByTagName(sh.config.tagName)),
907
+ conf = sh.config,
908
+ result = []
909
+ ;
910
+
911
+ // support for <SCRIPT TYPE="syntaxhighlighter" /> feature
912
+ if (conf.useScriptTags)
913
+ elements = elements.concat(getSyntaxHighlighterScriptTags());
914
+
915
+ if (elements.length === 0)
916
+ return result;
917
+
918
+ for (var i = 0; i < elements.length; i++)
919
+ {
920
+ var item = {
921
+ target: elements[i],
922
+ // local params take precedence over globals
923
+ params: merge(globalParams, parseParams(elements[i].className))
924
+ };
925
+
926
+ if (item.params['brush'] == null)
927
+ continue;
928
+
929
+ result.push(item);
930
+ }
931
+
932
+ return result;
933
+ },
934
+
935
+ /**
936
+ * Shorthand to highlight all elements on the page that are marked as
937
+ * SyntaxHighlighter source code.
938
+ *
939
+ * @param {Object} globalParams Optional parameters which override element's
940
+ * parameters. Only used if element is specified.
941
+ *
942
+ * @param {Object} element Optional element to highlight. If none is
943
+ * provided, all elements in the current document
944
+ * are highlighted.
945
+ */
946
+ highlight: function(globalParams, element)
947
+ {
948
+ var elements = this.findElements(globalParams, element),
949
+ propertyName = 'innerHTML',
950
+ highlighter = null,
951
+ conf = sh.config
952
+ ;
953
+
954
+ if (elements.length === 0)
955
+ return;
956
+
957
+ for (var i = 0; i < elements.length; i++)
958
+ {
959
+ var element = elements[i],
960
+ target = element.target,
961
+ params = element.params,
962
+ brushName = params.brush,
963
+ code
964
+ ;
965
+
966
+ if (brushName == null)
967
+ continue;
968
+
969
+ // Instantiate a brush
970
+ if (params['html-script'] == 'true' || sh.defaults['html-script'] == true)
971
+ {
972
+ highlighter = new sh.HtmlScript(brushName);
973
+ brushName = 'htmlscript';
974
+ }
975
+ else
976
+ {
977
+ var brush = findBrush(brushName);
978
+
979
+ if (brush)
980
+ highlighter = new brush();
981
+ else
982
+ continue;
983
+ }
984
+
985
+ code = target[propertyName];
986
+
987
+ // remove CDATA from <SCRIPT/> tags if it's present
988
+ if (conf.useScriptTags)
989
+ code = stripCData(code);
990
+
991
+ // Inject title if the attribute is present
992
+ if ((target.title || '') != '')
993
+ params.title = target.title;
994
+
995
+ params['brush'] = brushName;
996
+ highlighter.init(params);
997
+ element = highlighter.getDiv(code);
998
+
999
+ // carry over ID
1000
+ if ((target.id || '') != '')
1001
+ element.id = target.id;
1002
+
1003
+ target.parentNode.replaceChild(element, target);
1004
+ }
1005
+ },
1006
+
1007
+ /**
1008
+ * Main entry point for the SyntaxHighlighter.
1009
+ * @param {Object} params Optional params to apply to all highlighted elements.
1010
+ */
1011
+ all: function(params)
1012
+ {
1013
+ attachEvent(
1014
+ window,
1015
+ 'load',
1016
+ function() { sh.highlight(params); }
1017
+ );
1018
+ }
1019
+ }; // end of sh
1020
+
1021
+ /**
1022
+ * Checks if target DOM elements has specified CSS class.
1023
+ * @param {DOMElement} target Target DOM element to check.
1024
+ * @param {String} className Name of the CSS class to check for.
1025
+ * @return {Boolean} Returns true if class name is present, false otherwise.
1026
+ */
1027
+ function hasClass(target, className)
1028
+ {
1029
+ return target.className.indexOf(className) != -1;
1030
+ };
1031
+
1032
+ /**
1033
+ * Adds CSS class name to the target DOM element.
1034
+ * @param {DOMElement} target Target DOM element.
1035
+ * @param {String} className New CSS class to add.
1036
+ */
1037
+ function addClass(target, className)
1038
+ {
1039
+ if (!hasClass(target, className))
1040
+ target.className += ' ' + className;
1041
+ };
1042
+
1043
+ /**
1044
+ * Removes CSS class name from the target DOM element.
1045
+ * @param {DOMElement} target Target DOM element.
1046
+ * @param {String} className CSS class to remove.
1047
+ */
1048
+ function removeClass(target, className)
1049
+ {
1050
+ target.className = target.className.replace(className, '');
1051
+ };
1052
+
1053
+ /**
1054
+ * Converts the source to array object. Mostly used for function arguments and
1055
+ * lists returned by getElementsByTagName() which aren't Array objects.
1056
+ * @param {List} source Source list.
1057
+ * @return {Array} Returns array.
1058
+ */
1059
+ function toArray(source)
1060
+ {
1061
+ var result = [];
1062
+
1063
+ for (var i = 0; i < source.length; i++)
1064
+ result.push(source[i]);
1065
+
1066
+ return result;
1067
+ };
1068
+
1069
+ /**
1070
+ * Splits block of text into lines.
1071
+ * @param {String} block Block of text.
1072
+ * @return {Array} Returns array of lines.
1073
+ */
1074
+ function splitLines(block)
1075
+ {
1076
+ return block.split(/\r?\n/);
1077
+ }
1078
+
1079
+ /**
1080
+ * Generates HTML ID for the highlighter.
1081
+ * @param {String} highlighterId Highlighter ID.
1082
+ * @return {String} Returns HTML ID.
1083
+ */
1084
+ function getHighlighterId(id)
1085
+ {
1086
+ var prefix = 'highlighter_';
1087
+ return id.indexOf(prefix) == 0 ? id : prefix + id;
1088
+ };
1089
+
1090
+ /**
1091
+ * Finds Highlighter instance by ID.
1092
+ * @param {String} highlighterId Highlighter ID.
1093
+ * @return {Highlighter} Returns instance of the highlighter.
1094
+ */
1095
+ function getHighlighterById(id)
1096
+ {
1097
+ return sh.vars.highlighters[getHighlighterId(id)];
1098
+ };
1099
+
1100
+ /**
1101
+ * Finds highlighter's DIV container.
1102
+ * @param {String} highlighterId Highlighter ID.
1103
+ * @return {Element} Returns highlighter's DIV element.
1104
+ */
1105
+ function getHighlighterDivById(id)
1106
+ {
1107
+ return document.getElementById(getHighlighterId(id));
1108
+ };
1109
+
1110
+ /**
1111
+ * Stores highlighter so that getHighlighterById() can do its thing. Each
1112
+ * highlighter must call this method to preserve itself.
1113
+ * @param {Highilghter} highlighter Highlighter instance.
1114
+ */
1115
+ function storeHighlighter(highlighter)
1116
+ {
1117
+ sh.vars.highlighters[getHighlighterId(highlighter.id)] = highlighter;
1118
+ };
1119
+
1120
+ /**
1121
+ * Looks for a child or parent node which has specified classname.
1122
+ * Equivalent to jQuery's $(container).find(".className")
1123
+ * @param {Element} target Target element.
1124
+ * @param {String} search Class name or node name to look for.
1125
+ * @param {Boolean} reverse If set to true, will go up the node tree instead of down.
1126
+ * @return {Element} Returns found child or parent element on null.
1127
+ */
1128
+ function findElement(target, search, reverse /* optional */)
1129
+ {
1130
+ if (target == null)
1131
+ return null;
1132
+
1133
+ var nodes = reverse != true ? target.childNodes : [ target.parentNode ],
1134
+ propertyToFind = { '#' : 'id', '.' : 'className' }[search.substr(0, 1)] || 'nodeName',
1135
+ expectedValue,
1136
+ found
1137
+ ;
1138
+
1139
+ expectedValue = propertyToFind != 'nodeName'
1140
+ ? search.substr(1)
1141
+ : search.toUpperCase()
1142
+ ;
1143
+
1144
+ // main return of the found node
1145
+ if ((target[propertyToFind] || '').indexOf(expectedValue) != -1)
1146
+ return target;
1147
+
1148
+ for (var i = 0; nodes && i < nodes.length && found == null; i++)
1149
+ found = findElement(nodes[i], search, reverse);
1150
+
1151
+ return found;
1152
+ };
1153
+
1154
+ /**
1155
+ * Looks for a parent node which has specified classname.
1156
+ * This is an alias to <code>findElement(container, className, true)</code>.
1157
+ * @param {Element} target Target element.
1158
+ * @param {String} className Class name to look for.
1159
+ * @return {Element} Returns found parent element on null.
1160
+ */
1161
+ function findParentElement(target, className)
1162
+ {
1163
+ return findElement(target, className, true);
1164
+ };
1165
+
1166
+ /**
1167
+ * Finds an index of element in the array.
1168
+ * @ignore
1169
+ * @param {Object} searchElement
1170
+ * @param {Number} fromIndex
1171
+ * @return {Number} Returns index of element if found; -1 otherwise.
1172
+ */
1173
+ function indexOf(array, searchElement, fromIndex)
1174
+ {
1175
+ fromIndex = Math.max(fromIndex || 0, 0);
1176
+
1177
+ for (var i = fromIndex; i < array.length; i++)
1178
+ if(array[i] == searchElement)
1179
+ return i;
1180
+
1181
+ return -1;
1182
+ };
1183
+
1184
+ /**
1185
+ * Generates a unique element ID.
1186
+ */
1187
+ function guid(prefix)
1188
+ {
1189
+ return (prefix || '') + Math.round(Math.random() * 1000000).toString();
1190
+ };
1191
+
1192
+ /**
1193
+ * Merges two objects. Values from obj2 override values in obj1.
1194
+ * Function is NOT recursive and works only for one dimensional objects.
1195
+ * @param {Object} obj1 First object.
1196
+ * @param {Object} obj2 Second object.
1197
+ * @return {Object} Returns combination of both objects.
1198
+ */
1199
+ function merge(obj1, obj2)
1200
+ {
1201
+ var result = {}, name;
1202
+
1203
+ for (name in obj1)
1204
+ result[name] = obj1[name];
1205
+
1206
+ for (name in obj2)
1207
+ result[name] = obj2[name];
1208
+
1209
+ return result;
1210
+ };
1211
+
1212
+ /**
1213
+ * Attempts to convert string to boolean.
1214
+ * @param {String} value Input string.
1215
+ * @return {Boolean} Returns true if input was "true", false if input was "false" and value otherwise.
1216
+ */
1217
+ function toBoolean(value)
1218
+ {
1219
+ var result = { "true" : true, "false" : false }[value];
1220
+ return result == null ? value : result;
1221
+ };
1222
+
1223
+ /**
1224
+ * Opens up a centered popup window.
1225
+ * @param {String} url URL to open in the window.
1226
+ * @param {String} name Popup name.
1227
+ * @param {int} width Popup width.
1228
+ * @param {int} height Popup height.
1229
+ * @param {String} options window.open() options.
1230
+ * @return {Window} Returns window instance.
1231
+ */
1232
+ function popup(url, name, width, height, options)
1233
+ {
1234
+ var x = (screen.width - width) / 2,
1235
+ y = (screen.height - height) / 2
1236
+ ;
1237
+
1238
+ options += ', left=' + x +
1239
+ ', top=' + y +
1240
+ ', width=' + width +
1241
+ ', height=' + height
1242
+ ;
1243
+ options = options.replace(/^,/, '');
1244
+
1245
+ var win = window.open(url, name, options);
1246
+ win.focus();
1247
+ return win;
1248
+ };
1249
+
1250
+ /**
1251
+ * Adds event handler to the target object.
1252
+ * @param {Object} obj Target object.
1253
+ * @param {String} type Name of the event.
1254
+ * @param {Function} func Handling function.
1255
+ */
1256
+ function attachEvent(obj, type, func, scope)
1257
+ {
1258
+ function handler(e)
1259
+ {
1260
+ e = e || window.event;
1261
+
1262
+ if (!e.target)
1263
+ {
1264
+ e.target = e.srcElement;
1265
+ e.preventDefault = function()
1266
+ {
1267
+ this.returnValue = false;
1268
+ };
1269
+ }
1270
+
1271
+ func.call(scope || window, e);
1272
+ };
1273
+
1274
+ if (obj.attachEvent)
1275
+ {
1276
+ obj.attachEvent('on' + type, handler);
1277
+ }
1278
+ else
1279
+ {
1280
+ obj.addEventListener(type, handler, false);
1281
+ }
1282
+ };
1283
+
1284
+ /**
1285
+ * Displays an alert.
1286
+ * @param {String} str String to display.
1287
+ */
1288
+ function alert(str)
1289
+ {
1290
+ window.alert(sh.config.strings.alert + str);
1291
+ };
1292
+
1293
+ /**
1294
+ * Finds a brush by its alias.
1295
+ *
1296
+ * @param {String} alias Brush alias.
1297
+ * @param {Boolean} showAlert Suppresses the alert if false.
1298
+ * @return {Brush} Returns bursh constructor if found, null otherwise.
1299
+ */
1300
+ function findBrush(alias, showAlert)
1301
+ {
1302
+ var brushes = sh.vars.discoveredBrushes,
1303
+ result = null
1304
+ ;
1305
+
1306
+ if (brushes == null)
1307
+ {
1308
+ brushes = {};
1309
+
1310
+ // Find all brushes
1311
+ for (var brush in sh.brushes)
1312
+ {
1313
+ var info = sh.brushes[brush],
1314
+ aliases = info.aliases
1315
+ ;
1316
+
1317
+ if (aliases == null)
1318
+ continue;
1319
+
1320
+ // keep the brush name
1321
+ info.brushName = brush.toLowerCase();
1322
+
1323
+ for (var i = 0; i < aliases.length; i++)
1324
+ brushes[aliases[i]] = brush;
1325
+ }
1326
+
1327
+ sh.vars.discoveredBrushes = brushes;
1328
+ }
1329
+
1330
+ result = sh.brushes[brushes[alias]];
1331
+
1332
+ if (result == null && showAlert)
1333
+ alert(sh.config.strings.noBrush + alias);
1334
+
1335
+ return result;
1336
+ };
1337
+
1338
+ /**
1339
+ * Executes a callback on each line and replaces each line with result from the callback.
1340
+ * @param {Object} str Input string.
1341
+ * @param {Object} callback Callback function taking one string argument and returning a string.
1342
+ */
1343
+ function eachLine(str, callback)
1344
+ {
1345
+ var lines = splitLines(str);
1346
+
1347
+ for (var i = 0; i < lines.length; i++)
1348
+ lines[i] = callback(lines[i], i);
1349
+
1350
+ // include \r to enable copy-paste on windows (ie8) without getting everything on one line
1351
+ return lines.join('\r\n');
1352
+ };
1353
+
1354
+ /**
1355
+ * This is a special trim which only removes first and last empty lines
1356
+ * and doesn't affect valid leading space on the first line.
1357
+ *
1358
+ * @param {String} str Input string
1359
+ * @return {String} Returns string without empty first and last lines.
1360
+ */
1361
+ function trimFirstAndLastLines(str)
1362
+ {
1363
+ return str.replace(/^[ ]*[\n]+|[\n]*[ ]*$/g, '');
1364
+ };
1365
+
1366
+ /**
1367
+ * Parses key/value pairs into hash object.
1368
+ *
1369
+ * Understands the following formats:
1370
+ * - name: word;
1371
+ * - name: [word, word];
1372
+ * - name: "string";
1373
+ * - name: 'string';
1374
+ *
1375
+ * For example:
1376
+ * name1: value; name2: [value, value]; name3: 'value'
1377
+ *
1378
+ * @param {String} str Input string.
1379
+ * @return {Object} Returns deserialized object.
1380
+ */
1381
+ function parseParams(str)
1382
+ {
1383
+ var match,
1384
+ result = {},
1385
+ arrayRegex = new XRegExp("^\\[(?<values>(.*?))\\]$"),
1386
+ regex = new XRegExp(
1387
+ "(?<name>[\\w-]+)" +
1388
+ "\\s*:\\s*" +
1389
+ "(?<value>" +
1390
+ "[\\w-%#]+|" + // word
1391
+ "\\[.*?\\]|" + // [] array
1392
+ '".*?"|' + // "" string
1393
+ "'.*?'" + // '' string
1394
+ ")\\s*;?",
1395
+ "g"
1396
+ )
1397
+ ;
1398
+
1399
+ while ((match = regex.exec(str)) != null)
1400
+ {
1401
+ var value = match.value
1402
+ .replace(/^['"]|['"]$/g, '') // strip quotes from end of strings
1403
+ ;
1404
+
1405
+ // try to parse array value
1406
+ if (value != null && arrayRegex.test(value))
1407
+ {
1408
+ var m = arrayRegex.exec(value);
1409
+ value = m.values.length > 0 ? m.values.split(/\s*,\s*/) : [];
1410
+ }
1411
+
1412
+ result[match.name] = value;
1413
+ }
1414
+
1415
+ return result;
1416
+ };
1417
+
1418
+ /**
1419
+ * Wraps each line of the string into <code/> tag with given style applied to it.
1420
+ *
1421
+ * @param {String} str Input string.
1422
+ * @param {String} css Style name to apply to the string.
1423
+ * @return {String} Returns input string with each line surrounded by <span/> tag.
1424
+ */
1425
+ function wrapLinesWithCode(str, css)
1426
+ {
1427
+ if (str == null || str.length == 0 || str == '\n')
1428
+ return str;
1429
+
1430
+ str = str.replace(/</g, '&lt;');
1431
+
1432
+ // Replace two or more sequential spaces with &nbsp; leaving last space untouched.
1433
+ str = str.replace(/ {2,}/g, function(m)
1434
+ {
1435
+ var spaces = '';
1436
+
1437
+ for (var i = 0; i < m.length - 1; i++)
1438
+ spaces += sh.config.space;
1439
+
1440
+ return spaces + ' ';
1441
+ });
1442
+
1443
+ // Split each line and apply <span class="...">...</span> to them so that
1444
+ // leading spaces aren't included.
1445
+ if (css != null)
1446
+ str = eachLine(str, function(line)
1447
+ {
1448
+ if (line.length == 0)
1449
+ return '';
1450
+
1451
+ var spaces = '';
1452
+
1453
+ line = line.replace(/^(&nbsp;| )+/, function(s)
1454
+ {
1455
+ spaces = s;
1456
+ return '';
1457
+ });
1458
+
1459
+ if (line.length == 0)
1460
+ return spaces;
1461
+
1462
+ return spaces + '<code class="' + css + '">' + line + '</code>';
1463
+ });
1464
+
1465
+ return str;
1466
+ };
1467
+
1468
+ /**
1469
+ * Pads number with zeros until it's length is the same as given length.
1470
+ *
1471
+ * @param {Number} number Number to pad.
1472
+ * @param {Number} length Max string length with.
1473
+ * @return {String} Returns a string padded with proper amount of '0'.
1474
+ */
1475
+ function padNumber(number, length)
1476
+ {
1477
+ var result = number.toString();
1478
+
1479
+ while (result.length < length)
1480
+ result = '0' + result;
1481
+
1482
+ return result;
1483
+ };
1484
+
1485
+ /**
1486
+ * Replaces tabs with spaces.
1487
+ *
1488
+ * @param {String} code Source code.
1489
+ * @param {Number} tabSize Size of the tab.
1490
+ * @return {String} Returns code with all tabs replaces by spaces.
1491
+ */
1492
+ function processTabs(code, tabSize)
1493
+ {
1494
+ var tab = '';
1495
+
1496
+ for (var i = 0; i < tabSize; i++)
1497
+ tab += ' ';
1498
+
1499
+ return code.replace(/\t/g, tab);
1500
+ };
1501
+
1502
+ /**
1503
+ * Replaces tabs with smart spaces.
1504
+ *
1505
+ * @param {String} code Code to fix the tabs in.
1506
+ * @param {Number} tabSize Number of spaces in a column.
1507
+ * @return {String} Returns code with all tabs replaces with roper amount of spaces.
1508
+ */
1509
+ function processSmartTabs(code, tabSize)
1510
+ {
1511
+ var lines = splitLines(code),
1512
+ tab = '\t',
1513
+ spaces = ''
1514
+ ;
1515
+
1516
+ // Create a string with 1000 spaces to copy spaces from...
1517
+ // It's assumed that there would be no indentation longer than that.
1518
+ for (var i = 0; i < 50; i++)
1519
+ spaces += ' '; // 20 spaces * 50
1520
+
1521
+ // This function inserts specified amount of spaces in the string
1522
+ // where a tab is while removing that given tab.
1523
+ function insertSpaces(line, pos, count)
1524
+ {
1525
+ return line.substr(0, pos)
1526
+ + spaces.substr(0, count)
1527
+ + line.substr(pos + 1, line.length) // pos + 1 will get rid of the tab
1528
+ ;
1529
+ };
1530
+
1531
+ // Go through all the lines and do the 'smart tabs' magic.
1532
+ code = eachLine(code, function(line)
1533
+ {
1534
+ if (line.indexOf(tab) == -1)
1535
+ return line;
1536
+
1537
+ var pos = 0;
1538
+
1539
+ while ((pos = line.indexOf(tab)) != -1)
1540
+ {
1541
+ // This is pretty much all there is to the 'smart tabs' logic.
1542
+ // Based on the position within the line and size of a tab,
1543
+ // calculate the amount of spaces we need to insert.
1544
+ var spaces = tabSize - pos % tabSize;
1545
+ line = insertSpaces(line, pos, spaces);
1546
+ }
1547
+
1548
+ return line;
1549
+ });
1550
+
1551
+ return code;
1552
+ };
1553
+
1554
+ /**
1555
+ * Performs various string fixes based on configuration.
1556
+ */
1557
+ function fixInputString(str)
1558
+ {
1559
+ var br = /<br\s*\/?>|&lt;br\s*\/?&gt;/gi;
1560
+
1561
+ if (sh.config.bloggerMode == true)
1562
+ str = str.replace(br, '\n');
1563
+
1564
+ if (sh.config.stripBrs == true)
1565
+ str = str.replace(br, '');
1566
+
1567
+ return str;
1568
+ };
1569
+
1570
+ /**
1571
+ * Removes all white space at the begining and end of a string.
1572
+ *
1573
+ * @param {String} str String to trim.
1574
+ * @return {String} Returns string without leading and following white space characters.
1575
+ */
1576
+ function trim(str)
1577
+ {
1578
+ return str.replace(/^\s+|\s+$/g, '');
1579
+ };
1580
+
1581
+ /**
1582
+ * Unindents a block of text by the lowest common indent amount.
1583
+ * @param {String} str Text to unindent.
1584
+ * @return {String} Returns unindented text block.
1585
+ */
1586
+ function unindent(str)
1587
+ {
1588
+ var lines = splitLines(fixInputString(str)),
1589
+ indents = new Array(),
1590
+ regex = /^\s*/,
1591
+ min = 1000
1592
+ ;
1593
+
1594
+ // go through every line and check for common number of indents
1595
+ for (var i = 0; i < lines.length && min > 0; i++)
1596
+ {
1597
+ var line = lines[i];
1598
+
1599
+ if (trim(line).length == 0)
1600
+ continue;
1601
+
1602
+ var matches = regex.exec(line);
1603
+
1604
+ // In the event that just one line doesn't have leading white space
1605
+ // we can't unindent anything, so bail completely.
1606
+ if (matches == null)
1607
+ return str;
1608
+
1609
+ min = Math.min(matches[0].length, min);
1610
+ }
1611
+
1612
+ // trim minimum common number of white space from the begining of every line
1613
+ if (min > 0)
1614
+ for (var i = 0; i < lines.length; i++)
1615
+ lines[i] = lines[i].substr(min);
1616
+
1617
+ return lines.join('\n');
1618
+ };
1619
+
1620
+ /**
1621
+ * Callback method for Array.sort() which sorts matches by
1622
+ * index position and then by length.
1623
+ *
1624
+ * @param {Match} m1 Left object.
1625
+ * @param {Match} m2 Right object.
1626
+ * @return {Number} Returns -1, 0 or -1 as a comparison result.
1627
+ */
1628
+ function matchesSortCallback(m1, m2)
1629
+ {
1630
+ // sort matches by index first
1631
+ if(m1.index < m2.index)
1632
+ return -1;
1633
+ else if(m1.index > m2.index)
1634
+ return 1;
1635
+ else
1636
+ {
1637
+ // if index is the same, sort by length
1638
+ if(m1.length < m2.length)
1639
+ return -1;
1640
+ else if(m1.length > m2.length)
1641
+ return 1;
1642
+ }
1643
+
1644
+ return 0;
1645
+ };
1646
+
1647
+ /**
1648
+ * Executes given regular expression on provided code and returns all
1649
+ * matches that are found.
1650
+ *
1651
+ * @param {String} code Code to execute regular expression on.
1652
+ * @param {Object} regex Regular expression item info from <code>regexList</code> collection.
1653
+ * @return {Array} Returns a list of Match objects.
1654
+ */
1655
+ function getMatches(code, regexInfo)
1656
+ {
1657
+ function defaultAdd(match, regexInfo)
1658
+ {
1659
+ return match[0];
1660
+ };
1661
+
1662
+ var index = 0,
1663
+ match = null,
1664
+ matches = [],
1665
+ func = regexInfo.func ? regexInfo.func : defaultAdd
1666
+ ;
1667
+
1668
+ while((match = regexInfo.regex.exec(code)) != null)
1669
+ {
1670
+ var resultMatch = func(match, regexInfo);
1671
+
1672
+ if (typeof(resultMatch) == 'string')
1673
+ resultMatch = [new sh.Match(resultMatch, match.index, regexInfo.css)];
1674
+
1675
+ matches = matches.concat(resultMatch);
1676
+ }
1677
+
1678
+ return matches;
1679
+ };
1680
+
1681
+ /**
1682
+ * Turns all URLs in the code into <a/> tags.
1683
+ * @param {String} code Input code.
1684
+ * @return {String} Returns code with </a> tags.
1685
+ */
1686
+ function processUrls(code)
1687
+ {
1688
+ var gt = /(.*)((&gt;|&lt;).*)/;
1689
+
1690
+ return code.replace(sh.regexLib.url, function(m)
1691
+ {
1692
+ var suffix = '',
1693
+ match = null
1694
+ ;
1695
+
1696
+ // We include &lt; and &gt; in the URL for the common cases like <http://google.com>
1697
+ // The problem is that they get transformed into &lt;http://google.com&gt;
1698
+ // Where as &gt; easily looks like part of the URL string.
1699
+
1700
+ if (match = gt.exec(m))
1701
+ {
1702
+ m = match[1];
1703
+ suffix = match[2];
1704
+ }
1705
+
1706
+ return '<a href="' + m + '">' + m + '</a>' + suffix;
1707
+ });
1708
+ };
1709
+
1710
+ /**
1711
+ * Finds all <SCRIPT TYPE="syntaxhighlighter" /> elementss.
1712
+ * @return {Array} Returns array of all found SyntaxHighlighter tags.
1713
+ */
1714
+ function getSyntaxHighlighterScriptTags()
1715
+ {
1716
+ var tags = document.getElementsByTagName('script'),
1717
+ result = []
1718
+ ;
1719
+
1720
+ for (var i = 0; i < tags.length; i++)
1721
+ if (tags[i].type == 'syntaxhighlighter')
1722
+ result.push(tags[i]);
1723
+
1724
+ return result;
1725
+ };
1726
+
1727
+ /**
1728
+ * Strips <![CDATA[]]> from <SCRIPT /> content because it should be used
1729
+ * there in most cases for XHTML compliance.
1730
+ * @param {String} original Input code.
1731
+ * @return {String} Returns code without leading <![CDATA[]]> tags.
1732
+ */
1733
+ function stripCData(original)
1734
+ {
1735
+ var left = '<![CDATA[',
1736
+ right = ']]>',
1737
+ // for some reason IE inserts some leading blanks here
1738
+ copy = trim(original),
1739
+ changed = false,
1740
+ leftLength = left.length,
1741
+ rightLength = right.length
1742
+ ;
1743
+
1744
+ if (copy.indexOf(left) == 0)
1745
+ {
1746
+ copy = copy.substring(leftLength);
1747
+ changed = true;
1748
+ }
1749
+
1750
+ var copyLength = copy.length;
1751
+
1752
+ if (copy.indexOf(right) == copyLength - rightLength)
1753
+ {
1754
+ copy = copy.substring(0, copyLength - rightLength);
1755
+ changed = true;
1756
+ }
1757
+
1758
+ return changed ? copy : original;
1759
+ };
1760
+
1761
+
1762
+ /**
1763
+ * Quick code mouse double click handler.
1764
+ */
1765
+ function quickCodeHandler(e)
1766
+ {
1767
+ var target = e.target,
1768
+ highlighterDiv = findParentElement(target, '.syntaxhighlighter'),
1769
+ container = findParentElement(target, '.container'),
1770
+ textarea = document.createElement('textarea'),
1771
+ highlighter
1772
+ ;
1773
+
1774
+ if (!container || !highlighterDiv || findElement(container, 'textarea'))
1775
+ return;
1776
+
1777
+ highlighter = getHighlighterById(highlighterDiv.id);
1778
+
1779
+ // add source class name
1780
+ addClass(highlighterDiv, 'source');
1781
+
1782
+ // Have to go over each line and grab it's text, can't just do it on the
1783
+ // container because Firefox loses all \n where as Webkit doesn't.
1784
+ var lines = container.childNodes,
1785
+ code = []
1786
+ ;
1787
+
1788
+ for (var i = 0; i < lines.length; i++)
1789
+ code.push(lines[i].innerText || lines[i].textContent);
1790
+
1791
+ // using \r instead of \r or \r\n makes this work equally well on IE, FF and Webkit
1792
+ code = code.join('\r');
1793
+
1794
+ // For Webkit browsers, replace nbsp with a breaking space
1795
+ code = code.replace(/\u00a0/g, " ");
1796
+
1797
+ // inject <textarea/> tag
1798
+ textarea.appendChild(document.createTextNode(code));
1799
+ container.appendChild(textarea);
1800
+
1801
+ // preselect all text
1802
+ textarea.focus();
1803
+ textarea.select();
1804
+
1805
+ // set up handler for lost focus
1806
+ attachEvent(textarea, 'blur', function(e)
1807
+ {
1808
+ textarea.parentNode.removeChild(textarea);
1809
+ removeClass(highlighterDiv, 'source');
1810
+ });
1811
+ };
1812
+
1813
+ /**
1814
+ * Match object.
1815
+ */
1816
+ sh.Match = function(value, index, css)
1817
+ {
1818
+ this.value = value;
1819
+ this.index = index;
1820
+ this.length = value.length;
1821
+ this.css = css;
1822
+ this.brushName = null;
1823
+ };
1824
+
1825
+ sh.Match.prototype.toString = function()
1826
+ {
1827
+ return this.value;
1828
+ };
1829
+
1830
+ /**
1831
+ * Simulates HTML code with a scripting language embedded.
1832
+ *
1833
+ * @param {String} scriptBrushName Brush name of the scripting language.
1834
+ */
1835
+ sh.HtmlScript = function(scriptBrushName)
1836
+ {
1837
+ var brushClass = findBrush(scriptBrushName),
1838
+ scriptBrush,
1839
+ xmlBrush = new sh.brushes.Xml(),
1840
+ bracketsRegex = null,
1841
+ ref = this,
1842
+ methodsToExpose = 'getDiv getHtml init'.split(' ')
1843
+ ;
1844
+
1845
+ if (brushClass == null)
1846
+ return;
1847
+
1848
+ scriptBrush = new brushClass();
1849
+
1850
+ for(var i = 0; i < methodsToExpose.length; i++)
1851
+ // make a closure so we don't lose the name after i changes
1852
+ (function() {
1853
+ var name = methodsToExpose[i];
1854
+
1855
+ ref[name] = function()
1856
+ {
1857
+ return xmlBrush[name].apply(xmlBrush, arguments);
1858
+ };
1859
+ })();
1860
+
1861
+ if (scriptBrush.htmlScript == null)
1862
+ {
1863
+ alert(sh.config.strings.brushNotHtmlScript + scriptBrushName);
1864
+ return;
1865
+ }
1866
+
1867
+ xmlBrush.regexList.push(
1868
+ { regex: scriptBrush.htmlScript.code, func: process }
1869
+ );
1870
+
1871
+ function offsetMatches(matches, offset)
1872
+ {
1873
+ for (var j = 0; j < matches.length; j++)
1874
+ matches[j].index += offset;
1875
+ }
1876
+
1877
+ function process(match, info)
1878
+ {
1879
+ var code = match.code,
1880
+ matches = [],
1881
+ regexList = scriptBrush.regexList,
1882
+ offset = match.index + match.left.length,
1883
+ htmlScript = scriptBrush.htmlScript,
1884
+ result
1885
+ ;
1886
+
1887
+ // add all matches from the code
1888
+ for (var i = 0; i < regexList.length; i++)
1889
+ {
1890
+ result = getMatches(code, regexList[i]);
1891
+ offsetMatches(result, offset);
1892
+ matches = matches.concat(result);
1893
+ }
1894
+
1895
+ // add left script bracket
1896
+ if (htmlScript.left != null && match.left != null)
1897
+ {
1898
+ result = getMatches(match.left, htmlScript.left);
1899
+ offsetMatches(result, match.index);
1900
+ matches = matches.concat(result);
1901
+ }
1902
+
1903
+ // add right script bracket
1904
+ if (htmlScript.right != null && match.right != null)
1905
+ {
1906
+ result = getMatches(match.right, htmlScript.right);
1907
+ offsetMatches(result, match.index + match[0].lastIndexOf(match.right));
1908
+ matches = matches.concat(result);
1909
+ }
1910
+
1911
+ for (var j = 0; j < matches.length; j++)
1912
+ matches[j].brushName = brushClass.brushName;
1913
+
1914
+ return matches;
1915
+ }
1916
+ };
1917
+
1918
+ /**
1919
+ * Main Highlither class.
1920
+ * @constructor
1921
+ */
1922
+ sh.Highlighter = function()
1923
+ {
1924
+ // not putting any code in here because of the prototype inheritance
1925
+ };
1926
+
1927
+ sh.Highlighter.prototype = {
1928
+ /**
1929
+ * Returns value of the parameter passed to the highlighter.
1930
+ * @param {String} name Name of the parameter.
1931
+ * @param {Object} defaultValue Default value.
1932
+ * @return {Object} Returns found value or default value otherwise.
1933
+ */
1934
+ getParam: function(name, defaultValue)
1935
+ {
1936
+ var result = this.params[name];
1937
+ return toBoolean(result == null ? defaultValue : result);
1938
+ },
1939
+
1940
+ /**
1941
+ * Shortcut to document.createElement().
1942
+ * @param {String} name Name of the element to create (DIV, A, etc).
1943
+ * @return {HTMLElement} Returns new HTML element.
1944
+ */
1945
+ create: function(name)
1946
+ {
1947
+ return document.createElement(name);
1948
+ },
1949
+
1950
+ /**
1951
+ * Applies all regular expression to the code and stores all found
1952
+ * matches in the `this.matches` array.
1953
+ * @param {Array} regexList List of regular expressions.
1954
+ * @param {String} code Source code.
1955
+ * @return {Array} Returns list of matches.
1956
+ */
1957
+ findMatches: function(regexList, code)
1958
+ {
1959
+ var result = [];
1960
+
1961
+ if (regexList != null)
1962
+ for (var i = 0; i < regexList.length; i++)
1963
+ // BUG: length returns len+1 for array if methods added to prototype chain (oising@gmail.com)
1964
+ if (typeof (regexList[i]) == "object")
1965
+ result = result.concat(getMatches(code, regexList[i]));
1966
+
1967
+ // sort and remove nested the matches
1968
+ return this.removeNestedMatches(result.sort(matchesSortCallback));
1969
+ },
1970
+
1971
+ /**
1972
+ * Checks to see if any of the matches are inside of other matches.
1973
+ * This process would get rid of highligted strings inside comments,
1974
+ * keywords inside strings and so on.
1975
+ */
1976
+ removeNestedMatches: function(matches)
1977
+ {
1978
+ // Optimized by Jose Prado (http://joseprado.com)
1979
+ for (var i = 0; i < matches.length; i++)
1980
+ {
1981
+ if (matches[i] === null)
1982
+ continue;
1983
+
1984
+ var itemI = matches[i],
1985
+ itemIEndPos = itemI.index + itemI.length
1986
+ ;
1987
+
1988
+ for (var j = i + 1; j < matches.length && matches[i] !== null; j++)
1989
+ {
1990
+ var itemJ = matches[j];
1991
+
1992
+ if (itemJ === null)
1993
+ continue;
1994
+ else if (itemJ.index > itemIEndPos)
1995
+ break;
1996
+ else if (itemJ.index == itemI.index && itemJ.length > itemI.length)
1997
+ matches[i] = null;
1998
+ else if (itemJ.index >= itemI.index && itemJ.index < itemIEndPos)
1999
+ matches[j] = null;
2000
+ }
2001
+ }
2002
+
2003
+ return matches;
2004
+ },
2005
+
2006
+ /**
2007
+ * Creates an array containing integer line numbers starting from the 'first-line' param.
2008
+ * @return {Array} Returns array of integers.
2009
+ */
2010
+ figureOutLineNumbers: function(code)
2011
+ {
2012
+ var lines = [],
2013
+ firstLine = parseInt(this.getParam('first-line'))
2014
+ ;
2015
+
2016
+ eachLine(code, function(line, index)
2017
+ {
2018
+ lines.push(index + firstLine);
2019
+ });
2020
+
2021
+ return lines;
2022
+ },
2023
+
2024
+ /**
2025
+ * Determines if specified line number is in the highlighted list.
2026
+ */
2027
+ isLineHighlighted: function(lineNumber)
2028
+ {
2029
+ var list = this.getParam('highlight', []);
2030
+
2031
+ if (typeof(list) != 'object' && list.push == null)
2032
+ list = [ list ];
2033
+
2034
+ return indexOf(list, lineNumber.toString()) != -1;
2035
+ },
2036
+
2037
+ /**
2038
+ * Generates HTML markup for a single line of code while determining alternating line style.
2039
+ * @param {Integer} lineNumber Line number.
2040
+ * @param {String} code Line HTML markup.
2041
+ * @return {String} Returns HTML markup.
2042
+ */
2043
+ getLineHtml: function(lineIndex, lineNumber, code)
2044
+ {
2045
+ var classes = [
2046
+ 'line',
2047
+ 'number' + lineNumber,
2048
+ 'index' + lineIndex,
2049
+ 'alt' + (lineNumber % 2 == 0 ? 1 : 2).toString()
2050
+ ];
2051
+
2052
+ if (this.isLineHighlighted(lineNumber))
2053
+ classes.push('highlighted');
2054
+
2055
+ if (lineNumber == 0)
2056
+ classes.push('break');
2057
+
2058
+ return '<div class="' + classes.join(' ') + '">' + code + '</div>';
2059
+ },
2060
+
2061
+ /**
2062
+ * Generates HTML markup for line number column.
2063
+ * @param {String} code Complete code HTML markup.
2064
+ * @param {Array} lineNumbers Calculated line numbers.
2065
+ * @return {String} Returns HTML markup.
2066
+ */
2067
+ getLineNumbersHtml: function(code, lineNumbers)
2068
+ {
2069
+ var html = '',
2070
+ count = splitLines(code).length,
2071
+ firstLine = parseInt(this.getParam('first-line')),
2072
+ pad = this.getParam('pad-line-numbers')
2073
+ ;
2074
+
2075
+ if (pad == true)
2076
+ pad = (firstLine + count - 1).toString().length;
2077
+ else if (isNaN(pad) == true)
2078
+ pad = 0;
2079
+
2080
+ for (var i = 0; i < count; i++)
2081
+ {
2082
+ var lineNumber = lineNumbers ? lineNumbers[i] : firstLine + i,
2083
+ code = lineNumber == 0 ? sh.config.space : padNumber(lineNumber, pad)
2084
+ ;
2085
+
2086
+ html += this.getLineHtml(i, lineNumber, code);
2087
+ }
2088
+
2089
+ return html;
2090
+ },
2091
+
2092
+ /**
2093
+ * Splits block of text into individual DIV lines.
2094
+ * @param {String} code Code to highlight.
2095
+ * @param {Array} lineNumbers Calculated line numbers.
2096
+ * @return {String} Returns highlighted code in HTML form.
2097
+ */
2098
+ getCodeLinesHtml: function(html, lineNumbers)
2099
+ {
2100
+ html = trim(html);
2101
+
2102
+ var lines = splitLines(html),
2103
+ padLength = this.getParam('pad-line-numbers'),
2104
+ firstLine = parseInt(this.getParam('first-line')),
2105
+ html = '',
2106
+ brushName = this.getParam('brush')
2107
+ ;
2108
+
2109
+ for (var i = 0; i < lines.length; i++)
2110
+ {
2111
+ var line = lines[i],
2112
+ indent = /^(&nbsp;|\s)+/.exec(line),
2113
+ spaces = null,
2114
+ lineNumber = lineNumbers ? lineNumbers[i] : firstLine + i;
2115
+ ;
2116
+
2117
+ if (indent != null)
2118
+ {
2119
+ spaces = indent[0].toString();
2120
+ line = line.substr(spaces.length);
2121
+ spaces = spaces.replace(' ', sh.config.space);
2122
+ }
2123
+
2124
+ line = trim(line);
2125
+
2126
+ if (line.length == 0)
2127
+ line = sh.config.space;
2128
+
2129
+ html += this.getLineHtml(
2130
+ i,
2131
+ lineNumber,
2132
+ (spaces != null ? '<code class="' + brushName + ' spaces">' + spaces + '</code>' : '') + line
2133
+ );
2134
+ }
2135
+
2136
+ return html;
2137
+ },
2138
+
2139
+ /**
2140
+ * Returns HTML for the table title or empty string if title is null.
2141
+ */
2142
+ getTitleHtml: function(title)
2143
+ {
2144
+ return title ? '<caption>' + title + '</caption>' : '';
2145
+ },
2146
+
2147
+ /**
2148
+ * Finds all matches in the source code.
2149
+ * @param {String} code Source code to process matches in.
2150
+ * @param {Array} matches Discovered regex matches.
2151
+ * @return {String} Returns formatted HTML with processed mathes.
2152
+ */
2153
+ getMatchesHtml: function(code, matches)
2154
+ {
2155
+ var pos = 0,
2156
+ result = '',
2157
+ brushName = this.getParam('brush', '')
2158
+ ;
2159
+
2160
+ function getBrushNameCss(match)
2161
+ {
2162
+ var result = match ? (match.brushName || brushName) : brushName;
2163
+ return result ? result + ' ' : '';
2164
+ };
2165
+
2166
+ // Finally, go through the final list of matches and pull the all
2167
+ // together adding everything in between that isn't a match.
2168
+ for (var i = 0; i < matches.length; i++)
2169
+ {
2170
+ var match = matches[i],
2171
+ matchBrushName
2172
+ ;
2173
+
2174
+ if (match === null || match.length === 0)
2175
+ continue;
2176
+
2177
+ matchBrushName = getBrushNameCss(match);
2178
+
2179
+ result += wrapLinesWithCode(code.substr(pos, match.index - pos), matchBrushName + 'plain')
2180
+ + wrapLinesWithCode(match.value, matchBrushName + match.css)
2181
+ ;
2182
+
2183
+ pos = match.index + match.length + (match.offset || 0);
2184
+ }
2185
+
2186
+ // don't forget to add whatever's remaining in the string
2187
+ result += wrapLinesWithCode(code.substr(pos), getBrushNameCss() + 'plain');
2188
+
2189
+ return result;
2190
+ },
2191
+
2192
+ /**
2193
+ * Generates HTML markup for the whole syntax highlighter.
2194
+ * @param {String} code Source code.
2195
+ * @return {String} Returns HTML markup.
2196
+ */
2197
+ getHtml: function(code)
2198
+ {
2199
+ var html = '',
2200
+ classes = [ 'syntaxhighlighter' ],
2201
+ tabSize,
2202
+ matches,
2203
+ lineNumbers
2204
+ ;
2205
+
2206
+ // process light mode
2207
+ if (this.getParam('light') == true)
2208
+ this.params.toolbar = this.params.gutter = false;
2209
+
2210
+ className = 'syntaxhighlighter';
2211
+
2212
+ if (this.getParam('collapse') == true)
2213
+ classes.push('collapsed');
2214
+
2215
+ if ((gutter = this.getParam('gutter')) == false)
2216
+ classes.push('nogutter');
2217
+
2218
+ // add custom user style name
2219
+ classes.push(this.getParam('class-name'));
2220
+
2221
+ // add brush alias to the class name for custom CSS
2222
+ classes.push(this.getParam('brush'));
2223
+
2224
+ code = trimFirstAndLastLines(code)
2225
+ .replace(/\r/g, ' ') // IE lets these buggers through
2226
+ ;
2227
+
2228
+ tabSize = this.getParam('tab-size');
2229
+
2230
+ // replace tabs with spaces
2231
+ code = this.getParam('smart-tabs') == true
2232
+ ? processSmartTabs(code, tabSize)
2233
+ : processTabs(code, tabSize)
2234
+ ;
2235
+
2236
+ // unindent code by the common indentation
2237
+ if (this.getParam('unindent'))
2238
+ code = unindent(code);
2239
+
2240
+ if (gutter)
2241
+ lineNumbers = this.figureOutLineNumbers(code);
2242
+
2243
+ // find matches in the code using brushes regex list
2244
+ matches = this.findMatches(this.regexList, code);
2245
+ // processes found matches into the html
2246
+ html = this.getMatchesHtml(code, matches);
2247
+ // finally, split all lines so that they wrap well
2248
+ html = this.getCodeLinesHtml(html, lineNumbers);
2249
+
2250
+ // finally, process the links
2251
+ if (this.getParam('auto-links'))
2252
+ html = processUrls(html);
2253
+
2254
+ if (typeof(navigator) != 'undefined' && navigator.userAgent && navigator.userAgent.match(/MSIE/))
2255
+ classes.push('ie');
2256
+
2257
+ html =
2258
+ '<div id="' + getHighlighterId(this.id) + '" class="' + classes.join(' ') + '">'
2259
+ + (this.getParam('toolbar') ? sh.toolbar.getHtml(this) : '')
2260
+ + '<table border="0" cellpadding="0" cellspacing="0">'
2261
+ + this.getTitleHtml(this.getParam('title'))
2262
+ + '<tbody>'
2263
+ + '<tr>'
2264
+ + (gutter ? '<td class="gutter">' + this.getLineNumbersHtml(code) + '</td>' : '')
2265
+ + '<td class="code">'
2266
+ + '<div class="container">'
2267
+ + html
2268
+ + '</div>'
2269
+ + '</td>'
2270
+ + '</tr>'
2271
+ + '</tbody>'
2272
+ + '</table>'
2273
+ + '</div>'
2274
+ ;
2275
+
2276
+ return html;
2277
+ },
2278
+
2279
+ /**
2280
+ * Highlights the code and returns complete HTML.
2281
+ * @param {String} code Code to highlight.
2282
+ * @return {Element} Returns container DIV element with all markup.
2283
+ */
2284
+ getDiv: function(code)
2285
+ {
2286
+ if (code === null)
2287
+ code = '';
2288
+
2289
+ this.code = code;
2290
+
2291
+ var div = this.create('div');
2292
+
2293
+ // create main HTML
2294
+ div.innerHTML = this.getHtml(code);
2295
+
2296
+ // set up click handlers
2297
+ if (this.getParam('toolbar'))
2298
+ attachEvent(findElement(div, '.toolbar'), 'click', sh.toolbar.handler);
2299
+
2300
+ if (this.getParam('quick-code'))
2301
+ attachEvent(findElement(div, '.code'), 'dblclick', quickCodeHandler);
2302
+
2303
+ return div;
2304
+ },
2305
+
2306
+ /**
2307
+ * Initializes the highlighter/brush.
2308
+ *
2309
+ * Constructor isn't used for initialization so that nothing executes during necessary
2310
+ * `new SyntaxHighlighter.Highlighter()` call when setting up brush inheritence.
2311
+ *
2312
+ * @param {Hash} params Highlighter parameters.
2313
+ */
2314
+ init: function(params)
2315
+ {
2316
+ this.id = guid();
2317
+
2318
+ // register this instance in the highlighters list
2319
+ storeHighlighter(this);
2320
+
2321
+ // local params take precedence over defaults
2322
+ this.params = merge(sh.defaults, params || {})
2323
+
2324
+ // process light mode
2325
+ if (this.getParam('light') == true)
2326
+ this.params.toolbar = this.params.gutter = false;
2327
+ },
2328
+
2329
+ /**
2330
+ * Converts space separated list of keywords into a regular expression string.
2331
+ * @param {String} str Space separated keywords.
2332
+ * @return {String} Returns regular expression string.
2333
+ */
2334
+ getKeywords: function(str)
2335
+ {
2336
+ str = str
2337
+ .replace(/^\s+|\s+$/g, '')
2338
+ .replace(/\s+/g, '|')
2339
+ ;
2340
+
2341
+ return '\\b(?:' + str + ')\\b';
2342
+ },
2343
+
2344
+ /**
2345
+ * Makes a brush compatible with the `html-script` functionality.
2346
+ * @param {Object} regexGroup Object containing `left` and `right` regular expressions.
2347
+ */
2348
+ forHtmlScript: function(regexGroup)
2349
+ {
2350
+ var regex = { 'end' : regexGroup.right.source };
2351
+
2352
+ if(regexGroup.eof)
2353
+ regex.end = "(?:(?:" + regex.end + ")|$)";
2354
+
2355
+ this.htmlScript = {
2356
+ left : { regex: regexGroup.left, css: 'script' },
2357
+ right : { regex: regexGroup.right, css: 'script' },
2358
+ code : new XRegExp(
2359
+ "(?<left>" + regexGroup.left.source + ")" +
2360
+ "(?<code>.*?)" +
2361
+ "(?<right>" + regex.end + ")",
2362
+ "sgi"
2363
+ )
2364
+ };
2365
+ }
2366
+ }; // end of Highlighter
2367
+
2368
+ return sh;
2369
+ }(); // end of anonymous function
2370
+
2371
+ // CommonJS