marty 0.5.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (345) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +84 -0
  4. data/Rakefile +29 -0
  5. data/app/assets/javascripts/marty/application.js +15 -0
  6. data/app/assets/stylesheets/marty/application.css +13 -0
  7. data/app/components/marty/api_auth_view.rb +32 -0
  8. data/app/components/marty/auth_app.rb +55 -0
  9. data/app/components/marty/auth_app.rb~ +51 -0
  10. data/app/components/marty/auth_app/javascripts/auth_app.js +91 -0
  11. data/app/components/marty/auth_app/javascripts/auth_app.js~ +91 -0
  12. data/app/components/marty/cm_form_panel.rb~ +5 -0
  13. data/app/components/marty/cm_grid_panel.rb~ +35 -0
  14. data/app/components/marty/data_import_view.rb~ +142 -0
  15. data/app/components/marty/extras/layout.rb +46 -0
  16. data/app/components/marty/extras/layout.rb~ +46 -0
  17. data/app/components/marty/extras/misc.rb +18 -0
  18. data/app/components/marty/form.rb +6 -0
  19. data/app/components/marty/grid.rb +45 -0
  20. data/app/components/marty/grid_append_only.rb +12 -0
  21. data/app/components/marty/import_type_view.rb +53 -0
  22. data/app/components/marty/live_search_grid_panel.rb +46 -0
  23. data/app/components/marty/live_search_grid_panel.rb~ +49 -0
  24. data/app/components/marty/main_auth_app.rb +269 -0
  25. data/app/components/marty/main_auth_app.rb~ +238 -0
  26. data/app/components/marty/mcfly_grid_panel.rb +62 -0
  27. data/app/components/marty/mcfly_grid_panel.rb~ +80 -0
  28. data/app/components/marty/new_posting_form.rb +46 -0
  29. data/app/components/marty/new_posting_form.rb~ +46 -0
  30. data/app/components/marty/new_posting_window.rb +21 -0
  31. data/app/components/marty/new_posting_window.rb~ +21 -0
  32. data/app/components/marty/panel.rb +12 -0
  33. data/app/components/marty/pivot_grid.rb +52 -0
  34. data/app/components/marty/pivot_grid/endpoints.rb +45 -0
  35. data/app/components/marty/pivot_grid/javascripts/extensions.js +150 -0
  36. data/app/components/marty/pivot_grid/javascripts/pivot_grid.js +86 -0
  37. data/app/components/marty/pivot_grid/services.rb +44 -0
  38. data/app/components/marty/posting_grid.rb +139 -0
  39. data/app/components/marty/posting_grid.rb~ +140 -0
  40. data/app/components/marty/posting_window.rb +27 -0
  41. data/app/components/marty/promise_view.rb +177 -0
  42. data/app/components/marty/promise_view.rb~ +157 -0
  43. data/app/components/marty/promise_view/stylesheets/promise_view.css +26 -0
  44. data/app/components/marty/promise_view/stylesheets/promise_view.css~ +15 -0
  45. data/app/components/marty/report_form.rb +225 -0
  46. data/app/components/marty/report_form.rb~ +217 -0
  47. data/app/components/marty/report_select.rb +145 -0
  48. data/app/components/marty/report_select.rb~ +133 -0
  49. data/app/components/marty/reporting.rb +39 -0
  50. data/app/components/marty/reporting.rb~ +39 -0
  51. data/app/components/marty/script_detail.rb~ +430 -0
  52. data/app/components/marty/script_form.rb +233 -0
  53. data/app/components/marty/script_form.rb~ +233 -0
  54. data/app/components/marty/script_form/javascripts/Ext.ux.form.field.CodeMirror.js +698 -0
  55. data/app/components/marty/script_form/javascripts/Ext.ux.form.field.CodeMirror.js~ +909 -0
  56. data/app/components/marty/script_form/javascripts/codemirror.js +3130 -0
  57. data/app/components/marty/script_form/javascripts/mode/clike/clike.js +284 -0
  58. data/app/components/marty/script_form/javascripts/mode/clike/index.html +102 -0
  59. data/app/components/marty/script_form/javascripts/mode/clike/scala.html +766 -0
  60. data/app/components/marty/script_form/javascripts/mode/clojure/clojure.js +206 -0
  61. data/app/components/marty/script_form/javascripts/mode/clojure/index.html +67 -0
  62. data/app/components/marty/script_form/javascripts/mode/coffeescript/LICENSE +22 -0
  63. data/app/components/marty/script_form/javascripts/mode/coffeescript/coffeescript.js +346 -0
  64. data/app/components/marty/script_form/javascripts/mode/coffeescript/index.html +728 -0
  65. data/app/components/marty/script_form/javascripts/mode/commonlisp/commonlisp.js +101 -0
  66. data/app/components/marty/script_form/javascripts/mode/commonlisp/index.html +165 -0
  67. data/app/components/marty/script_form/javascripts/mode/css/css.js +448 -0
  68. data/app/components/marty/script_form/javascripts/mode/css/index.html +58 -0
  69. data/app/components/marty/script_form/javascripts/mode/css/test.js +501 -0
  70. data/app/components/marty/script_form/javascripts/mode/delorean/delorean.js +189 -0
  71. data/app/components/marty/script_form/javascripts/mode/diff/diff.js +32 -0
  72. data/app/components/marty/script_form/javascripts/mode/diff/index.html +105 -0
  73. data/app/components/marty/script_form/javascripts/mode/ecl/ecl.js +203 -0
  74. data/app/components/marty/script_form/javascripts/mode/ecl/index.html +42 -0
  75. data/app/components/marty/script_form/javascripts/mode/erlang/erlang.js +463 -0
  76. data/app/components/marty/script_form/javascripts/mode/erlang/index.html +63 -0
  77. data/app/components/marty/script_form/javascripts/mode/gfm/gfm.js +150 -0
  78. data/app/components/marty/script_form/javascripts/mode/gfm/index.html +48 -0
  79. data/app/components/marty/script_form/javascripts/mode/go/go.js +170 -0
  80. data/app/components/marty/script_form/javascripts/mode/go/index.html +73 -0
  81. data/app/components/marty/script_form/javascripts/mode/groovy/groovy.js +210 -0
  82. data/app/components/marty/script_form/javascripts/mode/groovy/index.html +72 -0
  83. data/app/components/marty/script_form/javascripts/mode/haskell/haskell.js +242 -0
  84. data/app/components/marty/script_form/javascripts/mode/haskell/index.html +61 -0
  85. data/app/components/marty/script_form/javascripts/mode/haxe/haxe.js +429 -0
  86. data/app/components/marty/script_form/javascripts/mode/haxe/index.html +91 -0
  87. data/app/components/marty/script_form/javascripts/mode/htmlembedded/htmlembedded.js +72 -0
  88. data/app/components/marty/script_form/javascripts/mode/htmlembedded/index.html +50 -0
  89. data/app/components/marty/script_form/javascripts/mode/htmlmixed/htmlmixed.js +84 -0
  90. data/app/components/marty/script_form/javascripts/mode/htmlmixed/index.html +52 -0
  91. data/app/components/marty/script_form/javascripts/mode/javascript/index.html +78 -0
  92. data/app/components/marty/script_form/javascripts/mode/javascript/javascript.js +361 -0
  93. data/app/components/marty/script_form/javascripts/mode/jinja2/index.html +38 -0
  94. data/app/components/marty/script_form/javascripts/mode/jinja2/jinja2.js +42 -0
  95. data/app/components/marty/script_form/javascripts/mode/less/index.html +740 -0
  96. data/app/components/marty/script_form/javascripts/mode/less/less.js +266 -0
  97. data/app/components/marty/script_form/javascripts/mode/lua/index.html +73 -0
  98. data/app/components/marty/script_form/javascripts/mode/lua/lua.js +140 -0
  99. data/app/components/marty/script_form/javascripts/mode/markdown/index.html +343 -0
  100. data/app/components/marty/script_form/javascripts/mode/markdown/markdown.js +382 -0
  101. data/app/components/marty/script_form/javascripts/mode/markdown/test.js +1084 -0
  102. data/app/components/marty/script_form/javascripts/mode/mysql/index.html +42 -0
  103. data/app/components/marty/script_form/javascripts/mode/mysql/mysql.js +186 -0
  104. data/app/components/marty/script_form/javascripts/mode/ntriples/index.html +33 -0
  105. data/app/components/marty/script_form/javascripts/mode/ntriples/ntriples.js +172 -0
  106. data/app/components/marty/script_form/javascripts/mode/ocaml/index.html +130 -0
  107. data/app/components/marty/script_form/javascripts/mode/ocaml/ocaml.js +114 -0
  108. data/app/components/marty/script_form/javascripts/mode/pascal/LICENSE +7 -0
  109. data/app/components/marty/script_form/javascripts/mode/pascal/index.html +49 -0
  110. data/app/components/marty/script_form/javascripts/mode/pascal/pascal.js +94 -0
  111. data/app/components/marty/script_form/javascripts/mode/perl/LICENSE +19 -0
  112. data/app/components/marty/script_form/javascripts/mode/perl/index.html +63 -0
  113. data/app/components/marty/script_form/javascripts/mode/perl/perl.js +816 -0
  114. data/app/components/marty/script_form/javascripts/mode/php/index.html +49 -0
  115. data/app/components/marty/script_form/javascripts/mode/php/php.js +148 -0
  116. data/app/components/marty/script_form/javascripts/mode/pig/index.html +43 -0
  117. data/app/components/marty/script_form/javascripts/mode/pig/pig.js +172 -0
  118. data/app/components/marty/script_form/javascripts/mode/plsql/index.html +63 -0
  119. data/app/components/marty/script_form/javascripts/mode/plsql/plsql.js +217 -0
  120. data/app/components/marty/script_form/javascripts/mode/properties/index.html +41 -0
  121. data/app/components/marty/script_form/javascripts/mode/properties/properties.js +63 -0
  122. data/app/components/marty/script_form/javascripts/mode/python/LICENSE.txt +21 -0
  123. data/app/components/marty/script_form/javascripts/mode/python/index.html +123 -0
  124. data/app/components/marty/script_form/javascripts/mode/python/python.js +338 -0
  125. data/app/components/marty/script_form/javascripts/mode/r/LICENSE +24 -0
  126. data/app/components/marty/script_form/javascripts/mode/r/index.html +74 -0
  127. data/app/components/marty/script_form/javascripts/mode/r/r.js +141 -0
  128. data/app/components/marty/script_form/javascripts/mode/rpm/changes/changes.js +19 -0
  129. data/app/components/marty/script_form/javascripts/mode/rpm/changes/index.html +54 -0
  130. data/app/components/marty/script_form/javascripts/mode/rpm/spec/index.html +100 -0
  131. data/app/components/marty/script_form/javascripts/mode/rpm/spec/spec.css +5 -0
  132. data/app/components/marty/script_form/javascripts/mode/rpm/spec/spec.js +66 -0
  133. data/app/components/marty/script_form/javascripts/mode/rst/index.html +526 -0
  134. data/app/components/marty/script_form/javascripts/mode/rst/rst.js +326 -0
  135. data/app/components/marty/script_form/javascripts/mode/ruby/LICENSE +24 -0
  136. data/app/components/marty/script_form/javascripts/mode/ruby/index.html +172 -0
  137. data/app/components/marty/script_form/javascripts/mode/ruby/ruby.js +195 -0
  138. data/app/components/marty/script_form/javascripts/mode/rust/index.html +49 -0
  139. data/app/components/marty/script_form/javascripts/mode/rust/rust.js +432 -0
  140. data/app/components/marty/script_form/javascripts/mode/scheme/index.html +65 -0
  141. data/app/components/marty/script_form/javascripts/mode/scheme/scheme.js +230 -0
  142. data/app/components/marty/script_form/javascripts/mode/shell/index.html +50 -0
  143. data/app/components/marty/script_form/javascripts/mode/shell/shell.js +118 -0
  144. data/app/components/marty/script_form/javascripts/mode/sieve/LICENSE +23 -0
  145. data/app/components/marty/script_form/javascripts/mode/sieve/index.html +81 -0
  146. data/app/components/marty/script_form/javascripts/mode/sieve/sieve.js +156 -0
  147. data/app/components/marty/script_form/javascripts/mode/smalltalk/index.html +56 -0
  148. data/app/components/marty/script_form/javascripts/mode/smalltalk/smalltalk.js +139 -0
  149. data/app/components/marty/script_form/javascripts/mode/smarty/index.html +83 -0
  150. data/app/components/marty/script_form/javascripts/mode/smarty/smarty.js +148 -0
  151. data/app/components/marty/script_form/javascripts/mode/sparql/index.html +41 -0
  152. data/app/components/marty/script_form/javascripts/mode/sparql/sparql.js +143 -0
  153. data/app/components/marty/script_form/javascripts/mode/stex/index.html +98 -0
  154. data/app/components/marty/script_form/javascripts/mode/stex/stex.js +182 -0
  155. data/app/components/marty/script_form/javascripts/mode/stex/test.js +343 -0
  156. data/app/components/marty/script_form/javascripts/mode/tiddlywiki/index.html +141 -0
  157. data/app/components/marty/script_form/javascripts/mode/tiddlywiki/tiddlywiki.css +14 -0
  158. data/app/components/marty/script_form/javascripts/mode/tiddlywiki/tiddlywiki.js +384 -0
  159. data/app/components/marty/script_form/javascripts/mode/tiki/index.html +83 -0
  160. data/app/components/marty/script_form/javascripts/mode/tiki/tiki.css +26 -0
  161. data/app/components/marty/script_form/javascripts/mode/tiki/tiki.js +309 -0
  162. data/app/components/marty/script_form/javascripts/mode/vb/LICENSE.txt +21 -0
  163. data/app/components/marty/script_form/javascripts/mode/vb/index.html +89 -0
  164. data/app/components/marty/script_form/javascripts/mode/vb/vb.js +260 -0
  165. data/app/components/marty/script_form/javascripts/mode/vbscript/index.html +43 -0
  166. data/app/components/marty/script_form/javascripts/mode/vbscript/vbscript.js +26 -0
  167. data/app/components/marty/script_form/javascripts/mode/velocity/index.html +104 -0
  168. data/app/components/marty/script_form/javascripts/mode/velocity/velocity.js +146 -0
  169. data/app/components/marty/script_form/javascripts/mode/verilog/index.html +211 -0
  170. data/app/components/marty/script_form/javascripts/mode/verilog/verilog.js +194 -0
  171. data/app/components/marty/script_form/javascripts/mode/xml/index.html +45 -0
  172. data/app/components/marty/script_form/javascripts/mode/xml/xml.js +318 -0
  173. data/app/components/marty/script_form/javascripts/mode/xquery/LICENSE +20 -0
  174. data/app/components/marty/script_form/javascripts/mode/xquery/index.html +223 -0
  175. data/app/components/marty/script_form/javascripts/mode/xquery/test/index.html +27 -0
  176. data/app/components/marty/script_form/javascripts/mode/xquery/test/testBase.js +42 -0
  177. data/app/components/marty/script_form/javascripts/mode/xquery/test/testEmptySequenceKeyword.js +16 -0
  178. data/app/components/marty/script_form/javascripts/mode/xquery/test/testMultiAttr.js +16 -0
  179. data/app/components/marty/script_form/javascripts/mode/xquery/test/testNamespaces.js +91 -0
  180. data/app/components/marty/script_form/javascripts/mode/xquery/test/testProcessingInstructions.js +16 -0
  181. data/app/components/marty/script_form/javascripts/mode/xquery/test/testQuotes.js +19 -0
  182. data/app/components/marty/script_form/javascripts/mode/xquery/xquery.js +451 -0
  183. data/app/components/marty/script_form/javascripts/mode/yaml/index.html +68 -0
  184. data/app/components/marty/script_form/javascripts/mode/yaml/yaml.js +95 -0
  185. data/app/components/marty/script_form/javascripts/util/closetag.js +164 -0
  186. data/app/components/marty/script_form/javascripts/util/dialog.css +27 -0
  187. data/app/components/marty/script_form/javascripts/util/dialog.js +70 -0
  188. data/app/components/marty/script_form/javascripts/util/foldcode.js +196 -0
  189. data/app/components/marty/script_form/javascripts/util/formatting.js +193 -0
  190. data/app/components/marty/script_form/javascripts/util/javascript-hint.js +134 -0
  191. data/app/components/marty/script_form/javascripts/util/loadmode.js +51 -0
  192. data/app/components/marty/script_form/javascripts/util/match-highlighter.js +44 -0
  193. data/app/components/marty/script_form/javascripts/util/multiplex.js +77 -0
  194. data/app/components/marty/script_form/javascripts/util/overlay.js +54 -0
  195. data/app/components/marty/script_form/javascripts/util/pig-hint.js +123 -0
  196. data/app/components/marty/script_form/javascripts/util/runmode-standalone.js +90 -0
  197. data/app/components/marty/script_form/javascripts/util/runmode.js +53 -0
  198. data/app/components/marty/script_form/javascripts/util/search.js +118 -0
  199. data/app/components/marty/script_form/javascripts/util/searchcursor.js +119 -0
  200. data/app/components/marty/script_form/javascripts/util/simple-hint.css +16 -0
  201. data/app/components/marty/script_form/javascripts/util/simple-hint.js +97 -0
  202. data/app/components/marty/script_form/javascripts/util/xml-hint.js +137 -0
  203. data/app/components/marty/script_form/stylesheets/codemirror.css +172 -0
  204. data/app/components/marty/script_form/stylesheets/delorean.css +10 -0
  205. data/app/components/marty/script_form/stylesheets/theme/ambiance.css +81 -0
  206. data/app/components/marty/script_form/stylesheets/theme/blackboard.css +25 -0
  207. data/app/components/marty/script_form/stylesheets/theme/cobalt.css +18 -0
  208. data/app/components/marty/script_form/stylesheets/theme/eclipse.css +25 -0
  209. data/app/components/marty/script_form/stylesheets/theme/elegant.css +10 -0
  210. data/app/components/marty/script_form/stylesheets/theme/erlang-dark.css +21 -0
  211. data/app/components/marty/script_form/stylesheets/theme/lesser-dark.css +44 -0
  212. data/app/components/marty/script_form/stylesheets/theme/monokai.css +28 -0
  213. data/app/components/marty/script_form/stylesheets/theme/neat.css +9 -0
  214. data/app/components/marty/script_form/stylesheets/theme/night.css +21 -0
  215. data/app/components/marty/script_form/stylesheets/theme/rubyblue.css +21 -0
  216. data/app/components/marty/script_form/stylesheets/theme/vibrant-ink.css +27 -0
  217. data/app/components/marty/script_form/stylesheets/theme/xq-dark.css +46 -0
  218. data/app/components/marty/script_grid.rb +104 -0
  219. data/app/components/marty/script_grid.rb~ +99 -0
  220. data/app/components/marty/script_tester.rb +114 -0
  221. data/app/components/marty/script_tester.rb~ +213 -0
  222. data/app/components/marty/scripting.rb +132 -0
  223. data/app/components/marty/scripting.rb~ +124 -0
  224. data/app/components/marty/select_report.rb~ +143 -0
  225. data/app/components/marty/simple_app.rb +97 -0
  226. data/app/components/marty/simple_app.rb~ +101 -0
  227. data/app/components/marty/simple_app/javascripts/simple_app.js +50 -0
  228. data/app/components/marty/simple_app/javascripts/statusbar_ext.js +8 -0
  229. data/app/components/marty/tag_grid.rb +83 -0
  230. data/app/components/marty/tag_grid.rb~ +89 -0
  231. data/app/components/marty/tree_panel.rb~ +256 -0
  232. data/app/components/marty/tree_panel/javascripts/tree_panel.js~ +317 -0
  233. data/app/components/marty/user_pivot.rb +128 -0
  234. data/app/components/marty/user_view.rb +181 -0
  235. data/app/components/marty/user_view.rb~ +188 -0
  236. data/app/controllers/marty/application_controller.rb +124 -0
  237. data/app/controllers/marty/application_controller.rb~ +133 -0
  238. data/app/controllers/marty/components_controller.rb +41 -0
  239. data/app/controllers/marty/components_controller.rb~ +37 -0
  240. data/app/controllers/marty/job_controller.rb +28 -0
  241. data/app/controllers/marty/job_controller.rb~ +28 -0
  242. data/app/controllers/marty/rpc_controller.rb +64 -0
  243. data/app/controllers/marty/rpc_controller.rb~ +61 -0
  244. data/app/helpers/marty/application_helper.rb +4 -0
  245. data/app/helpers/marty/script_set.rb +57 -0
  246. data/app/helpers/marty/script_set.rb~ +59 -0
  247. data/app/models/marty/api_auth.rb +44 -0
  248. data/app/models/marty/api_auth.rb~ +48 -0
  249. data/app/models/marty/base.rb +4 -0
  250. data/app/models/marty/data_change.rb +179 -0
  251. data/app/models/marty/data_change.rb~ +141 -0
  252. data/app/models/marty/enum.rb +22 -0
  253. data/app/models/marty/enum.rb~ +16 -0
  254. data/app/models/marty/import_type.rb +44 -0
  255. data/app/models/marty/import_type.rb~ +48 -0
  256. data/app/models/marty/poop.rb~ +169 -0
  257. data/app/models/marty/posting.rb +101 -0
  258. data/app/models/marty/posting.rb~ +86 -0
  259. data/app/models/marty/posting_type.rb +12 -0
  260. data/app/models/marty/posting_type.rb~ +21 -0
  261. data/app/models/marty/promise.rb +252 -0
  262. data/app/models/marty/promise.rb~ +196 -0
  263. data/app/models/marty/role.rb +6 -0
  264. data/app/models/marty/role.rb~ +10 -0
  265. data/app/models/marty/script.rb +144 -0
  266. data/app/models/marty/script.rb~ +62 -0
  267. data/app/models/marty/tag.rb +96 -0
  268. data/app/models/marty/tag.rb~ +91 -0
  269. data/app/models/marty/token.rb +30 -0
  270. data/app/models/marty/user.rb +146 -0
  271. data/app/models/marty/user.rb~ +148 -0
  272. data/app/models/marty/user_role.rb +7 -0
  273. data/app/models/marty/user_role.rb~ +13 -0
  274. data/app/views/layouts/marty/application.html.erb +12 -0
  275. data/app/views/layouts/marty/application.html.erb~ +11 -0
  276. data/config/locales/en.yml +134 -0
  277. data/config/routes.rb +6 -0
  278. data/config/routes.rb~ +10 -0
  279. data/db/migrate/001_create_marty_scripts.rb +14 -0
  280. data/db/migrate/003_create_marty_users.rb +12 -0
  281. data/db/migrate/004_create_marty_roles.rb +7 -0
  282. data/db/migrate/005_create_marty_user_roles.rb +14 -0
  283. data/db/migrate/006_create_marty_tokens.rb +14 -0
  284. data/db/migrate/008_create_marty_posting_types.rb +7 -0
  285. data/db/migrate/019_create_marty_postings.rb +18 -0
  286. data/db/migrate/019_create_marty_postings.rb~ +19 -0
  287. data/db/migrate/068_create_marty_import_types.rb +12 -0
  288. data/db/migrate/069_create_marty_import_synonyms.rb +15 -0
  289. data/db/migrate/070_create_versions.rb +18 -0
  290. data/db/migrate/071_add_object_changes_column_to_versions.rb +9 -0
  291. data/db/migrate/072_add_validation_function_to_import_types.rb +6 -0
  292. data/db/migrate/073_add_preprocess_function_to_import_types.rb +5 -0
  293. data/db/migrate/090_create_delayed_jobs.rb +22 -0
  294. data/db/migrate/091_create_marty_promises.rb +36 -0
  295. data/db/migrate/095_create_marty_tags.rb +14 -0
  296. data/db/migrate/095_create_marty_tags.rb~ +19 -0
  297. data/db/migrate/096_add_user_roles_to_import_types.rb +11 -0
  298. data/db/migrate/097_drop_versions.rb +9 -0
  299. data/db/migrate/098_create_marty_api_auths.rb +20 -0
  300. data/db/seeds.rb +48 -0
  301. data/lib/marty.rb +18 -0
  302. data/lib/marty.rb~ +13 -0
  303. data/lib/marty/content_handler.rb +97 -0
  304. data/lib/marty/content_handler.rb~ +93 -0
  305. data/lib/marty/data_conversion.rb +298 -0
  306. data/lib/marty/data_exporter.rb +150 -0
  307. data/lib/marty/data_exporter.rb~ +137 -0
  308. data/lib/marty/data_importer.rb +122 -0
  309. data/lib/marty/data_importer.rb~ +114 -0
  310. data/lib/marty/data_row_processor.rb~ +206 -0
  311. data/lib/marty/drop_folder_hook.rb~ +17 -0
  312. data/lib/marty/engine.rb +10 -0
  313. data/lib/marty/folder_hook.rb~ +9 -0
  314. data/lib/marty/lazy_column_loader.rb +57 -0
  315. data/lib/marty/lazy_column_loader.rb~ +47 -0
  316. data/lib/marty/mcfly_query.rb +189 -0
  317. data/lib/marty/mcfly_query.rb~ +188 -0
  318. data/lib/marty/migrations.rb +108 -0
  319. data/lib/marty/migrations.rb~ +65 -0
  320. data/lib/marty/monkey.rb +163 -0
  321. data/lib/marty/monkey.rb~ +160 -0
  322. data/lib/marty/permissions.rb +64 -0
  323. data/lib/marty/permissions.rb~ +69 -0
  324. data/lib/marty/promise.rb~ +41 -0
  325. data/lib/marty/promise_job.rb +123 -0
  326. data/lib/marty/promise_job.rb~ +121 -0
  327. data/lib/marty/promise_proxy.rb +94 -0
  328. data/lib/marty/promise_proxy.rb~ +69 -0
  329. data/lib/marty/railtie.rb +5 -0
  330. data/lib/marty/relation.rb +39 -0
  331. data/lib/marty/util.rb +110 -0
  332. data/lib/marty/util.rb~ +80 -0
  333. data/lib/marty/version.rb +3 -0
  334. data/lib/marty/version.rb~ +3 -0
  335. data/lib/marty/xl.rb +527 -0
  336. data/lib/marty/xl.rb~ +526 -0
  337. data/lib/pyxll/README.txt +19 -0
  338. data/lib/pyxll/README.txt~ +16 -0
  339. data/lib/pyxll/gemini.py +155 -0
  340. data/lib/pyxll/gemini.py~ +110 -0
  341. data/lib/pyxll/pyxll.cfg +12 -0
  342. data/lib/pyxll/pyxll.cfg~ +12 -0
  343. data/lib/pyxll/sample.xlsx +0 -0
  344. data/lib/tasks/marty_tasks.rake +37 -0
  345. metadata +517 -0
@@ -0,0 +1,3130 @@
1
+ // All functions that need access to the editor's state live inside
2
+ // the CodeMirror function. Below that, at the bottom of the file,
3
+ // some utilities are defined.
4
+
5
+ // CodeMirror is the only global var we claim
6
+ window.CodeMirror = (function() {
7
+ "use strict";
8
+ // This is the function that produces an editor instance. Its
9
+ // closure is used to store the editor state.
10
+ function CodeMirror(place, givenOptions) {
11
+ // Determine effective options based on given values and defaults.
12
+ var options = {}, defaults = CodeMirror.defaults;
13
+ for (var opt in defaults)
14
+ if (defaults.hasOwnProperty(opt))
15
+ options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
16
+
17
+ var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em");
18
+ input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
19
+ // Wraps and hides input textarea
20
+ var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
21
+ // The empty scrollbar content, used solely for managing the scrollbar thumb.
22
+ var scrollbarInner = elt("div", null, "CodeMirror-scrollbar-inner");
23
+ // The vertical scrollbar. Horizontal scrolling is handled by the scroller itself.
24
+ var scrollbar = elt("div", [scrollbarInner], "CodeMirror-scrollbar");
25
+ // DIVs containing the selection and the actual code
26
+ var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1");
27
+ // Blinky cursor, and element used to ensure cursor fits at the end of a line
28
+ var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"), widthForcer = elt("pre", "\u00a0", "CodeMirror-cursor", "visibility: hidden");
29
+ // Used to measure text size
30
+ var measure = elt("div", null, null, "position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden;");
31
+ var lineSpace = elt("div", [measure, cursor, widthForcer, selectionDiv, lineDiv], null, "position: relative; z-index: 0");
32
+ var gutterText = elt("div", null, "CodeMirror-gutter-text"), gutter = elt("div", [gutterText], "CodeMirror-gutter");
33
+ // Moved around its parent to cover visible view
34
+ var mover = elt("div", [gutter, elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative");
35
+ // Set to the height of the text, causes scrolling
36
+ var sizer = elt("div", [mover], null, "position: relative");
37
+ // Provides scrolling
38
+ var scroller = elt("div", [sizer], "CodeMirror-scroll");
39
+ scroller.setAttribute("tabIndex", "-1");
40
+ // The element in which the editor lives.
41
+ var wrapper = elt("div", [inputDiv, scrollbar, scroller], "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : ""));
42
+ if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
43
+
44
+ themeChanged(); keyMapChanged();
45
+ // Needed to hide big blue blinking cursor on Mobile Safari
46
+ if (ios) input.style.width = "0px";
47
+ if (!webkit) scroller.draggable = true;
48
+ lineSpace.style.outline = "none";
49
+ if (options.tabindex != null) input.tabIndex = options.tabindex;
50
+ if (options.autofocus) focusInput();
51
+ if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
52
+ // Needed to handle Tab key in KHTML
53
+ if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute";
54
+
55
+ // Check for OS X >= 10.7. This has transparent scrollbars, so the
56
+ // overlaying of one scrollbar with another won't work. This is a
57
+ // temporary hack to simply turn off the overlay scrollbar. See
58
+ // issue #727.
59
+ if (mac_geLion) { scrollbar.style.zIndex = -2; scrollbar.style.visibility = "hidden"; }
60
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
61
+ else if (ie_lt8) scrollbar.style.minWidth = "18px";
62
+
63
+ // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
64
+ var poll = new Delayed(), highlight = new Delayed(), blinker;
65
+
66
+ // mode holds a mode API object. doc is the tree of Line objects,
67
+ // frontier is the point up to which the content has been parsed,
68
+ // and history the undo history (instance of History constructor).
69
+ var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), frontier = 0, focused;
70
+ loadMode();
71
+ // The selection. These are always maintained to point at valid
72
+ // positions. Inverted is used to remember that the user is
73
+ // selecting bottom-to-top.
74
+ var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
75
+ // Selection-related flags. shiftSelecting obviously tracks
76
+ // whether the user is holding shift.
77
+ var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, draggingText,
78
+ overwrite = false, suppressEdits = false;
79
+ // Variables used by startOperation/endOperation to track what
80
+ // happened during the operation.
81
+ var updateInput, userSelChange, changes, textChanged, selectionChanged,
82
+ gutterDirty, callbacks;
83
+ // Current visible range (may be bigger than the view window).
84
+ var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
85
+ // bracketHighlighted is used to remember that a bracket has been
86
+ // marked.
87
+ var bracketHighlighted;
88
+ // Tracks the maximum line length so that the horizontal scrollbar
89
+ // can be kept static when scrolling.
90
+ var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true;
91
+ var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
92
+ var goalColumn = null;
93
+
94
+ // Initialize the content.
95
+ operation(function(){setValue(options.value || ""); updateInput = false;})();
96
+ var history = new History();
97
+
98
+ // Register our event handlers.
99
+ connect(scroller, "mousedown", operation(onMouseDown));
100
+ connect(scroller, "dblclick", operation(onDoubleClick));
101
+ connect(lineSpace, "selectstart", e_preventDefault);
102
+ // Gecko browsers fire contextmenu *after* opening the menu, at
103
+ // which point we can't mess with it anymore. Context menu is
104
+ // handled in onMouseDown for Gecko.
105
+ if (!gecko) connect(scroller, "contextmenu", onContextMenu);
106
+ connect(scroller, "scroll", onScrollMain);
107
+ connect(scrollbar, "scroll", onScrollBar);
108
+ connect(scrollbar, "mousedown", function() {if (focused) setTimeout(focusInput, 0);});
109
+ var resizeHandler = connect(window, "resize", function() {
110
+ if (wrapper.parentNode) updateDisplay(true);
111
+ else resizeHandler();
112
+ }, true);
113
+ connect(input, "keyup", operation(onKeyUp));
114
+ connect(input, "input", fastPoll);
115
+ connect(input, "keydown", operation(onKeyDown));
116
+ connect(input, "keypress", operation(onKeyPress));
117
+ connect(input, "focus", onFocus);
118
+ connect(input, "blur", onBlur);
119
+
120
+ function drag_(e) {
121
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
122
+ e_stop(e);
123
+ }
124
+ if (options.dragDrop) {
125
+ connect(scroller, "dragstart", onDragStart);
126
+ connect(scroller, "dragenter", drag_);
127
+ connect(scroller, "dragover", drag_);
128
+ connect(scroller, "drop", operation(onDrop));
129
+ }
130
+ connect(scroller, "paste", function(){focusInput(); fastPoll();});
131
+ connect(input, "paste", fastPoll);
132
+ connect(input, "cut", operation(function(){
133
+ if (!options.readOnly) replaceSelection("");
134
+ }));
135
+
136
+ // Needed to handle Tab key in KHTML
137
+ if (khtml) connect(sizer, "mouseup", function() {
138
+ if (document.activeElement == input) input.blur();
139
+ focusInput();
140
+ });
141
+
142
+ // IE throws unspecified error in certain cases, when
143
+ // trying to access activeElement before onload
144
+ var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { }
145
+ if (hasFocus || options.autofocus) setTimeout(onFocus, 20);
146
+ else onBlur();
147
+
148
+ function isLine(l) {return l >= 0 && l < doc.size;}
149
+ // The instance object that we'll return. Mostly calls out to
150
+ // local functions in the CodeMirror function. Some do some extra
151
+ // range checking and/or clipping. operation is used to wrap the
152
+ // call so that changes it makes are tracked, and the display is
153
+ // updated afterwards.
154
+ var instance = wrapper.CodeMirror = {
155
+ getValue: getValue,
156
+ setValue: operation(setValue),
157
+ getSelection: getSelection,
158
+ replaceSelection: operation(replaceSelection),
159
+ focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();},
160
+ setOption: function(option, value) {
161
+ var oldVal = options[option];
162
+ options[option] = value;
163
+ if (option == "mode" || option == "indentUnit") loadMode();
164
+ else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();}
165
+ else if (option == "readOnly" && !value) {resetInput(true);}
166
+ else if (option == "theme") themeChanged();
167
+ else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
168
+ else if (option == "tabSize") updateDisplay(true);
169
+ else if (option == "keyMap") keyMapChanged();
170
+ if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" ||
171
+ option == "theme" || option == "lineNumberFormatter") {
172
+ gutterChanged();
173
+ updateDisplay(true);
174
+ }
175
+ },
176
+ getOption: function(option) {return options[option];},
177
+ getMode: function() {return mode;},
178
+ undo: operation(undo),
179
+ redo: operation(redo),
180
+ indentLine: operation(function(n, dir) {
181
+ if (typeof dir != "string") {
182
+ if (dir == null) dir = options.smartIndent ? "smart" : "prev";
183
+ else dir = dir ? "add" : "subtract";
184
+ }
185
+ if (isLine(n)) indentLine(n, dir);
186
+ }),
187
+ indentSelection: operation(indentSelected),
188
+ historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
189
+ clearHistory: function() {history = new History();},
190
+ setHistory: function(histData) {
191
+ history = new History();
192
+ history.done = histData.done;
193
+ history.undone = histData.undone;
194
+ },
195
+ getHistory: function() {
196
+ history.time = 0;
197
+ return {done: history.done.concat([]), undone: history.undone.concat([])};
198
+ },
199
+ matchBrackets: operation(function(){matchBrackets(true);}),
200
+ getTokenAt: operation(function(pos) {
201
+ pos = clipPos(pos);
202
+ return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), options.tabSize, pos.ch);
203
+ }),
204
+ getStateAfter: function(line) {
205
+ line = clipLine(line == null ? doc.size - 1: line);
206
+ return getStateBefore(line + 1);
207
+ },
208
+ cursorCoords: function(start, mode) {
209
+ if (start == null) start = sel.inverted;
210
+ return this.charCoords(start ? sel.from : sel.to, mode);
211
+ },
212
+ charCoords: function(pos, mode) {
213
+ pos = clipPos(pos);
214
+ if (mode == "local") return localCoords(pos, false);
215
+ if (mode == "div") return localCoords(pos, true);
216
+ return pageCoords(pos);
217
+ },
218
+ coordsChar: function(coords) {
219
+ var off = eltOffset(lineSpace);
220
+ return coordsChar(coords.x - off.left, coords.y - off.top);
221
+ },
222
+ markText: operation(markText),
223
+ setBookmark: setBookmark,
224
+ findMarksAt: findMarksAt,
225
+ setMarker: operation(addGutterMarker),
226
+ clearMarker: operation(removeGutterMarker),
227
+ setLineClass: operation(setLineClass),
228
+ hideLine: operation(function(h) {return setLineHidden(h, true);}),
229
+ showLine: operation(function(h) {return setLineHidden(h, false);}),
230
+ onDeleteLine: function(line, f) {
231
+ if (typeof line == "number") {
232
+ if (!isLine(line)) return null;
233
+ line = getLine(line);
234
+ }
235
+ (line.handlers || (line.handlers = [])).push(f);
236
+ return line;
237
+ },
238
+ lineInfo: lineInfo,
239
+ getViewport: function() { return {from: showingFrom, to: showingTo};},
240
+ addWidget: function(pos, node, scroll, vert, horiz) {
241
+ pos = localCoords(clipPos(pos));
242
+ var top = pos.yBot, left = pos.x;
243
+ node.style.position = "absolute";
244
+ sizer.appendChild(node);
245
+ if (vert == "over") top = pos.y;
246
+ else if (vert == "near") {
247
+ var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
248
+ hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth) - paddingLeft();
249
+ if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
250
+ top = pos.y - node.offsetHeight;
251
+ if (left + node.offsetWidth > hspace)
252
+ left = hspace - node.offsetWidth;
253
+ }
254
+ node.style.top = (top + paddingTop()) + "px";
255
+ node.style.left = node.style.right = "";
256
+ if (horiz == "right") {
257
+ left = sizer.clientWidth - node.offsetWidth;
258
+ node.style.right = "0px";
259
+ } else {
260
+ if (horiz == "left") left = 0;
261
+ else if (horiz == "middle") left = (sizer.clientWidth - node.offsetWidth) / 2;
262
+ node.style.left = (left + paddingLeft()) + "px";
263
+ }
264
+ if (scroll)
265
+ scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
266
+ },
267
+
268
+ lineCount: function() {return doc.size;},
269
+ clipPos: clipPos,
270
+ getCursor: function(start) {
271
+ if (start == null) start = sel.inverted;
272
+ return copyPos(start ? sel.from : sel.to);
273
+ },
274
+ somethingSelected: function() {return !posEq(sel.from, sel.to);},
275
+ setCursor: operation(function(line, ch, user) {
276
+ if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user);
277
+ else setCursor(line, ch, user);
278
+ }),
279
+ setSelection: operation(function(from, to, user) {
280
+ (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from));
281
+ }),
282
+ getLine: function(line) {if (isLine(line)) return getLine(line).text;},
283
+ getLineHandle: function(line) {if (isLine(line)) return getLine(line);},
284
+ setLine: operation(function(line, text) {
285
+ if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
286
+ }),
287
+ removeLine: operation(function(line) {
288
+ if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
289
+ }),
290
+ replaceRange: operation(replaceRange),
291
+ getRange: function(from, to, lineSep) {return getRange(clipPos(from), clipPos(to), lineSep);},
292
+
293
+ triggerOnKeyDown: operation(onKeyDown),
294
+ execCommand: function(cmd) {return commands[cmd](instance);},
295
+ // Stuff used by commands, probably not much use to outside code.
296
+ moveH: operation(moveH),
297
+ deleteH: operation(deleteH),
298
+ moveV: operation(moveV),
299
+ toggleOverwrite: function() {
300
+ if(overwrite){
301
+ overwrite = false;
302
+ cursor.className = cursor.className.replace(" CodeMirror-overwrite", "");
303
+ } else {
304
+ overwrite = true;
305
+ cursor.className += " CodeMirror-overwrite";
306
+ }
307
+ },
308
+
309
+ posFromIndex: function(off) {
310
+ var lineNo = 0, ch;
311
+ doc.iter(0, doc.size, function(line) {
312
+ var sz = line.text.length + 1;
313
+ if (sz > off) { ch = off; return true; }
314
+ off -= sz;
315
+ ++lineNo;
316
+ });
317
+ return clipPos({line: lineNo, ch: ch});
318
+ },
319
+ indexFromPos: function (coords) {
320
+ if (coords.line < 0 || coords.ch < 0) return 0;
321
+ var index = coords.ch;
322
+ doc.iter(0, coords.line, function (line) {
323
+ index += line.text.length + 1;
324
+ });
325
+ return index;
326
+ },
327
+ scrollTo: function(x, y) {
328
+ if (x != null) scroller.scrollLeft = x;
329
+ if (y != null) scrollbar.scrollTop = scroller.scrollTop = y;
330
+ updateDisplay([]);
331
+ },
332
+ getScrollInfo: function() {
333
+ return {x: scroller.scrollLeft, y: scrollbar.scrollTop,
334
+ height: scrollbar.scrollHeight, width: scroller.scrollWidth};
335
+ },
336
+ setSize: function(width, height) {
337
+ function interpret(val) {
338
+ val = String(val);
339
+ return /^\d+$/.test(val) ? val + "px" : val;
340
+ }
341
+ if (width != null) wrapper.style.width = interpret(width);
342
+ if (height != null) scroller.style.height = interpret(height);
343
+ instance.refresh();
344
+ },
345
+
346
+ operation: function(f){return operation(f)();},
347
+ compoundChange: function(f){return compoundChange(f);},
348
+ refresh: function(){
349
+ updateDisplay(true, null, lastScrollTop);
350
+ if (scrollbar.scrollHeight > lastScrollTop)
351
+ scrollbar.scrollTop = lastScrollTop;
352
+ },
353
+ getInputField: function(){return input;},
354
+ getWrapperElement: function(){return wrapper;},
355
+ getScrollerElement: function(){return scroller;},
356
+ getGutterElement: function(){return gutter;}
357
+ };
358
+
359
+ function getLine(n) { return getLineAt(doc, n); }
360
+ function updateLineHeight(line, height) {
361
+ gutterDirty = true;
362
+ var diff = height - line.height;
363
+ for (var n = line; n; n = n.parent) n.height += diff;
364
+ }
365
+
366
+ function lineContent(line, wrapAt) {
367
+ if (!line.styles)
368
+ line.highlight(mode, line.stateAfter = getStateBefore(lineNo(line)), options.tabSize);
369
+ return line.getContent(options.tabSize, wrapAt, options.lineWrapping);
370
+ }
371
+
372
+ function setValue(code) {
373
+ var top = {line: 0, ch: 0};
374
+ updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
375
+ splitLines(code), top, top);
376
+ updateInput = true;
377
+ }
378
+ function getValue(lineSep) {
379
+ var text = [];
380
+ doc.iter(0, doc.size, function(line) { text.push(line.text); });
381
+ return text.join(lineSep || "\n");
382
+ }
383
+
384
+ function onScrollBar(e) {
385
+ if (scrollbar.scrollTop != lastScrollTop) {
386
+ lastScrollTop = scroller.scrollTop = scrollbar.scrollTop;
387
+ updateDisplay([]);
388
+ }
389
+ }
390
+
391
+ function onScrollMain(e) {
392
+ if (options.fixedGutter && gutter.style.left != scroller.scrollLeft + "px")
393
+ gutter.style.left = scroller.scrollLeft + "px";
394
+ if (scroller.scrollTop != lastScrollTop) {
395
+ lastScrollTop = scroller.scrollTop;
396
+ if (scrollbar.scrollTop != lastScrollTop)
397
+ scrollbar.scrollTop = lastScrollTop;
398
+ updateDisplay([]);
399
+ }
400
+ if (options.onScroll) options.onScroll(instance);
401
+ }
402
+
403
+ function onMouseDown(e) {
404
+ setShift(e_prop(e, "shiftKey"));
405
+ // Check whether this is a click in a widget
406
+ for (var n = e_target(e); n != wrapper; n = n.parentNode)
407
+ if (n.parentNode == sizer && n != mover) return;
408
+
409
+ // See if this is a click in the gutter
410
+ for (var n = e_target(e); n != wrapper; n = n.parentNode)
411
+ if (n.parentNode == gutterText) {
412
+ if (options.onGutterClick)
413
+ options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
414
+ return e_preventDefault(e);
415
+ }
416
+
417
+ var start = posFromMouse(e);
418
+
419
+ switch (e_button(e)) {
420
+ case 3:
421
+ if (gecko) onContextMenu(e);
422
+ return;
423
+ case 2:
424
+ if (start) setCursor(start.line, start.ch, true);
425
+ setTimeout(focusInput, 20);
426
+ e_preventDefault(e);
427
+ return;
428
+ }
429
+ // For button 1, if it was clicked inside the editor
430
+ // (posFromMouse returning non-null), we have to adjust the
431
+ // selection.
432
+ if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
433
+
434
+ if (!focused) onFocus();
435
+
436
+ var now = +new Date, type = "single";
437
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
438
+ type = "triple";
439
+ e_preventDefault(e);
440
+ setTimeout(focusInput, 20);
441
+ selectLine(start.line);
442
+ } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
443
+ type = "double";
444
+ lastDoubleClick = {time: now, pos: start};
445
+ e_preventDefault(e);
446
+ var word = findWordAt(start);
447
+ setSelectionUser(word.from, word.to);
448
+ } else { lastClick = {time: now, pos: start}; }
449
+
450
+ function dragEnd(e2) {
451
+ if (webkit) scroller.draggable = false;
452
+ draggingText = false;
453
+ up(); drop();
454
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
455
+ e_preventDefault(e2);
456
+ setCursor(start.line, start.ch, true);
457
+ focusInput();
458
+ }
459
+ }
460
+ var last = start, going;
461
+ if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
462
+ !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
463
+ // Let the drag handler handle this.
464
+ if (webkit) scroller.draggable = true;
465
+ var up = connect(document, "mouseup", operation(dragEnd), true);
466
+ var drop = connect(scroller, "drop", operation(dragEnd), true);
467
+ draggingText = true;
468
+ // IE's approach to draggable
469
+ if (scroller.dragDrop) scroller.dragDrop();
470
+ return;
471
+ }
472
+ e_preventDefault(e);
473
+ if (type == "single") setCursor(start.line, start.ch, true);
474
+
475
+ var startstart = sel.from, startend = sel.to;
476
+
477
+ function doSelect(cur) {
478
+ if (type == "single") {
479
+ setSelectionUser(start, cur);
480
+ } else if (type == "double") {
481
+ var word = findWordAt(cur);
482
+ if (posLess(cur, startstart)) setSelectionUser(word.from, startend);
483
+ else setSelectionUser(startstart, word.to);
484
+ } else if (type == "triple") {
485
+ if (posLess(cur, startstart)) setSelectionUser(startend, clipPos({line: cur.line, ch: 0}));
486
+ else setSelectionUser(startstart, clipPos({line: cur.line + 1, ch: 0}));
487
+ }
488
+ }
489
+
490
+ function extend(e) {
491
+ var cur = posFromMouse(e, true);
492
+ if (cur && !posEq(cur, last)) {
493
+ if (!focused) onFocus();
494
+ last = cur;
495
+ doSelect(cur);
496
+ updateInput = false;
497
+ var visible = visibleLines();
498
+ if (cur.line >= visible.to || cur.line < visible.from)
499
+ going = setTimeout(operation(function(){extend(e);}), 150);
500
+ }
501
+ }
502
+
503
+ function done(e) {
504
+ clearTimeout(going);
505
+ var cur = posFromMouse(e);
506
+ if (cur) doSelect(cur);
507
+ e_preventDefault(e);
508
+ focusInput();
509
+ updateInput = true;
510
+ move(); up();
511
+ }
512
+ var move = connect(document, "mousemove", operation(function(e) {
513
+ clearTimeout(going);
514
+ e_preventDefault(e);
515
+ if (!ie && !e_button(e)) done(e);
516
+ else extend(e);
517
+ }), true);
518
+ var up = connect(document, "mouseup", operation(done), true);
519
+ }
520
+ function onDoubleClick(e) {
521
+ for (var n = e_target(e); n != wrapper; n = n.parentNode)
522
+ if (n.parentNode == gutterText) return e_preventDefault(e);
523
+ e_preventDefault(e);
524
+ }
525
+ function onDrop(e) {
526
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
527
+ e_preventDefault(e);
528
+ var pos = posFromMouse(e, true), files = e.dataTransfer.files;
529
+ if (!pos || options.readOnly) return;
530
+ if (files && files.length && window.FileReader && window.File) {
531
+ var n = files.length, text = Array(n), read = 0;
532
+ var loadFile = function(file, i) {
533
+ var reader = new FileReader;
534
+ reader.onload = function() {
535
+ text[i] = reader.result;
536
+ if (++read == n) {
537
+ pos = clipPos(pos);
538
+ operation(function() {
539
+ var end = replaceRange(text.join(""), pos, pos);
540
+ setSelectionUser(pos, end);
541
+ })();
542
+ }
543
+ };
544
+ reader.readAsText(file);
545
+ };
546
+ for (var i = 0; i < n; ++i) loadFile(files[i], i);
547
+ } else {
548
+ // Don't do a replace if the drop happened inside of the selected text.
549
+ if (draggingText && !(posLess(pos, sel.from) || posLess(sel.to, pos))) return;
550
+ try {
551
+ var text = e.dataTransfer.getData("Text");
552
+ if (text) {
553
+ compoundChange(function() {
554
+ var curFrom = sel.from, curTo = sel.to;
555
+ setSelectionUser(pos, pos);
556
+ if (draggingText) replaceRange("", curFrom, curTo);
557
+ replaceSelection(text);
558
+ focusInput();
559
+ });
560
+ }
561
+ }
562
+ catch(e){}
563
+ }
564
+ }
565
+ function onDragStart(e) {
566
+ var txt = getSelection();
567
+ e.dataTransfer.setData("Text", txt);
568
+
569
+ // Use dummy image instead of default browsers image.
570
+ if (e.dataTransfer.setDragImage)
571
+ e.dataTransfer.setDragImage(elt('img'), 0, 0);
572
+ }
573
+
574
+ function doHandleBinding(bound, dropShift) {
575
+ if (typeof bound == "string") {
576
+ bound = commands[bound];
577
+ if (!bound) return false;
578
+ }
579
+ var prevShift = shiftSelecting;
580
+ try {
581
+ if (options.readOnly) suppressEdits = true;
582
+ if (dropShift) shiftSelecting = null;
583
+ bound(instance);
584
+ } catch(e) {
585
+ if (e != Pass) throw e;
586
+ return false;
587
+ } finally {
588
+ shiftSelecting = prevShift;
589
+ suppressEdits = false;
590
+ }
591
+ return true;
592
+ }
593
+ var maybeTransition;
594
+ function handleKeyBinding(e) {
595
+ // Handle auto keymap transitions
596
+ var startMap = getKeyMap(options.keyMap), next = startMap.auto;
597
+ clearTimeout(maybeTransition);
598
+ if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
599
+ if (getKeyMap(options.keyMap) == startMap) {
600
+ options.keyMap = (next.call ? next.call(null, instance) : next);
601
+ }
602
+ }, 50);
603
+
604
+ var name = keyNames[e_prop(e, "keyCode")], handled = false;
605
+ if (name == null || e.altGraphKey) return false;
606
+ if (e_prop(e, "altKey")) name = "Alt-" + name;
607
+ if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name;
608
+ if (e_prop(e, "metaKey")) name = "Cmd-" + name;
609
+
610
+ var stopped = false;
611
+ function stop() { stopped = true; }
612
+
613
+ if (e_prop(e, "shiftKey")) {
614
+ handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap,
615
+ function(b) {return doHandleBinding(b, true);}, stop)
616
+ || lookupKey(name, options.extraKeys, options.keyMap, function(b) {
617
+ if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b);
618
+ }, stop);
619
+ } else {
620
+ handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding, stop);
621
+ }
622
+ if (stopped) handled = false;
623
+ if (handled) {
624
+ e_preventDefault(e);
625
+ restartBlink();
626
+ if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
627
+ }
628
+ return handled;
629
+ }
630
+ function handleCharBinding(e, ch) {
631
+ var handled = lookupKey("'" + ch + "'", options.extraKeys,
632
+ options.keyMap, function(b) { return doHandleBinding(b, true); });
633
+ if (handled) {
634
+ e_preventDefault(e);
635
+ restartBlink();
636
+ }
637
+ return handled;
638
+ }
639
+
640
+ var lastStoppedKey = null;
641
+ function onKeyDown(e) {
642
+ if (!focused) onFocus();
643
+ if (ie && e.keyCode == 27) { e.returnValue = false; }
644
+ if (pollingFast) { if (readInput()) pollingFast = false; }
645
+ if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
646
+ var code = e_prop(e, "keyCode");
647
+ // IE does strange things with escape.
648
+ setShift(code == 16 || e_prop(e, "shiftKey"));
649
+ // First give onKeyEvent option a chance to handle this.
650
+ var handled = handleKeyBinding(e);
651
+ if (opera) {
652
+ lastStoppedKey = handled ? code : null;
653
+ // Opera has no cut event... we try to at least catch the key combo
654
+ if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey"))
655
+ replaceSelection("");
656
+ }
657
+ }
658
+ function onKeyPress(e) {
659
+ if (pollingFast) readInput();
660
+ if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
661
+ var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
662
+ if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
663
+ if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(e)) return;
664
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
665
+ if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) {
666
+ if (mode.electricChars.indexOf(ch) > -1)
667
+ setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
668
+ }
669
+ if (handleCharBinding(e, ch)) return;
670
+ fastPoll();
671
+ }
672
+ function onKeyUp(e) {
673
+ if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
674
+ if (e_prop(e, "keyCode") == 16) shiftSelecting = null;
675
+ }
676
+
677
+ function onFocus() {
678
+ if (options.readOnly == "nocursor") return;
679
+ if (!focused) {
680
+ if (options.onFocus) options.onFocus(instance);
681
+ focused = true;
682
+ if (scroller.className.search(/\bCodeMirror-focused\b/) == -1)
683
+ scroller.className += " CodeMirror-focused";
684
+ }
685
+ slowPoll();
686
+ restartBlink();
687
+ }
688
+ function onBlur() {
689
+ if (focused) {
690
+ if (options.onBlur) options.onBlur(instance);
691
+ focused = false;
692
+ if (bracketHighlighted)
693
+ operation(function(){
694
+ if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; }
695
+ })();
696
+ scroller.className = scroller.className.replace(" CodeMirror-focused", "");
697
+ }
698
+ clearInterval(blinker);
699
+ setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
700
+ }
701
+
702
+ // Replace the range from from to to by the strings in newText.
703
+ // Afterwards, set the selection to selFrom, selTo.
704
+ function updateLines(from, to, newText, selFrom, selTo) {
705
+ if (suppressEdits) return;
706
+ var old = [];
707
+ doc.iter(from.line, to.line + 1, function(line) {
708
+ old.push(newHL(line.text, line.markedSpans));
709
+ });
710
+ if (history) {
711
+ history.addChange(from.line, newText.length, old);
712
+ while (history.done.length > options.undoDepth) history.done.shift();
713
+ }
714
+ var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
715
+ updateLinesNoUndo(from, to, lines, selFrom, selTo);
716
+ }
717
+ function unredoHelper(from, to) {
718
+ if (!from.length) return;
719
+ var set = from.pop(), out = [];
720
+ for (var i = set.length - 1; i >= 0; i -= 1) {
721
+ var change = set[i];
722
+ var replaced = [], end = change.start + change.added;
723
+ doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
724
+ out.push({start: change.start, added: change.old.length, old: replaced});
725
+ var pos = {line: change.start + change.old.length - 1,
726
+ ch: editEnd(hlText(lst(replaced)), hlText(lst(change.old)))};
727
+ updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length},
728
+ change.old, pos, pos);
729
+ }
730
+ updateInput = true;
731
+ to.push(out);
732
+ }
733
+ function undo() {unredoHelper(history.done, history.undone);}
734
+ function redo() {unredoHelper(history.undone, history.done);}
735
+
736
+ function updateLinesNoUndo(from, to, lines, selFrom, selTo) {
737
+ if (suppressEdits) return;
738
+ var recomputeMaxLength = false, maxLineLength = maxLine.text.length;
739
+ if (!options.lineWrapping)
740
+ doc.iter(from.line, to.line + 1, function(line) {
741
+ if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
742
+ });
743
+ if (from.line != to.line || lines.length > 1) gutterDirty = true;
744
+
745
+ var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
746
+ var lastHL = lst(lines);
747
+
748
+ // First adjust the line structure
749
+ if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
750
+ // This is a whole-line replace. Treated specially to make
751
+ // sure line objects move the way they are supposed to.
752
+ var added = [], prevLine = null;
753
+ for (var i = 0, e = lines.length - 1; i < e; ++i)
754
+ added.push(new Line(hlText(lines[i]), hlSpans(lines[i])));
755
+ lastLine.update(lastLine.text, hlSpans(lastHL));
756
+ if (nlines) doc.remove(from.line, nlines, callbacks);
757
+ if (added.length) doc.insert(from.line, added);
758
+ } else if (firstLine == lastLine) {
759
+ if (lines.length == 1) {
760
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + firstLine.text.slice(to.ch), hlSpans(lines[0]));
761
+ } else {
762
+ for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
763
+ added.push(new Line(hlText(lines[i]), hlSpans(lines[i])));
764
+ added.push(new Line(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL)));
765
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
766
+ doc.insert(from.line + 1, added);
767
+ }
768
+ } else if (lines.length == 1) {
769
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + lastLine.text.slice(to.ch), hlSpans(lines[0]));
770
+ doc.remove(from.line + 1, nlines, callbacks);
771
+ } else {
772
+ var added = [];
773
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
774
+ lastLine.update(hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
775
+ for (var i = 1, e = lines.length - 1; i < e; ++i)
776
+ added.push(new Line(hlText(lines[i]), hlSpans(lines[i])));
777
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks);
778
+ doc.insert(from.line + 1, added);
779
+ }
780
+ if (options.lineWrapping) {
781
+ var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3);
782
+ doc.iter(from.line, from.line + lines.length, function(line) {
783
+ if (line.hidden) return;
784
+ var guess = Math.ceil(line.text.length / perLine) || 1;
785
+ if (guess != line.height) updateLineHeight(line, guess);
786
+ });
787
+ } else {
788
+ doc.iter(from.line, from.line + lines.length, function(line) {
789
+ var l = line.text;
790
+ if (!line.hidden && l.length > maxLineLength) {
791
+ maxLine = line; maxLineLength = l.length; maxLineChanged = true;
792
+ recomputeMaxLength = false;
793
+ }
794
+ });
795
+ if (recomputeMaxLength) updateMaxLine = true;
796
+ }
797
+
798
+ // Adjust frontier, schedule worker
799
+ frontier = Math.min(frontier, from.line);
800
+ startWorker(400);
801
+
802
+ var lendiff = lines.length - nlines - 1;
803
+ // Remember that these lines changed, for updating the display
804
+ changes.push({from: from.line, to: to.line + 1, diff: lendiff});
805
+ if (options.onChange) {
806
+ // Normalize lines to contain only strings, since that's what
807
+ // the change event handler expects
808
+ for (var i = 0; i < lines.length; ++i)
809
+ if (typeof lines[i] != "string") lines[i] = lines[i].text;
810
+ var changeObj = {from: from, to: to, text: lines};
811
+ if (textChanged) {
812
+ for (var cur = textChanged; cur.next; cur = cur.next) {}
813
+ cur.next = changeObj;
814
+ } else textChanged = changeObj;
815
+ }
816
+
817
+ // Update the selection
818
+ function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
819
+ setSelection(clipPos(selFrom), clipPos(selTo),
820
+ updateLine(sel.from.line), updateLine(sel.to.line));
821
+ }
822
+
823
+ function needsScrollbar() {
824
+ var realHeight = doc.height * textHeight() + 2 * paddingTop();
825
+ return realHeight * .99 > scroller.offsetHeight ? realHeight : false;
826
+ }
827
+
828
+ function updateVerticalScroll(scrollTop) {
829
+ var scrollHeight = needsScrollbar();
830
+ scrollbar.style.display = scrollHeight ? "block" : "none";
831
+ if (scrollHeight) {
832
+ scrollbarInner.style.height = sizer.style.minHeight = scrollHeight + "px";
833
+ scrollbar.style.height = scroller.clientHeight + "px";
834
+ if (scrollTop != null) {
835
+ scrollbar.scrollTop = scroller.scrollTop = scrollTop;
836
+ // 'Nudge' the scrollbar to work around a Webkit bug where,
837
+ // in some situations, we'd end up with a scrollbar that
838
+ // reported its scrollTop (and looked) as expected, but
839
+ // *behaved* as if it was still in a previous state (i.e.
840
+ // couldn't scroll up, even though it appeared to be at the
841
+ // bottom).
842
+ if (webkit) setTimeout(function() {
843
+ if (scrollbar.scrollTop != scrollTop) return;
844
+ scrollbar.scrollTop = scrollTop + (scrollTop ? -1 : 1);
845
+ scrollbar.scrollTop = scrollTop;
846
+ }, 0);
847
+ }
848
+ } else {
849
+ sizer.style.minHeight = "";
850
+ }
851
+ // Position the mover div to align with the current virtual scroll position
852
+ mover.style.top = displayOffset * textHeight() + "px";
853
+ }
854
+
855
+ function computeMaxLength() {
856
+ maxLine = getLine(0); maxLineChanged = true;
857
+ var maxLineLength = maxLine.text.length;
858
+ doc.iter(1, doc.size, function(line) {
859
+ var l = line.text;
860
+ if (!line.hidden && l.length > maxLineLength) {
861
+ maxLineLength = l.length; maxLine = line;
862
+ }
863
+ });
864
+ updateMaxLine = false;
865
+ }
866
+
867
+ function replaceRange(code, from, to) {
868
+ from = clipPos(from);
869
+ if (!to) to = from; else to = clipPos(to);
870
+ code = splitLines(code);
871
+ function adjustPos(pos) {
872
+ if (posLess(pos, from)) return pos;
873
+ if (!posLess(to, pos)) return end;
874
+ var line = pos.line + code.length - (to.line - from.line) - 1;
875
+ var ch = pos.ch;
876
+ if (pos.line == to.line)
877
+ ch += lst(code).length - (to.ch - (to.line == from.line ? from.ch : 0));
878
+ return {line: line, ch: ch};
879
+ }
880
+ var end;
881
+ replaceRange1(code, from, to, function(end1) {
882
+ end = end1;
883
+ return {from: adjustPos(sel.from), to: adjustPos(sel.to)};
884
+ });
885
+ return end;
886
+ }
887
+ function replaceSelection(code, collapse) {
888
+ replaceRange1(splitLines(code), sel.from, sel.to, function(end) {
889
+ if (collapse == "end") return {from: end, to: end};
890
+ else if (collapse == "start") return {from: sel.from, to: sel.from};
891
+ else return {from: sel.from, to: end};
892
+ });
893
+ }
894
+ function replaceRange1(code, from, to, computeSel) {
895
+ var endch = code.length == 1 ? code[0].length + from.ch : lst(code).length;
896
+ var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
897
+ updateLines(from, to, code, newSel.from, newSel.to);
898
+ }
899
+
900
+ function getRange(from, to, lineSep) {
901
+ var l1 = from.line, l2 = to.line;
902
+ if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
903
+ var code = [getLine(l1).text.slice(from.ch)];
904
+ doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
905
+ code.push(getLine(l2).text.slice(0, to.ch));
906
+ return code.join(lineSep || "\n");
907
+ }
908
+ function getSelection(lineSep) {
909
+ return getRange(sel.from, sel.to, lineSep);
910
+ }
911
+
912
+ function slowPoll() {
913
+ if (pollingFast) return;
914
+ poll.set(options.pollInterval, function() {
915
+ readInput();
916
+ if (focused) slowPoll();
917
+ });
918
+ }
919
+ function fastPoll() {
920
+ var missed = false;
921
+ pollingFast = true;
922
+ function p() {
923
+ var changed = readInput();
924
+ if (!changed && !missed) {missed = true; poll.set(60, p);}
925
+ else {pollingFast = false; slowPoll();}
926
+ }
927
+ poll.set(20, p);
928
+ }
929
+
930
+ // Previnput is a hack to work with IME. If we reset the textarea
931
+ // on every change, that breaks IME. So we look for changes
932
+ // compared to the previous content instead. (Modern browsers have
933
+ // events that indicate IME taking place, but these are not widely
934
+ // supported or compatible enough yet to rely on.)
935
+ var prevInput = "";
936
+ function readInput() {
937
+ if (!focused || hasSelection(input) || options.readOnly) return false;
938
+ var text = input.value;
939
+ if (text == prevInput) return false;
940
+ if (!nestedOperation) startOperation();
941
+ shiftSelecting = null;
942
+ var same = 0, l = Math.min(prevInput.length, text.length);
943
+ while (same < l && prevInput[same] == text[same]) ++same;
944
+ if (same < prevInput.length)
945
+ sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)};
946
+ else if (overwrite && posEq(sel.from, sel.to))
947
+ sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
948
+ replaceSelection(text.slice(same), "end");
949
+ if (text.length > 1000) { input.value = prevInput = ""; }
950
+ else prevInput = text;
951
+ if (!nestedOperation) endOperation();
952
+ return true;
953
+ }
954
+ function resetInput(user) {
955
+ if (!posEq(sel.from, sel.to)) {
956
+ prevInput = "";
957
+ input.value = getSelection();
958
+ if (focused) selectInput(input);
959
+ } else if (user) prevInput = input.value = "";
960
+ }
961
+
962
+ function focusInput() {
963
+ if (options.readOnly != "nocursor") input.focus();
964
+ }
965
+
966
+ function scrollCursorIntoView() {
967
+ var coords = calculateCursorCoords();
968
+ scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
969
+ if (!focused) return;
970
+ var box = sizer.getBoundingClientRect(), doScroll = null;
971
+ if (coords.y + box.top < 0) doScroll = true;
972
+ else if (coords.y + box.top + textHeight() > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
973
+ if (doScroll != null) {
974
+ var hidden = cursor.style.display == "none";
975
+ if (hidden) {
976
+ cursor.style.display = "";
977
+ cursor.style.left = coords.x + "px";
978
+ cursor.style.top = (coords.y - displayOffset) + "px";
979
+ }
980
+ cursor.scrollIntoView(doScroll);
981
+ if (hidden) cursor.style.display = "none";
982
+ }
983
+ }
984
+ function calculateCursorCoords() {
985
+ var cursor = localCoords(sel.inverted ? sel.from : sel.to);
986
+ var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
987
+ return {x: x, y: cursor.y, yBot: cursor.yBot};
988
+ }
989
+ function scrollIntoView(x1, y1, x2, y2) {
990
+ var scrollPos = calculateScrollPos(x1, y1, x2, y2);
991
+ if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft;}
992
+ if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scroller.scrollTop = scrollPos.scrollTop;}
993
+ }
994
+ function calculateScrollPos(x1, y1, x2, y2) {
995
+ var pl = paddingLeft(), pt = paddingTop();
996
+ y1 += pt; y2 += pt; x1 += pl; x2 += pl;
997
+ var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {};
998
+ var docBottom = needsScrollbar() || Infinity;
999
+ var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
1000
+ if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
1001
+ else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
1002
+
1003
+ var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
1004
+ var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
1005
+ var atLeft = x1 < gutterw + pl + 10;
1006
+ if (x1 < screenleft + gutterw || atLeft) {
1007
+ if (atLeft) x1 = 0;
1008
+ result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
1009
+ } else if (x2 > screenw + screenleft - 3) {
1010
+ result.scrollLeft = x2 + 10 - screenw;
1011
+ }
1012
+ return result;
1013
+ }
1014
+
1015
+ function visibleLines(scrollTop) {
1016
+ var lh = textHeight(), top = (scrollTop != null ? scrollTop : scrollbar.scrollTop) - paddingTop();
1017
+ var fromHeight = Math.max(0, Math.floor(top / lh));
1018
+ var toHeight = Math.ceil((top + scroller.clientHeight) / lh);
1019
+ return {from: lineAtHeight(doc, fromHeight),
1020
+ to: lineAtHeight(doc, toHeight)};
1021
+ }
1022
+ // Uses a set of changes plus the current scroll position to
1023
+ // determine which DOM updates have to be made, and makes the
1024
+ // updates.
1025
+ function updateDisplay(changes, suppressCallback, scrollTop) {
1026
+ if (!scroller.clientWidth) {
1027
+ showingFrom = showingTo = displayOffset = 0;
1028
+ return;
1029
+ }
1030
+ // Compute the new visible window
1031
+ // If scrollTop is specified, use that to determine which lines
1032
+ // to render instead of the current scrollbar position.
1033
+ var visible = visibleLines(scrollTop);
1034
+ // Bail out if the visible area is already rendered and nothing changed.
1035
+ if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) {
1036
+ updateVerticalScroll(scrollTop);
1037
+ return;
1038
+ }
1039
+ var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
1040
+ if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
1041
+ if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
1042
+
1043
+ // Create a range of theoretically intact lines, and punch holes
1044
+ // in that using the change info.
1045
+ var intact = changes === true ? [] :
1046
+ computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes);
1047
+ // Clip off the parts that won't be visible
1048
+ var intactLines = 0;
1049
+ for (var i = 0; i < intact.length; ++i) {
1050
+ var range = intact[i];
1051
+ if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
1052
+ if (range.to > to) range.to = to;
1053
+ if (range.from >= range.to) intact.splice(i--, 1);
1054
+ else intactLines += range.to - range.from;
1055
+ }
1056
+ if (intactLines == to - from && from == showingFrom && to == showingTo) {
1057
+ updateVerticalScroll(scrollTop);
1058
+ return;
1059
+ }
1060
+ intact.sort(function(a, b) {return a.domStart - b.domStart;});
1061
+
1062
+ var th = textHeight(), gutterDisplay = gutter.style.display;
1063
+ lineDiv.style.display = "none";
1064
+ patchDisplay(from, to, intact);
1065
+ lineDiv.style.display = gutter.style.display = "";
1066
+
1067
+ var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
1068
+ // This is just a bogus formula that detects when the editor is
1069
+ // resized or the font size changes.
1070
+ if (different) lastSizeC = scroller.clientHeight + th;
1071
+ if (from != showingFrom || to != showingTo && options.onViewportChange)
1072
+ setTimeout(function(){
1073
+ if (options.onViewportChange) options.onViewportChange(instance, from, to);
1074
+ });
1075
+ showingFrom = from; showingTo = to;
1076
+ displayOffset = heightAtLine(doc, from);
1077
+ startWorker(100);
1078
+
1079
+ // Since this is all rather error prone, it is honoured with the
1080
+ // only assertion in the whole file.
1081
+ if (lineDiv.childNodes.length != showingTo - showingFrom)
1082
+ throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
1083
+ " nodes=" + lineDiv.childNodes.length);
1084
+
1085
+ function checkHeights() {
1086
+ var curNode = lineDiv.firstChild, heightChanged = false;
1087
+ doc.iter(showingFrom, showingTo, function(line) {
1088
+ // Work around bizarro IE7 bug where, sometimes, our curNode
1089
+ // is magically replaced with a new node in the DOM, leaving
1090
+ // us with a reference to an orphan (nextSibling-less) node.
1091
+ if (!curNode) return;
1092
+ if (!line.hidden) {
1093
+ var height = Math.round(curNode.offsetHeight / th) || 1;
1094
+ if (line.height != height) {
1095
+ updateLineHeight(line, height);
1096
+ gutterDirty = heightChanged = true;
1097
+ }
1098
+ }
1099
+ curNode = curNode.nextSibling;
1100
+ });
1101
+ return heightChanged;
1102
+ }
1103
+
1104
+ if (options.lineWrapping) checkHeights();
1105
+
1106
+ gutter.style.display = gutterDisplay;
1107
+ if (different || gutterDirty) {
1108
+ // If the gutter grew in size, re-check heights. If those changed, re-draw gutter.
1109
+ updateGutter() && options.lineWrapping && checkHeights() && updateGutter();
1110
+ }
1111
+ updateVerticalScroll(scrollTop);
1112
+ updateSelection();
1113
+ if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
1114
+ return true;
1115
+ }
1116
+
1117
+ function computeIntact(intact, changes) {
1118
+ for (var i = 0, l = changes.length || 0; i < l; ++i) {
1119
+ var change = changes[i], intact2 = [], diff = change.diff || 0;
1120
+ for (var j = 0, l2 = intact.length; j < l2; ++j) {
1121
+ var range = intact[j];
1122
+ if (change.to <= range.from && change.diff)
1123
+ intact2.push({from: range.from + diff, to: range.to + diff,
1124
+ domStart: range.domStart});
1125
+ else if (change.to <= range.from || change.from >= range.to)
1126
+ intact2.push(range);
1127
+ else {
1128
+ if (change.from > range.from)
1129
+ intact2.push({from: range.from, to: change.from, domStart: range.domStart});
1130
+ if (change.to < range.to)
1131
+ intact2.push({from: change.to + diff, to: range.to + diff,
1132
+ domStart: range.domStart + (change.to - range.from)});
1133
+ }
1134
+ }
1135
+ intact = intact2;
1136
+ }
1137
+ return intact;
1138
+ }
1139
+
1140
+ function patchDisplay(from, to, intact) {
1141
+ function killNode(node) {
1142
+ var tmp = node.nextSibling;
1143
+ node.parentNode.removeChild(node);
1144
+ return tmp;
1145
+ }
1146
+ // The first pass removes the DOM nodes that aren't intact.
1147
+ if (!intact.length) removeChildren(lineDiv);
1148
+ else {
1149
+ var domPos = 0, curNode = lineDiv.firstChild, n;
1150
+ for (var i = 0; i < intact.length; ++i) {
1151
+ var cur = intact[i];
1152
+ while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
1153
+ for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;}
1154
+ }
1155
+ while (curNode) curNode = killNode(curNode);
1156
+ }
1157
+ // This pass fills in the lines that actually changed.
1158
+ var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
1159
+ doc.iter(from, to, function(line) {
1160
+ if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
1161
+ if (!nextIntact || nextIntact.from > j) {
1162
+ if (line.hidden) var lineElement = elt("pre");
1163
+ else {
1164
+ var lineElement = lineContent(line);
1165
+ if (line.className) lineElement.className = line.className;
1166
+ // Kludge to make sure the styled element lies behind the selection (by z-index)
1167
+ if (line.bgClassName) {
1168
+ var pre = elt("pre", "\u00a0", line.bgClassName, "position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2");
1169
+ lineElement = elt("div", [pre, lineElement], null, "position: relative");
1170
+ }
1171
+ }
1172
+ lineDiv.insertBefore(lineElement, curNode);
1173
+ } else {
1174
+ curNode = curNode.nextSibling;
1175
+ }
1176
+ ++j;
1177
+ });
1178
+ }
1179
+
1180
+ function updateGutter() {
1181
+ if (!options.gutter && !options.lineNumbers) return;
1182
+ var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
1183
+ gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
1184
+ var fragment = document.createDocumentFragment(), i = showingFrom, normalNode;
1185
+ doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
1186
+ if (line.hidden) {
1187
+ fragment.appendChild(elt("pre"));
1188
+ } else {
1189
+ var marker = line.gutterMarker;
1190
+ var text = options.lineNumbers ? options.lineNumberFormatter(i + options.firstLineNumber) : null;
1191
+ if (marker && marker.text)
1192
+ text = marker.text.replace("%N%", text != null ? text : "");
1193
+ else if (text == null)
1194
+ text = "\u00a0";
1195
+ var markerElement = fragment.appendChild(elt("pre", null, marker && marker.style));
1196
+ markerElement.innerHTML = text;
1197
+ for (var j = 1; j < line.height; ++j) {
1198
+ markerElement.appendChild(elt("br"));
1199
+ markerElement.appendChild(document.createTextNode("\u00a0"));
1200
+ }
1201
+ if (!marker) normalNode = i;
1202
+ }
1203
+ ++i;
1204
+ });
1205
+ gutter.style.display = "none";
1206
+ removeChildrenAndAdd(gutterText, fragment);
1207
+ // Make sure scrolling doesn't cause number gutter size to pop
1208
+ if (normalNode != null && options.lineNumbers) {
1209
+ var node = gutterText.childNodes[normalNode - showingFrom];
1210
+ var minwidth = String(doc.size).length, val = eltText(node.firstChild), pad = "";
1211
+ while (val.length + pad.length < minwidth) pad += "\u00a0";
1212
+ if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild);
1213
+ }
1214
+ gutter.style.display = "";
1215
+ var resized = Math.abs((parseInt(lineSpace.style.marginLeft) || 0) - gutter.offsetWidth) > 2;
1216
+ lineSpace.style.marginLeft = gutter.offsetWidth + "px";
1217
+ gutterDirty = false;
1218
+ return resized;
1219
+ }
1220
+ function updateSelection() {
1221
+ var collapsed = posEq(sel.from, sel.to);
1222
+ var fromPos = localCoords(sel.from, true);
1223
+ var toPos = collapsed ? fromPos : localCoords(sel.to, true);
1224
+ var headPos = sel.inverted ? fromPos : toPos, th = textHeight();
1225
+ var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
1226
+ inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px";
1227
+ inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px";
1228
+ if (collapsed) {
1229
+ cursor.style.top = headPos.y + "px";
1230
+ cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px";
1231
+ cursor.style.display = "";
1232
+ selectionDiv.style.display = "none";
1233
+ } else {
1234
+ var sameLine = fromPos.y == toPos.y, fragment = document.createDocumentFragment();
1235
+ var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth;
1236
+ var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight;
1237
+ var add = function(left, top, right, height) {
1238
+ var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px"
1239
+ : "right: " + right + "px";
1240
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
1241
+ "px; top: " + top + "px; " + rstyle + "; height: " + height + "px"));
1242
+ };
1243
+ if (sel.from.ch && fromPos.y >= 0) {
1244
+ var right = sameLine ? clientWidth - toPos.x : 0;
1245
+ add(fromPos.x, fromPos.y, right, th);
1246
+ }
1247
+ var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0));
1248
+ var middleHeight = Math.min(toPos.y, clientHeight) - middleStart;
1249
+ if (middleHeight > 0.2 * th)
1250
+ add(0, middleStart, 0, middleHeight);
1251
+ if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th)
1252
+ add(0, toPos.y, clientWidth - toPos.x, th);
1253
+ removeChildrenAndAdd(selectionDiv, fragment);
1254
+ cursor.style.display = "none";
1255
+ selectionDiv.style.display = "";
1256
+ }
1257
+ }
1258
+
1259
+ function setShift(val) {
1260
+ if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
1261
+ else shiftSelecting = null;
1262
+ }
1263
+ function setSelectionUser(from, to) {
1264
+ var sh = shiftSelecting && clipPos(shiftSelecting);
1265
+ if (sh) {
1266
+ if (posLess(sh, from)) from = sh;
1267
+ else if (posLess(to, sh)) to = sh;
1268
+ }
1269
+ setSelection(from, to);
1270
+ userSelChange = true;
1271
+ }
1272
+ // Update the selection. Last two args are only used by
1273
+ // updateLines, since they have to be expressed in the line
1274
+ // numbers before the update.
1275
+ function setSelection(from, to, oldFrom, oldTo) {
1276
+ goalColumn = null;
1277
+ if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
1278
+ if (posEq(sel.from, from) && posEq(sel.to, to)) return;
1279
+ if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
1280
+
1281
+ // Skip over hidden lines.
1282
+ if (from.line != oldFrom) {
1283
+ var from1 = skipHidden(from, oldFrom, sel.from.ch);
1284
+ // If there is no non-hidden line left, force visibility on current line
1285
+ if (!from1) setLineHidden(from.line, false);
1286
+ else from = from1;
1287
+ }
1288
+ if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
1289
+
1290
+ if (posEq(from, to)) sel.inverted = false;
1291
+ else if (posEq(from, sel.to)) sel.inverted = false;
1292
+ else if (posEq(to, sel.from)) sel.inverted = true;
1293
+
1294
+ if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) {
1295
+ var head = sel.inverted ? from : to;
1296
+ if (head.line != sel.from.line && sel.from.line < doc.size) {
1297
+ var oldLine = getLine(sel.from.line);
1298
+ if (/^\s+$/.test(oldLine.text))
1299
+ setTimeout(operation(function() {
1300
+ if (oldLine.parent && /^\s+$/.test(oldLine.text)) {
1301
+ var no = lineNo(oldLine);
1302
+ replaceRange("", {line: no, ch: 0}, {line: no, ch: oldLine.text.length});
1303
+ }
1304
+ }, 10));
1305
+ }
1306
+ }
1307
+
1308
+ sel.from = from; sel.to = to;
1309
+ selectionChanged = true;
1310
+ }
1311
+ function skipHidden(pos, oldLine, oldCh) {
1312
+ function getNonHidden(dir) {
1313
+ var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
1314
+ while (lNo != end) {
1315
+ var line = getLine(lNo);
1316
+ if (!line.hidden) {
1317
+ var ch = pos.ch;
1318
+ if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length;
1319
+ return {line: lNo, ch: ch};
1320
+ }
1321
+ lNo += dir;
1322
+ }
1323
+ }
1324
+ var line = getLine(pos.line);
1325
+ var toEnd = pos.ch == line.text.length && pos.ch != oldCh;
1326
+ if (!line.hidden) return pos;
1327
+ if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
1328
+ else return getNonHidden(-1) || getNonHidden(1);
1329
+ }
1330
+ function setCursor(line, ch, user) {
1331
+ var pos = clipPos({line: line, ch: ch || 0});
1332
+ (user ? setSelectionUser : setSelection)(pos, pos);
1333
+ }
1334
+
1335
+ function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));}
1336
+ function clipPos(pos) {
1337
+ if (pos.line < 0) return {line: 0, ch: 0};
1338
+ if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length};
1339
+ var ch = pos.ch, linelen = getLine(pos.line).text.length;
1340
+ if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
1341
+ else if (ch < 0) return {line: pos.line, ch: 0};
1342
+ else return pos;
1343
+ }
1344
+
1345
+ function findPosH(dir, unit) {
1346
+ var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch;
1347
+ var lineObj = getLine(line);
1348
+ function findNextLine() {
1349
+ for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) {
1350
+ var lo = getLine(l);
1351
+ if (!lo.hidden) { line = l; lineObj = lo; return true; }
1352
+ }
1353
+ }
1354
+ function moveOnce(boundToLine) {
1355
+ if (ch == (dir < 0 ? 0 : lineObj.text.length)) {
1356
+ if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0;
1357
+ else return false;
1358
+ } else ch += dir;
1359
+ return true;
1360
+ }
1361
+ if (unit == "char") moveOnce();
1362
+ else if (unit == "column") moveOnce(true);
1363
+ else if (unit == "word") {
1364
+ var sawWord = false;
1365
+ for (;;) {
1366
+ if (dir < 0) if (!moveOnce()) break;
1367
+ if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
1368
+ else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
1369
+ if (dir > 0) if (!moveOnce()) break;
1370
+ }
1371
+ }
1372
+ return {line: line, ch: ch};
1373
+ }
1374
+ function moveH(dir, unit) {
1375
+ var pos = dir < 0 ? sel.from : sel.to;
1376
+ if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit);
1377
+ setCursor(pos.line, pos.ch, true);
1378
+ }
1379
+ function deleteH(dir, unit) {
1380
+ if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to);
1381
+ else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to);
1382
+ else replaceRange("", sel.from, findPosH(dir, unit));
1383
+ userSelChange = true;
1384
+ }
1385
+ function moveV(dir, unit) {
1386
+ var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
1387
+ if (goalColumn != null) pos.x = goalColumn;
1388
+ if (unit == "page") {
1389
+ var screen = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
1390
+ var target = coordsChar(pos.x, pos.y + screen * dir);
1391
+ } else if (unit == "line") {
1392
+ var th = textHeight();
1393
+ var target = coordsChar(pos.x, pos.y + .5 * th + dir * th);
1394
+ }
1395
+ if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y;
1396
+ setCursor(target.line, target.ch, true);
1397
+ goalColumn = pos.x;
1398
+ }
1399
+
1400
+ function findWordAt(pos) {
1401
+ var line = getLine(pos.line).text;
1402
+ var start = pos.ch, end = pos.ch;
1403
+ if (line) {
1404
+ if (pos.after === false || end == line.length) --start; else ++end;
1405
+ var startChar = line.charAt(start);
1406
+ var check = isWordChar(startChar) ? isWordChar :
1407
+ /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
1408
+ function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
1409
+ while (start > 0 && check(line.charAt(start - 1))) --start;
1410
+ while (end < line.length && check(line.charAt(end))) ++end;
1411
+ }
1412
+ return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
1413
+ }
1414
+ function selectLine(line) {
1415
+ setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0}));
1416
+ }
1417
+ function indentSelected(mode) {
1418
+ if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
1419
+ var e = sel.to.line - (sel.to.ch ? 0 : 1);
1420
+ for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
1421
+ }
1422
+
1423
+ function indentLine(n, how) {
1424
+ if (!how) how = "add";
1425
+ if (how == "smart") {
1426
+ if (!mode.indent) how = "prev";
1427
+ else var state = getStateBefore(n);
1428
+ }
1429
+
1430
+ var line = getLine(n), curSpace = line.indentation(options.tabSize),
1431
+ curSpaceString = line.text.match(/^\s*/)[0], indentation;
1432
+ if (how == "smart") {
1433
+ indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
1434
+ if (indentation == Pass) how = "prev";
1435
+ }
1436
+ if (how == "prev") {
1437
+ if (n) indentation = getLine(n-1).indentation(options.tabSize);
1438
+ else indentation = 0;
1439
+ }
1440
+ else if (how == "add") indentation = curSpace + options.indentUnit;
1441
+ else if (how == "subtract") indentation = curSpace - options.indentUnit;
1442
+ indentation = Math.max(0, indentation);
1443
+ var diff = indentation - curSpace;
1444
+
1445
+ var indentString = "", pos = 0;
1446
+ if (options.indentWithTabs)
1447
+ for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
1448
+ if (pos < indentation) indentString += spaceStr(indentation - pos);
1449
+
1450
+ if (indentString != curSpaceString)
1451
+ replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1452
+ }
1453
+
1454
+ function loadMode() {
1455
+ mode = CodeMirror.getMode(options, options.mode);
1456
+ doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
1457
+ frontier = 0;
1458
+ startWorker(100);
1459
+ }
1460
+ function gutterChanged() {
1461
+ var visible = options.gutter || options.lineNumbers;
1462
+ gutter.style.display = visible ? "" : "none";
1463
+ if (visible) gutterDirty = true;
1464
+ else lineDiv.parentNode.style.marginLeft = 0;
1465
+ }
1466
+ function wrappingChanged(from, to) {
1467
+ if (options.lineWrapping) {
1468
+ wrapper.className += " CodeMirror-wrap";
1469
+ var perLine = scroller.clientWidth / charWidth() - 3;
1470
+ doc.iter(0, doc.size, function(line) {
1471
+ if (line.hidden) return;
1472
+ var guess = Math.ceil(line.text.length / perLine) || 1;
1473
+ if (guess != 1) updateLineHeight(line, guess);
1474
+ });
1475
+ lineSpace.style.minWidth = widthForcer.style.left = "";
1476
+ } else {
1477
+ wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
1478
+ computeMaxLength();
1479
+ doc.iter(0, doc.size, function(line) {
1480
+ if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
1481
+ });
1482
+ }
1483
+ changes.push({from: 0, to: doc.size});
1484
+ }
1485
+ function themeChanged() {
1486
+ scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") +
1487
+ options.theme.replace(/(^|\s)\s*/g, " cm-s-");
1488
+ }
1489
+ function keyMapChanged() {
1490
+ var style = keyMap[options.keyMap].style;
1491
+ wrapper.className = wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
1492
+ (style ? " cm-keymap-" + style : "");
1493
+ }
1494
+
1495
+ function TextMarker(type, style) { this.lines = []; this.type = type; if (style) this.style = style; }
1496
+ TextMarker.prototype.clear = operation(function() {
1497
+ var min = Infinity, max = -Infinity;
1498
+ for (var i = 0; i < this.lines.length; ++i) {
1499
+ var line = this.lines[i];
1500
+ var span = getMarkedSpanFor(line.markedSpans, this, true);
1501
+ if (span.from != null || span.to != null) {
1502
+ var lineN = lineNo(line);
1503
+ min = Math.min(min, lineN); max = Math.max(max, lineN);
1504
+ }
1505
+ }
1506
+ if (min != Infinity)
1507
+ changes.push({from: min, to: max + 1});
1508
+ this.lines.length = 0;
1509
+ });
1510
+ TextMarker.prototype.find = function() {
1511
+ var from, to;
1512
+ for (var i = 0; i < this.lines.length; ++i) {
1513
+ var line = this.lines[i];
1514
+ var span = getMarkedSpanFor(line.markedSpans, this);
1515
+ if (span.from != null || span.to != null) {
1516
+ var found = lineNo(line);
1517
+ if (span.from != null) from = {line: found, ch: span.from};
1518
+ if (span.to != null) to = {line: found, ch: span.to};
1519
+ }
1520
+ }
1521
+ if (this.type == "bookmark") return from;
1522
+ return from && {from: from, to: to};
1523
+ };
1524
+
1525
+ function markText(from, to, className, options) {
1526
+ from = clipPos(from); to = clipPos(to);
1527
+ var marker = new TextMarker("range", className);
1528
+ if (options) for (var opt in options) if (options.hasOwnProperty(opt))
1529
+ marker[opt] = options[opt];
1530
+ var curLine = from.line;
1531
+ doc.iter(curLine, to.line + 1, function(line) {
1532
+ var span = {from: curLine == from.line ? from.ch : null,
1533
+ to: curLine == to.line ? to.ch : null,
1534
+ marker: marker};
1535
+ (line.markedSpans || (line.markedSpans = [])).push(span);
1536
+ marker.lines.push(line);
1537
+ ++curLine;
1538
+ });
1539
+ changes.push({from: from.line, to: to.line + 1});
1540
+ return marker;
1541
+ }
1542
+
1543
+ function setBookmark(pos) {
1544
+ pos = clipPos(pos);
1545
+ var marker = new TextMarker("bookmark"), line = getLine(pos.line);
1546
+ var span = {from: pos.ch, to: pos.ch, marker: marker};
1547
+ (line.markedSpans || (line.markedSpans = [])).push(span);
1548
+ marker.lines.push(line);
1549
+ return marker;
1550
+ }
1551
+
1552
+ function findMarksAt(pos) {
1553
+ pos = clipPos(pos);
1554
+ var markers = [], spans = getLine(pos.line).markedSpans;
1555
+ if (spans) for (var i = 0; i < spans.length; ++i) {
1556
+ var span = spans[i];
1557
+ if ((span.from == null || span.from <= pos.ch) &&
1558
+ (span.to == null || span.to >= pos.ch))
1559
+ markers.push(span.marker);
1560
+ }
1561
+ return markers;
1562
+ }
1563
+
1564
+ function addGutterMarker(line, text, className) {
1565
+ if (typeof line == "number") line = getLine(clipLine(line));
1566
+ line.gutterMarker = {text: text, style: className};
1567
+ gutterDirty = true;
1568
+ return line;
1569
+ }
1570
+ function removeGutterMarker(line) {
1571
+ if (typeof line == "number") line = getLine(clipLine(line));
1572
+ line.gutterMarker = null;
1573
+ gutterDirty = true;
1574
+ }
1575
+
1576
+ function changeLine(handle, op) {
1577
+ var no = handle, line = handle;
1578
+ if (typeof handle == "number") line = getLine(clipLine(handle));
1579
+ else no = lineNo(handle);
1580
+ if (no == null) return null;
1581
+ if (op(line, no)) changes.push({from: no, to: no + 1});
1582
+ else return null;
1583
+ return line;
1584
+ }
1585
+ function setLineClass(handle, className, bgClassName) {
1586
+ return changeLine(handle, function(line) {
1587
+ if (line.className != className || line.bgClassName != bgClassName) {
1588
+ line.className = className;
1589
+ line.bgClassName = bgClassName;
1590
+ return true;
1591
+ }
1592
+ });
1593
+ }
1594
+ function setLineHidden(handle, hidden) {
1595
+ return changeLine(handle, function(line, no) {
1596
+ if (line.hidden != hidden) {
1597
+ line.hidden = hidden;
1598
+ if (!options.lineWrapping) {
1599
+ if (hidden && line.text.length == maxLine.text.length) {
1600
+ updateMaxLine = true;
1601
+ } else if (!hidden && line.text.length > maxLine.text.length) {
1602
+ maxLine = line; updateMaxLine = false;
1603
+ }
1604
+ }
1605
+ updateLineHeight(line, hidden ? 0 : 1);
1606
+ var fline = sel.from.line, tline = sel.to.line;
1607
+ if (hidden && (fline == no || tline == no)) {
1608
+ var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from;
1609
+ var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to;
1610
+ // Can't hide the last visible line, we'd have no place to put the cursor
1611
+ if (!to) return;
1612
+ setSelection(from, to);
1613
+ }
1614
+ return (gutterDirty = true);
1615
+ }
1616
+ });
1617
+ }
1618
+
1619
+ function lineInfo(line) {
1620
+ if (typeof line == "number") {
1621
+ if (!isLine(line)) return null;
1622
+ var n = line;
1623
+ line = getLine(line);
1624
+ if (!line) return null;
1625
+ } else {
1626
+ var n = lineNo(line);
1627
+ if (n == null) return null;
1628
+ }
1629
+ var marker = line.gutterMarker;
1630
+ return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
1631
+ markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName};
1632
+ }
1633
+
1634
+ function measureLine(line, ch) {
1635
+ if (ch == 0) return {top: 0, left: 0};
1636
+ var wbr = options.lineWrapping && ch < line.text.length &&
1637
+ spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1));
1638
+ var pre = lineContent(line, ch);
1639
+ removeChildrenAndAdd(measure, pre);
1640
+ var anchor = pre.anchor;
1641
+ var top = anchor.offsetTop, left = anchor.offsetLeft;
1642
+ // Older IEs report zero offsets for spans directly after a wrap
1643
+ if (ie && top == 0 && left == 0) {
1644
+ var backup = elt("span", "x");
1645
+ anchor.parentNode.insertBefore(backup, anchor.nextSibling);
1646
+ top = backup.offsetTop;
1647
+ }
1648
+ return {top: top, left: left};
1649
+ }
1650
+ function localCoords(pos, inLineWrap) {
1651
+ var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0));
1652
+ if (pos.ch == 0) x = 0;
1653
+ else {
1654
+ var sp = measureLine(getLine(pos.line), pos.ch);
1655
+ x = sp.left;
1656
+ if (options.lineWrapping) y += Math.max(0, sp.top);
1657
+ }
1658
+ return {x: x, y: y, yBot: y + lh};
1659
+ }
1660
+ // Coords must be lineSpace-local
1661
+ function coordsChar(x, y) {
1662
+ var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
1663
+ if (heightPos < 0) return {line: 0, ch: 0};
1664
+ var lineNo = lineAtHeight(doc, heightPos);
1665
+ if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
1666
+ var lineObj = getLine(lineNo), text = lineObj.text;
1667
+ var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
1668
+ if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
1669
+ var wrongLine = false;
1670
+ function getX(len) {
1671
+ var sp = measureLine(lineObj, len);
1672
+ if (tw) {
1673
+ var off = Math.round(sp.top / th);
1674
+ wrongLine = off != innerOff;
1675
+ return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
1676
+ }
1677
+ return sp.left;
1678
+ }
1679
+ var from = 0, fromX = 0, to = text.length, toX;
1680
+ // Guess a suitable upper bound for our search.
1681
+ var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw));
1682
+ for (;;) {
1683
+ var estX = getX(estimated);
1684
+ if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1685
+ else {toX = estX; to = estimated; break;}
1686
+ }
1687
+ if (x > toX) return {line: lineNo, ch: to};
1688
+ // Try to guess a suitable lower bound as well.
1689
+ estimated = Math.floor(to * 0.8); estX = getX(estimated);
1690
+ if (estX < x) {from = estimated; fromX = estX;}
1691
+ // Do a binary search between these bounds.
1692
+ for (;;) {
1693
+ if (to - from <= 1) {
1694
+ var after = x - fromX < toX - x;
1695
+ return {line: lineNo, ch: after ? from : to, after: after};
1696
+ }
1697
+ var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1698
+ if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; }
1699
+ else {from = middle; fromX = middleX;}
1700
+ }
1701
+ }
1702
+ function pageCoords(pos) {
1703
+ var local = localCoords(pos, true), off = eltOffset(lineSpace);
1704
+ return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1705
+ }
1706
+
1707
+ var cachedHeight, cachedHeightFor, measurePre;
1708
+ function textHeight() {
1709
+ if (measurePre == null) {
1710
+ measurePre = elt("pre");
1711
+ for (var i = 0; i < 49; ++i) {
1712
+ measurePre.appendChild(document.createTextNode("x"));
1713
+ measurePre.appendChild(elt("br"));
1714
+ }
1715
+ measurePre.appendChild(document.createTextNode("x"));
1716
+ }
1717
+ var offsetHeight = lineDiv.clientHeight;
1718
+ if (offsetHeight == cachedHeightFor) return cachedHeight;
1719
+ cachedHeightFor = offsetHeight;
1720
+ removeChildrenAndAdd(measure, measurePre.cloneNode(true));
1721
+ cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
1722
+ removeChildren(measure);
1723
+ return cachedHeight;
1724
+ }
1725
+ var cachedWidth, cachedWidthFor = 0;
1726
+ function charWidth() {
1727
+ if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
1728
+ cachedWidthFor = scroller.clientWidth;
1729
+ var anchor = elt("span", "x");
1730
+ var pre = elt("pre", [anchor]);
1731
+ removeChildrenAndAdd(measure, pre);
1732
+ return (cachedWidth = anchor.offsetWidth || 10);
1733
+ }
1734
+ function paddingTop() {return lineSpace.offsetTop;}
1735
+ function paddingLeft() {return lineSpace.offsetLeft;}
1736
+
1737
+ function posFromMouse(e, liberal) {
1738
+ var offW = eltOffset(scroller, true), x, y;
1739
+ // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1740
+ try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1741
+ // This is a mess of a heuristic to try and determine whether a
1742
+ // scroll-bar was clicked or not, and to return null if one was
1743
+ // (and !liberal).
1744
+ if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
1745
+ return null;
1746
+ var offL = eltOffset(lineSpace, true);
1747
+ return coordsChar(x - offL.left, y - offL.top);
1748
+ }
1749
+ var detectingSelectAll;
1750
+ function onContextMenu(e) {
1751
+ var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop;
1752
+ if (!pos || opera) return; // Opera is difficult.
1753
+ if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1754
+ operation(setCursor)(pos.line, pos.ch);
1755
+
1756
+ var oldCSS = input.style.cssText;
1757
+ inputDiv.style.position = "absolute";
1758
+ input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1759
+ "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
1760
+ "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1761
+ focusInput();
1762
+ resetInput(true);
1763
+ // Adds "Select all" to context menu in FF
1764
+ if (posEq(sel.from, sel.to)) input.value = prevInput = " ";
1765
+
1766
+ function rehide() {
1767
+ inputDiv.style.position = "relative";
1768
+ input.style.cssText = oldCSS;
1769
+ if (ie_lt9) scrollbar.scrollTop = scrollPos;
1770
+ slowPoll();
1771
+
1772
+ // Try to detect the user choosing select-all
1773
+ if (input.selectionStart != null) {
1774
+ clearTimeout(detectingSelectAll);
1775
+ var extval = input.value = " " + (posEq(sel.from, sel.to) ? "" : input.value), i = 0;
1776
+ prevInput = " ";
1777
+ input.selectionStart = 1; input.selectionEnd = extval.length;
1778
+ detectingSelectAll = setTimeout(function poll(){
1779
+ if (prevInput == " " && input.selectionStart == 0)
1780
+ operation(commands.selectAll)(instance);
1781
+ else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
1782
+ else resetInput();
1783
+ }, 200);
1784
+ }
1785
+ }
1786
+
1787
+ if (gecko) {
1788
+ e_stop(e);
1789
+ var mouseup = connect(window, "mouseup", function() {
1790
+ mouseup();
1791
+ setTimeout(rehide, 20);
1792
+ }, true);
1793
+ } else {
1794
+ setTimeout(rehide, 50);
1795
+ }
1796
+ }
1797
+
1798
+ // Cursor-blinking
1799
+ function restartBlink() {
1800
+ clearInterval(blinker);
1801
+ var on = true;
1802
+ cursor.style.visibility = "";
1803
+ blinker = setInterval(function() {
1804
+ cursor.style.visibility = (on = !on) ? "" : "hidden";
1805
+ }, options.cursorBlinkRate);
1806
+ }
1807
+
1808
+ var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
1809
+ function matchBrackets(autoclear) {
1810
+ var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
1811
+ var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
1812
+ if (!match) return;
1813
+ var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
1814
+ for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2)
1815
+ if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;}
1816
+
1817
+ var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
1818
+ function scan(line, from, to) {
1819
+ if (!line.text) return;
1820
+ var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
1821
+ for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
1822
+ var text = st[i];
1823
+ if (st[i+1] != style) {pos += d * text.length; continue;}
1824
+ for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
1825
+ if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
1826
+ var match = matching[cur];
1827
+ if (match.charAt(1) == ">" == forward) stack.push(cur);
1828
+ else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
1829
+ else if (!stack.length) return {pos: pos, match: true};
1830
+ }
1831
+ }
1832
+ }
1833
+ }
1834
+ for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
1835
+ var line = getLine(i), first = i == head.line;
1836
+ var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
1837
+ if (found) break;
1838
+ }
1839
+ if (!found) found = {pos: null, match: false};
1840
+ var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
1841
+ var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
1842
+ two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
1843
+ var clear = operation(function(){one.clear(); two && two.clear();});
1844
+ if (autoclear) setTimeout(clear, 800);
1845
+ else bracketHighlighted = clear;
1846
+ }
1847
+
1848
+ // Finds the line to start with when starting a parse. Tries to
1849
+ // find a line with a stateAfter, so that it can start with a
1850
+ // valid state. If that fails, it returns the line with the
1851
+ // smallest indentation, which tends to need the least context to
1852
+ // parse correctly.
1853
+ function findStartLine(n) {
1854
+ var minindent, minline;
1855
+ for (var search = n, lim = n - 40; search > lim; --search) {
1856
+ if (search == 0) return 0;
1857
+ var line = getLine(search-1);
1858
+ if (line.stateAfter) return search;
1859
+ var indented = line.indentation(options.tabSize);
1860
+ if (minline == null || minindent > indented) {
1861
+ minline = search - 1;
1862
+ minindent = indented;
1863
+ }
1864
+ }
1865
+ return minline;
1866
+ }
1867
+ function getStateBefore(n) {
1868
+ var pos = findStartLine(n), state = pos && getLine(pos-1).stateAfter;
1869
+ if (!state) state = startState(mode);
1870
+ else state = copyState(mode, state);
1871
+ doc.iter(pos, n, function(line) {
1872
+ line.process(mode, state, options.tabSize);
1873
+ line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(mode, state) : null;
1874
+ });
1875
+ return state;
1876
+ }
1877
+ function highlightWorker() {
1878
+ if (frontier >= showingTo) return;
1879
+ var end = +new Date + options.workTime, state = copyState(mode, getStateBefore(frontier));
1880
+ var startFrontier = frontier;
1881
+ doc.iter(frontier, showingTo, function(line) {
1882
+ if (frontier >= showingFrom) { // Visible
1883
+ line.highlight(mode, state, options.tabSize);
1884
+ line.stateAfter = copyState(mode, state);
1885
+ } else {
1886
+ line.process(mode, state, options.tabSize);
1887
+ line.stateAfter = frontier % 5 == 0 ? copyState(mode, state) : null;
1888
+ }
1889
+ ++frontier;
1890
+ if (+new Date > end) {
1891
+ startWorker(options.workDelay);
1892
+ return true;
1893
+ }
1894
+ });
1895
+ if (showingTo > startFrontier && frontier >= showingFrom)
1896
+ operation(function() {changes.push({from: startFrontier, to: frontier});})();
1897
+ }
1898
+ function startWorker(time) {
1899
+ if (frontier < showingTo)
1900
+ highlight.set(time, highlightWorker);
1901
+ }
1902
+
1903
+ // Operations are used to wrap changes in such a way that each
1904
+ // change won't have to update the cursor and display (which would
1905
+ // be awkward, slow, and error-prone), but instead updates are
1906
+ // batched and then all combined and executed at once.
1907
+ function startOperation() {
1908
+ updateInput = userSelChange = textChanged = null;
1909
+ changes = []; selectionChanged = false; callbacks = [];
1910
+ }
1911
+ function endOperation() {
1912
+ if (updateMaxLine) computeMaxLength();
1913
+ if (maxLineChanged && !options.lineWrapping) {
1914
+ var cursorWidth = widthForcer.offsetWidth, left = measureLine(maxLine, maxLine.text.length).left;
1915
+ if (!ie_lt8) {
1916
+ widthForcer.style.left = left + "px";
1917
+ lineSpace.style.minWidth = (left + cursorWidth) + "px";
1918
+ }
1919
+ maxLineChanged = false;
1920
+ }
1921
+ var newScrollPos, updated;
1922
+ if (selectionChanged) {
1923
+ var coords = calculateCursorCoords();
1924
+ newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot);
1925
+ }
1926
+ if (changes.length || newScrollPos && newScrollPos.scrollTop != null)
1927
+ updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop);
1928
+ if (!updated) {
1929
+ if (selectionChanged) updateSelection();
1930
+ if (gutterDirty) updateGutter();
1931
+ }
1932
+ if (newScrollPos) scrollCursorIntoView();
1933
+ if (selectionChanged) restartBlink();
1934
+
1935
+ if (focused && (updateInput === true || (updateInput !== false && selectionChanged)))
1936
+ resetInput(userSelChange);
1937
+
1938
+ if (selectionChanged && options.matchBrackets)
1939
+ setTimeout(operation(function() {
1940
+ if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1941
+ if (posEq(sel.from, sel.to)) matchBrackets(false);
1942
+ }), 20);
1943
+ var sc = selectionChanged, cbs = callbacks; // these can be reset by callbacks
1944
+ if (textChanged && options.onChange && instance)
1945
+ options.onChange(instance, textChanged);
1946
+ if (sc && options.onCursorActivity)
1947
+ options.onCursorActivity(instance);
1948
+ for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
1949
+ if (updated && options.onUpdate) options.onUpdate(instance);
1950
+ }
1951
+ var nestedOperation = 0;
1952
+ function operation(f) {
1953
+ return function() {
1954
+ if (!nestedOperation++) startOperation();
1955
+ try {var result = f.apply(this, arguments);}
1956
+ finally {if (!--nestedOperation) endOperation();}
1957
+ return result;
1958
+ };
1959
+ }
1960
+
1961
+ function compoundChange(f) {
1962
+ history.startCompound();
1963
+ try { return f(); } finally { history.endCompound(); }
1964
+ }
1965
+
1966
+ for (var ext in extensions)
1967
+ if (extensions.propertyIsEnumerable(ext) &&
1968
+ !instance.propertyIsEnumerable(ext))
1969
+ instance[ext] = extensions[ext];
1970
+ return instance;
1971
+ } // (end of function CodeMirror)
1972
+
1973
+ // The default configuration options.
1974
+ CodeMirror.defaults = {
1975
+ value: "",
1976
+ mode: null,
1977
+ theme: "default",
1978
+ indentUnit: 2,
1979
+ indentWithTabs: false,
1980
+ smartIndent: true,
1981
+ tabSize: 4,
1982
+ keyMap: "default",
1983
+ extraKeys: null,
1984
+ electricChars: true,
1985
+ autoClearEmptyLines: false,
1986
+ onKeyEvent: null,
1987
+ onDragEvent: null,
1988
+ lineWrapping: false,
1989
+ lineNumbers: false,
1990
+ gutter: false,
1991
+ fixedGutter: false,
1992
+ firstLineNumber: 1,
1993
+ readOnly: false,
1994
+ dragDrop: true,
1995
+ onChange: null,
1996
+ onCursorActivity: null,
1997
+ onViewportChange: null,
1998
+ onGutterClick: null,
1999
+ onUpdate: null,
2000
+ onFocus: null, onBlur: null, onScroll: null,
2001
+ matchBrackets: false,
2002
+ cursorBlinkRate: 530,
2003
+ workTime: 100,
2004
+ workDelay: 200,
2005
+ pollInterval: 100,
2006
+ undoDepth: 40,
2007
+ tabindex: null,
2008
+ autofocus: null,
2009
+ lineNumberFormatter: function(integer) { return integer; }
2010
+ };
2011
+
2012
+ var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
2013
+ var mac = ios || /Mac/.test(navigator.platform);
2014
+ var win = /Win/.test(navigator.platform);
2015
+
2016
+ // Known modes, by name and by MIME
2017
+ var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
2018
+ CodeMirror.defineMode = function(name, mode) {
2019
+ if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
2020
+ if (arguments.length > 2) {
2021
+ mode.dependencies = [];
2022
+ for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
2023
+ }
2024
+ modes[name] = mode;
2025
+ };
2026
+ CodeMirror.defineMIME = function(mime, spec) {
2027
+ mimeModes[mime] = spec;
2028
+ };
2029
+ CodeMirror.resolveMode = function(spec) {
2030
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
2031
+ spec = mimeModes[spec];
2032
+ else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
2033
+ return CodeMirror.resolveMode("application/xml");
2034
+ if (typeof spec == "string") return {name: spec};
2035
+ else return spec || {name: "null"};
2036
+ };
2037
+ CodeMirror.getMode = function(options, spec) {
2038
+ var spec = CodeMirror.resolveMode(spec);
2039
+ var mfactory = modes[spec.name];
2040
+ if (!mfactory) return CodeMirror.getMode(options, "text/plain");
2041
+ var modeObj = mfactory(options, spec);
2042
+ if (modeExtensions.hasOwnProperty(spec.name)) {
2043
+ var exts = modeExtensions[spec.name];
2044
+ for (var prop in exts) if (exts.hasOwnProperty(prop)) modeObj[prop] = exts[prop];
2045
+ }
2046
+ modeObj.name = spec.name;
2047
+ return modeObj;
2048
+ };
2049
+ CodeMirror.listModes = function() {
2050
+ var list = [];
2051
+ for (var m in modes)
2052
+ if (modes.propertyIsEnumerable(m)) list.push(m);
2053
+ return list;
2054
+ };
2055
+ CodeMirror.listMIMEs = function() {
2056
+ var list = [];
2057
+ for (var m in mimeModes)
2058
+ if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
2059
+ return list;
2060
+ };
2061
+
2062
+ var extensions = CodeMirror.extensions = {};
2063
+ CodeMirror.defineExtension = function(name, func) {
2064
+ extensions[name] = func;
2065
+ };
2066
+
2067
+ var modeExtensions = CodeMirror.modeExtensions = {};
2068
+ CodeMirror.extendMode = function(mode, properties) {
2069
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2070
+ for (var prop in properties) if (properties.hasOwnProperty(prop))
2071
+ exts[prop] = properties[prop];
2072
+ };
2073
+
2074
+ var commands = CodeMirror.commands = {
2075
+ selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
2076
+ killLine: function(cm) {
2077
+ var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
2078
+ if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0});
2079
+ else cm.replaceRange("", from, sel ? to : {line: from.line});
2080
+ },
2081
+ deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});},
2082
+ undo: function(cm) {cm.undo();},
2083
+ redo: function(cm) {cm.redo();},
2084
+ goDocStart: function(cm) {cm.setCursor(0, 0, true);},
2085
+ goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);},
2086
+ goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);},
2087
+ goLineStartSmart: function(cm) {
2088
+ var cur = cm.getCursor();
2089
+ var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/));
2090
+ cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true);
2091
+ },
2092
+ goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);},
2093
+ goLineUp: function(cm) {cm.moveV(-1, "line");},
2094
+ goLineDown: function(cm) {cm.moveV(1, "line");},
2095
+ goPageUp: function(cm) {cm.moveV(-1, "page");},
2096
+ goPageDown: function(cm) {cm.moveV(1, "page");},
2097
+ goCharLeft: function(cm) {cm.moveH(-1, "char");},
2098
+ goCharRight: function(cm) {cm.moveH(1, "char");},
2099
+ goColumnLeft: function(cm) {cm.moveH(-1, "column");},
2100
+ goColumnRight: function(cm) {cm.moveH(1, "column");},
2101
+ goWordLeft: function(cm) {cm.moveH(-1, "word");},
2102
+ goWordRight: function(cm) {cm.moveH(1, "word");},
2103
+ delCharLeft: function(cm) {cm.deleteH(-1, "char");},
2104
+ delCharRight: function(cm) {cm.deleteH(1, "char");},
2105
+ delWordLeft: function(cm) {cm.deleteH(-1, "word");},
2106
+ delWordRight: function(cm) {cm.deleteH(1, "word");},
2107
+ indentAuto: function(cm) {cm.indentSelection("smart");},
2108
+ indentMore: function(cm) {cm.indentSelection("add");},
2109
+ indentLess: function(cm) {cm.indentSelection("subtract");},
2110
+ insertTab: function(cm) {cm.replaceSelection("\t", "end");},
2111
+ defaultTab: function(cm) {
2112
+ if (cm.somethingSelected()) cm.indentSelection("add");
2113
+ else cm.replaceSelection("\t", "end");
2114
+ },
2115
+ transposeChars: function(cm) {
2116
+ var cur = cm.getCursor(), line = cm.getLine(cur.line);
2117
+ if (cur.ch > 0 && cur.ch < line.length - 1)
2118
+ cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
2119
+ {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
2120
+ },
2121
+ newlineAndIndent: function(cm) {
2122
+ cm.replaceSelection("\n", "end");
2123
+ cm.indentLine(cm.getCursor().line);
2124
+ },
2125
+ toggleOverwrite: function(cm) {cm.toggleOverwrite();}
2126
+ };
2127
+
2128
+ var keyMap = CodeMirror.keyMap = {};
2129
+ keyMap.basic = {
2130
+ "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
2131
+ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
2132
+ "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
2133
+ "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
2134
+ };
2135
+ // Note that the save and find-related commands aren't defined by
2136
+ // default. Unknown commands are simply ignored.
2137
+ keyMap.pcDefault = {
2138
+ "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
2139
+ "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
2140
+ "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
2141
+ "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
2142
+ "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
2143
+ "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
2144
+ fallthrough: "basic"
2145
+ };
2146
+ keyMap.macDefault = {
2147
+ "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
2148
+ "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
2149
+ "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
2150
+ "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
2151
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
2152
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore",
2153
+ fallthrough: ["basic", "emacsy"]
2154
+ };
2155
+ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
2156
+ keyMap.emacsy = {
2157
+ "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
2158
+ "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
2159
+ "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
2160
+ "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
2161
+ };
2162
+
2163
+ function getKeyMap(val) {
2164
+ if (typeof val == "string") return keyMap[val];
2165
+ else return val;
2166
+ }
2167
+ function lookupKey(name, extraMap, map, handle, stop) {
2168
+ function lookup(map) {
2169
+ map = getKeyMap(map);
2170
+ var found = map[name];
2171
+ if (found === false) {
2172
+ if (stop) stop();
2173
+ return true;
2174
+ }
2175
+ if (found != null && handle(found)) return true;
2176
+ if (map.nofallthrough) {
2177
+ if (stop) stop();
2178
+ return true;
2179
+ }
2180
+ var fallthrough = map.fallthrough;
2181
+ if (fallthrough == null) return false;
2182
+ if (Object.prototype.toString.call(fallthrough) != "[object Array]")
2183
+ return lookup(fallthrough);
2184
+ for (var i = 0, e = fallthrough.length; i < e; ++i) {
2185
+ if (lookup(fallthrough[i])) return true;
2186
+ }
2187
+ return false;
2188
+ }
2189
+ if (extraMap && lookup(extraMap)) return true;
2190
+ return lookup(map);
2191
+ }
2192
+ function isModifierKey(event) {
2193
+ var name = keyNames[e_prop(event, "keyCode")];
2194
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
2195
+ }
2196
+
2197
+ CodeMirror.fromTextArea = function(textarea, options) {
2198
+ if (!options) options = {};
2199
+ options.value = textarea.value;
2200
+ if (!options.tabindex && textarea.tabindex)
2201
+ options.tabindex = textarea.tabindex;
2202
+ // Set autofocus to true if this textarea is focused, or if it has
2203
+ // autofocus and no other element is focused.
2204
+ if (options.autofocus == null) {
2205
+ var hasFocus = document.body;
2206
+ // doc.activeElement occasionally throws on IE
2207
+ try { hasFocus = document.activeElement; } catch(e) {}
2208
+ options.autofocus = hasFocus == textarea ||
2209
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body;
2210
+ }
2211
+
2212
+ function save() {textarea.value = instance.getValue();}
2213
+ if (textarea.form) {
2214
+ // Deplorable hack to make the submit method do the right thing.
2215
+ var rmSubmit = connect(textarea.form, "submit", save, true);
2216
+ if (typeof textarea.form.submit == "function") {
2217
+ var realSubmit = textarea.form.submit;
2218
+ textarea.form.submit = function wrappedSubmit() {
2219
+ save();
2220
+ textarea.form.submit = realSubmit;
2221
+ textarea.form.submit();
2222
+ textarea.form.submit = wrappedSubmit;
2223
+ };
2224
+ }
2225
+ }
2226
+
2227
+ textarea.style.display = "none";
2228
+ var instance = CodeMirror(function(node) {
2229
+ textarea.parentNode.insertBefore(node, textarea.nextSibling);
2230
+ }, options);
2231
+ instance.save = save;
2232
+ instance.getTextArea = function() { return textarea; };
2233
+ instance.toTextArea = function() {
2234
+ save();
2235
+ textarea.parentNode.removeChild(instance.getWrapperElement());
2236
+ textarea.style.display = "";
2237
+ if (textarea.form) {
2238
+ rmSubmit();
2239
+ if (typeof textarea.form.submit == "function")
2240
+ textarea.form.submit = realSubmit;
2241
+ }
2242
+ };
2243
+ return instance;
2244
+ };
2245
+
2246
+ var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2247
+ var ie = /MSIE \d/.test(navigator.userAgent);
2248
+ var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
2249
+ var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
2250
+ var quirksMode = ie && document.documentMode == 5;
2251
+ var webkit = /WebKit\//.test(navigator.userAgent);
2252
+ var chrome = /Chrome\//.test(navigator.userAgent);
2253
+ var opera = /Opera\//.test(navigator.userAgent);
2254
+ var safari = /Apple Computer/.test(navigator.vendor);
2255
+ var khtml = /KHTML\//.test(navigator.userAgent);
2256
+ var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent);
2257
+
2258
+ // Utility functions for working with state. Exported because modes
2259
+ // sometimes need to do this.
2260
+ function copyState(mode, state) {
2261
+ if (state === true) return state;
2262
+ if (mode.copyState) return mode.copyState(state);
2263
+ var nstate = {};
2264
+ for (var n in state) {
2265
+ var val = state[n];
2266
+ if (val instanceof Array) val = val.concat([]);
2267
+ nstate[n] = val;
2268
+ }
2269
+ return nstate;
2270
+ }
2271
+ CodeMirror.copyState = copyState;
2272
+ function startState(mode, a1, a2) {
2273
+ return mode.startState ? mode.startState(a1, a2) : true;
2274
+ }
2275
+ CodeMirror.startState = startState;
2276
+ CodeMirror.innerMode = function(mode, state) {
2277
+ while (mode.innerMode) {
2278
+ var info = mode.innerMode(state);
2279
+ state = info.state;
2280
+ mode = info.mode;
2281
+ }
2282
+ return info || {mode: mode, state: state};
2283
+ };
2284
+
2285
+ // The character stream used by a mode's parser.
2286
+ function StringStream(string, tabSize) {
2287
+ this.pos = this.start = 0;
2288
+ this.string = string;
2289
+ this.tabSize = tabSize || 8;
2290
+ }
2291
+ StringStream.prototype = {
2292
+ eol: function() {return this.pos >= this.string.length;},
2293
+ sol: function() {return this.pos == 0;},
2294
+ peek: function() {return this.string.charAt(this.pos) || undefined;},
2295
+ next: function() {
2296
+ if (this.pos < this.string.length)
2297
+ return this.string.charAt(this.pos++);
2298
+ },
2299
+ eat: function(match) {
2300
+ var ch = this.string.charAt(this.pos);
2301
+ if (typeof match == "string") var ok = ch == match;
2302
+ else var ok = ch && (match.test ? match.test(ch) : match(ch));
2303
+ if (ok) {++this.pos; return ch;}
2304
+ },
2305
+ eatWhile: function(match) {
2306
+ var start = this.pos;
2307
+ while (this.eat(match)){}
2308
+ return this.pos > start;
2309
+ },
2310
+ eatSpace: function() {
2311
+ var start = this.pos;
2312
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
2313
+ return this.pos > start;
2314
+ },
2315
+ skipToEnd: function() {this.pos = this.string.length;},
2316
+ skipTo: function(ch) {
2317
+ var found = this.string.indexOf(ch, this.pos);
2318
+ if (found > -1) {this.pos = found; return true;}
2319
+ },
2320
+ backUp: function(n) {this.pos -= n;},
2321
+ column: function() {return countColumn(this.string, this.start, this.tabSize);},
2322
+ indentation: function() {return countColumn(this.string, null, this.tabSize);},
2323
+ match: function(pattern, consume, caseInsensitive) {
2324
+ if (typeof pattern == "string") {
2325
+ var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
2326
+ if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
2327
+ if (consume !== false) this.pos += pattern.length;
2328
+ return true;
2329
+ }
2330
+ } else {
2331
+ var match = this.string.slice(this.pos).match(pattern);
2332
+ if (match && match.index > 0) return null;
2333
+ if (match && consume !== false) this.pos += match[0].length;
2334
+ return match;
2335
+ }
2336
+ },
2337
+ current: function(){return this.string.slice(this.start, this.pos);}
2338
+ };
2339
+ CodeMirror.StringStream = StringStream;
2340
+
2341
+ function MarkedSpan(from, to, marker) {
2342
+ this.from = from; this.to = to; this.marker = marker;
2343
+ }
2344
+
2345
+ function getMarkedSpanFor(spans, marker, del) {
2346
+ if (spans) for (var i = 0; i < spans.length; ++i) {
2347
+ var span = spans[i];
2348
+ if (span.marker == marker) {
2349
+ if (del) spans.splice(i, 1);
2350
+ return span;
2351
+ }
2352
+ }
2353
+ }
2354
+
2355
+ function markedSpansBefore(old, startCh, endCh) {
2356
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
2357
+ var span = old[i], marker = span.marker;
2358
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
2359
+ if (startsBefore || marker.type == "bookmark" && span.from == startCh && span.from != endCh) {
2360
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
2361
+ (nw || (nw = [])).push({from: span.from,
2362
+ to: endsAfter ? null : span.to,
2363
+ marker: marker});
2364
+ }
2365
+ }
2366
+ return nw;
2367
+ }
2368
+
2369
+ function markedSpansAfter(old, endCh) {
2370
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
2371
+ var span = old[i], marker = span.marker;
2372
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
2373
+ if (endsAfter || marker.type == "bookmark" && span.from == endCh) {
2374
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
2375
+ (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
2376
+ to: span.to == null ? null : span.to - endCh,
2377
+ marker: marker});
2378
+ }
2379
+ }
2380
+ return nw;
2381
+ }
2382
+
2383
+ function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
2384
+ if (!oldFirst && !oldLast) return newText;
2385
+ // Get the spans that 'stick out' on both sides
2386
+ var first = markedSpansBefore(oldFirst, startCh);
2387
+ var last = markedSpansAfter(oldLast, endCh);
2388
+
2389
+ // Next, merge those two ends
2390
+ var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
2391
+ if (first) {
2392
+ // Fix up .to properties of first
2393
+ for (var i = 0; i < first.length; ++i) {
2394
+ var span = first[i];
2395
+ if (span.to == null) {
2396
+ var found = getMarkedSpanFor(last, span.marker);
2397
+ if (!found) span.to = startCh;
2398
+ else if (sameLine) span.to = found.to == null ? null : found.to + offset;
2399
+ }
2400
+ }
2401
+ }
2402
+ if (last) {
2403
+ // Fix up .from in last (or move them into first in case of sameLine)
2404
+ for (var i = 0; i < last.length; ++i) {
2405
+ var span = last[i];
2406
+ if (span.to != null) span.to += offset;
2407
+ if (span.from == null) {
2408
+ var found = getMarkedSpanFor(first, span.marker);
2409
+ if (!found) {
2410
+ span.from = offset;
2411
+ if (sameLine) (first || (first = [])).push(span);
2412
+ }
2413
+ } else {
2414
+ span.from += offset;
2415
+ if (sameLine) (first || (first = [])).push(span);
2416
+ }
2417
+ }
2418
+ }
2419
+
2420
+ var newMarkers = [newHL(newText[0], first)];
2421
+ if (!sameLine) {
2422
+ // Fill gap with whole-line-spans
2423
+ var gap = newText.length - 2, gapMarkers;
2424
+ if (gap > 0 && first)
2425
+ for (var i = 0; i < first.length; ++i)
2426
+ if (first[i].to == null)
2427
+ (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
2428
+ for (var i = 0; i < gap; ++i)
2429
+ newMarkers.push(newHL(newText[i+1], gapMarkers));
2430
+ newMarkers.push(newHL(lst(newText), last));
2431
+ }
2432
+ return newMarkers;
2433
+ }
2434
+
2435
+ // hl stands for history-line, a data structure that can be either a
2436
+ // string (line without markers) or a {text, markedSpans} object.
2437
+ function hlText(val) { return typeof val == "string" ? val : val.text; }
2438
+ function hlSpans(val) { return typeof val == "string" ? null : val.markedSpans; }
2439
+ function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
2440
+
2441
+ function detachMarkedSpans(line) {
2442
+ var spans = line.markedSpans;
2443
+ if (!spans) return;
2444
+ for (var i = 0; i < spans.length; ++i) {
2445
+ var lines = spans[i].marker.lines;
2446
+ var ix = indexOf(lines, line);
2447
+ lines.splice(ix, 1);
2448
+ }
2449
+ line.markedSpans = null;
2450
+ }
2451
+
2452
+ function attachMarkedSpans(line, spans) {
2453
+ if (!spans) return;
2454
+ for (var i = 0; i < spans.length; ++i)
2455
+ var marker = spans[i].marker.lines.push(line);
2456
+ line.markedSpans = spans;
2457
+ }
2458
+
2459
+ // When measuring the position of the end of a line, different
2460
+ // browsers require different approaches. If an empty span is added,
2461
+ // many browsers report bogus offsets. Of those, some (Webkit,
2462
+ // recent IE) will accept a space without moving the whole span to
2463
+ // the next line when wrapping it, others work with a zero-width
2464
+ // space.
2465
+ var eolSpanContent = " ";
2466
+ if (gecko || (ie && !ie_lt8)) eolSpanContent = "\u200b";
2467
+ else if (opera) eolSpanContent = "";
2468
+
2469
+ // Line objects. These hold state related to a line, including
2470
+ // highlighting info (the styles array).
2471
+ function Line(text, markedSpans) {
2472
+ this.text = text;
2473
+ this.height = 1;
2474
+ attachMarkedSpans(this, markedSpans);
2475
+ }
2476
+ Line.prototype = {
2477
+ update: function(text, markedSpans) {
2478
+ this.text = text;
2479
+ this.stateAfter = this.styles = null;
2480
+ detachMarkedSpans(this);
2481
+ attachMarkedSpans(this, markedSpans);
2482
+ },
2483
+ // Run the given mode's parser over a line, update the styles
2484
+ // array, which contains alternating fragments of text and CSS
2485
+ // classes.
2486
+ highlight: function(mode, state, tabSize) {
2487
+ var stream = new StringStream(this.text, tabSize), st = this.styles || (this.styles = []);
2488
+ var pos = st.length = 0;
2489
+ if (this.text == "" && mode.blankLine) mode.blankLine(state);
2490
+ while (!stream.eol()) {
2491
+ var style = mode.token(stream, state), substr = stream.current();
2492
+ stream.start = stream.pos;
2493
+ if (pos && st[pos-1] == style) {
2494
+ st[pos-2] += substr;
2495
+ } else if (substr) {
2496
+ st[pos++] = substr; st[pos++] = style;
2497
+ }
2498
+ // Give up when line is ridiculously long
2499
+ if (stream.pos > 5000) {
2500
+ st[pos++] = this.text.slice(stream.pos); st[pos++] = null;
2501
+ break;
2502
+ }
2503
+ }
2504
+ },
2505
+ process: function(mode, state, tabSize) {
2506
+ var stream = new StringStream(this.text, tabSize);
2507
+ if (this.text == "" && mode.blankLine) mode.blankLine(state);
2508
+ while (!stream.eol() && stream.pos <= 5000) {
2509
+ mode.token(stream, state);
2510
+ stream.start = stream.pos;
2511
+ }
2512
+ },
2513
+ // Fetch the parser token for a given character. Useful for hacks
2514
+ // that want to inspect the mode state (say, for completion).
2515
+ getTokenAt: function(mode, state, tabSize, ch) {
2516
+ var txt = this.text, stream = new StringStream(txt, tabSize);
2517
+ while (stream.pos < ch && !stream.eol()) {
2518
+ stream.start = stream.pos;
2519
+ var style = mode.token(stream, state);
2520
+ }
2521
+ return {start: stream.start,
2522
+ end: stream.pos,
2523
+ string: stream.current(),
2524
+ className: style || null,
2525
+ state: state};
2526
+ },
2527
+ indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2528
+ // Produces an HTML fragment for the line, taking selection,
2529
+ // marking, and highlighting into account.
2530
+ getContent: function(tabSize, wrapAt, compensateForWrapping) {
2531
+ var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
2532
+ var pre = elt("pre");
2533
+ function span_(html, text, style) {
2534
+ if (!text) return;
2535
+ // Work around a bug where, in some compat modes, IE ignores leading spaces
2536
+ if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
2537
+ first = false;
2538
+ if (!specials.test(text)) {
2539
+ col += text.length;
2540
+ var content = document.createTextNode(text);
2541
+ } else {
2542
+ var content = document.createDocumentFragment(), pos = 0;
2543
+ while (true) {
2544
+ specials.lastIndex = pos;
2545
+ var m = specials.exec(text);
2546
+ var skipped = m ? m.index - pos : text.length - pos;
2547
+ if (skipped) {
2548
+ content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
2549
+ col += skipped;
2550
+ }
2551
+ if (!m) break;
2552
+ pos += skipped + 1;
2553
+ if (m[0] == "\t") {
2554
+ var tabWidth = tabSize - col % tabSize;
2555
+ content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
2556
+ col += tabWidth;
2557
+ } else {
2558
+ var token = elt("span", "\u2022", "cm-invalidchar");
2559
+ token.title = "\\u" + m[0].charCodeAt(0).toString(16);
2560
+ content.appendChild(token);
2561
+ col += 1;
2562
+ }
2563
+ }
2564
+ }
2565
+ if (style) html.appendChild(elt("span", [content], style));
2566
+ else html.appendChild(content);
2567
+ }
2568
+ var span = span_;
2569
+ if (wrapAt != null) {
2570
+ var outPos = 0, anchor = pre.anchor = elt("span");
2571
+ span = function(html, text, style) {
2572
+ var l = text.length;
2573
+ if (wrapAt >= outPos && wrapAt < outPos + l) {
2574
+ if (wrapAt > outPos) {
2575
+ span_(html, text.slice(0, wrapAt - outPos), style);
2576
+ // See comment at the definition of spanAffectsWrapping
2577
+ if (compensateForWrapping) html.appendChild(elt("wbr"));
2578
+ }
2579
+ html.appendChild(anchor);
2580
+ var cut = wrapAt - outPos;
2581
+ span_(anchor, opera ? text.slice(cut, cut + 1) : text.slice(cut), style);
2582
+ if (opera) span_(html, text.slice(cut + 1), style);
2583
+ wrapAt--;
2584
+ outPos += l;
2585
+ } else {
2586
+ outPos += l;
2587
+ span_(html, text, style);
2588
+ if (outPos == wrapAt && outPos == len) {
2589
+ setTextContent(anchor, eolSpanContent);
2590
+ html.appendChild(anchor);
2591
+ }
2592
+ // Stop outputting HTML when gone sufficiently far beyond measure
2593
+ else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){};
2594
+ }
2595
+ };
2596
+ }
2597
+
2598
+ var st = this.styles, allText = this.text, marked = this.markedSpans;
2599
+ var len = allText.length;
2600
+ function styleToClass(style) {
2601
+ if (!style) return null;
2602
+ return "cm-" + style.replace(/ +/g, " cm-");
2603
+ }
2604
+ if (!allText && wrapAt == null) {
2605
+ span(pre, " ");
2606
+ } else if (!marked || !marked.length) {
2607
+ for (var i = 0, ch = 0; ch < len; i+=2) {
2608
+ var str = st[i], style = st[i+1], l = str.length;
2609
+ if (ch + l > len) str = str.slice(0, len - ch);
2610
+ ch += l;
2611
+ span(pre, str, styleToClass(style));
2612
+ }
2613
+ } else {
2614
+ marked.sort(function(a, b) { return a.from - b.from; });
2615
+ var pos = 0, i = 0, text = "", style, sg = 0;
2616
+ var nextChange = marked[0].from || 0, marks = [], markpos = 0;
2617
+ var advanceMarks = function() {
2618
+ var m;
2619
+ while (markpos < marked.length &&
2620
+ ((m = marked[markpos]).from == pos || m.from == null)) {
2621
+ if (m.marker.type == "range") marks.push(m);
2622
+ ++markpos;
2623
+ }
2624
+ nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
2625
+ for (var i = 0; i < marks.length; ++i) {
2626
+ var to = marks[i].to;
2627
+ if (to == null) to = Infinity;
2628
+ if (to == pos) marks.splice(i--, 1);
2629
+ else nextChange = Math.min(to, nextChange);
2630
+ }
2631
+ };
2632
+ var m = 0;
2633
+ while (pos < len) {
2634
+ if (nextChange == pos) advanceMarks();
2635
+ var upto = Math.min(len, nextChange);
2636
+ while (true) {
2637
+ if (text) {
2638
+ var end = pos + text.length;
2639
+ var appliedStyle = style;
2640
+ for (var j = 0; j < marks.length; ++j) {
2641
+ var mark = marks[j];
2642
+ appliedStyle = (appliedStyle ? appliedStyle + " " : "") + mark.marker.style;
2643
+ if (mark.marker.endStyle && mark.to === Math.min(end, upto)) appliedStyle += " " + mark.marker.endStyle;
2644
+ if (mark.marker.startStyle && mark.from === pos) appliedStyle += " " + mark.marker.startStyle;
2645
+ }
2646
+ span(pre, end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2647
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2648
+ pos = end;
2649
+ }
2650
+ text = st[i++]; style = styleToClass(st[i++]);
2651
+ }
2652
+ }
2653
+ }
2654
+ return pre;
2655
+ },
2656
+ cleanUp: function() {
2657
+ this.parent = null;
2658
+ detachMarkedSpans(this);
2659
+ }
2660
+ };
2661
+
2662
+ // Data structure that holds the sequence of lines.
2663
+ function LeafChunk(lines) {
2664
+ this.lines = lines;
2665
+ this.parent = null;
2666
+ for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
2667
+ lines[i].parent = this;
2668
+ height += lines[i].height;
2669
+ }
2670
+ this.height = height;
2671
+ }
2672
+ LeafChunk.prototype = {
2673
+ chunkSize: function() { return this.lines.length; },
2674
+ remove: function(at, n, callbacks) {
2675
+ for (var i = at, e = at + n; i < e; ++i) {
2676
+ var line = this.lines[i];
2677
+ this.height -= line.height;
2678
+ line.cleanUp();
2679
+ if (line.handlers)
2680
+ for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]);
2681
+ }
2682
+ this.lines.splice(at, n);
2683
+ },
2684
+ collapse: function(lines) {
2685
+ lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
2686
+ },
2687
+ insertHeight: function(at, lines, height) {
2688
+ this.height += height;
2689
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
2690
+ for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
2691
+ },
2692
+ iterN: function(at, n, op) {
2693
+ for (var e = at + n; at < e; ++at)
2694
+ if (op(this.lines[at])) return true;
2695
+ }
2696
+ };
2697
+ function BranchChunk(children) {
2698
+ this.children = children;
2699
+ var size = 0, height = 0;
2700
+ for (var i = 0, e = children.length; i < e; ++i) {
2701
+ var ch = children[i];
2702
+ size += ch.chunkSize(); height += ch.height;
2703
+ ch.parent = this;
2704
+ }
2705
+ this.size = size;
2706
+ this.height = height;
2707
+ this.parent = null;
2708
+ }
2709
+ BranchChunk.prototype = {
2710
+ chunkSize: function() { return this.size; },
2711
+ remove: function(at, n, callbacks) {
2712
+ this.size -= n;
2713
+ for (var i = 0; i < this.children.length; ++i) {
2714
+ var child = this.children[i], sz = child.chunkSize();
2715
+ if (at < sz) {
2716
+ var rm = Math.min(n, sz - at), oldHeight = child.height;
2717
+ child.remove(at, rm, callbacks);
2718
+ this.height -= oldHeight - child.height;
2719
+ if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
2720
+ if ((n -= rm) == 0) break;
2721
+ at = 0;
2722
+ } else at -= sz;
2723
+ }
2724
+ if (this.size - n < 25) {
2725
+ var lines = [];
2726
+ this.collapse(lines);
2727
+ this.children = [new LeafChunk(lines)];
2728
+ this.children[0].parent = this;
2729
+ }
2730
+ },
2731
+ collapse: function(lines) {
2732
+ for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
2733
+ },
2734
+ insert: function(at, lines) {
2735
+ var height = 0;
2736
+ for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
2737
+ this.insertHeight(at, lines, height);
2738
+ },
2739
+ insertHeight: function(at, lines, height) {
2740
+ this.size += lines.length;
2741
+ this.height += height;
2742
+ for (var i = 0, e = this.children.length; i < e; ++i) {
2743
+ var child = this.children[i], sz = child.chunkSize();
2744
+ if (at <= sz) {
2745
+ child.insertHeight(at, lines, height);
2746
+ if (child.lines && child.lines.length > 50) {
2747
+ while (child.lines.length > 50) {
2748
+ var spilled = child.lines.splice(child.lines.length - 25, 25);
2749
+ var newleaf = new LeafChunk(spilled);
2750
+ child.height -= newleaf.height;
2751
+ this.children.splice(i + 1, 0, newleaf);
2752
+ newleaf.parent = this;
2753
+ }
2754
+ this.maybeSpill();
2755
+ }
2756
+ break;
2757
+ }
2758
+ at -= sz;
2759
+ }
2760
+ },
2761
+ maybeSpill: function() {
2762
+ if (this.children.length <= 10) return;
2763
+ var me = this;
2764
+ do {
2765
+ var spilled = me.children.splice(me.children.length - 5, 5);
2766
+ var sibling = new BranchChunk(spilled);
2767
+ if (!me.parent) { // Become the parent node
2768
+ var copy = new BranchChunk(me.children);
2769
+ copy.parent = me;
2770
+ me.children = [copy, sibling];
2771
+ me = copy;
2772
+ } else {
2773
+ me.size -= sibling.size;
2774
+ me.height -= sibling.height;
2775
+ var myIndex = indexOf(me.parent.children, me);
2776
+ me.parent.children.splice(myIndex + 1, 0, sibling);
2777
+ }
2778
+ sibling.parent = me.parent;
2779
+ } while (me.children.length > 10);
2780
+ me.parent.maybeSpill();
2781
+ },
2782
+ iter: function(from, to, op) { this.iterN(from, to - from, op); },
2783
+ iterN: function(at, n, op) {
2784
+ for (var i = 0, e = this.children.length; i < e; ++i) {
2785
+ var child = this.children[i], sz = child.chunkSize();
2786
+ if (at < sz) {
2787
+ var used = Math.min(n, sz - at);
2788
+ if (child.iterN(at, used, op)) return true;
2789
+ if ((n -= used) == 0) break;
2790
+ at = 0;
2791
+ } else at -= sz;
2792
+ }
2793
+ }
2794
+ };
2795
+
2796
+ function getLineAt(chunk, n) {
2797
+ while (!chunk.lines) {
2798
+ for (var i = 0;; ++i) {
2799
+ var child = chunk.children[i], sz = child.chunkSize();
2800
+ if (n < sz) { chunk = child; break; }
2801
+ n -= sz;
2802
+ }
2803
+ }
2804
+ return chunk.lines[n];
2805
+ }
2806
+ function lineNo(line) {
2807
+ if (line.parent == null) return null;
2808
+ var cur = line.parent, no = indexOf(cur.lines, line);
2809
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
2810
+ for (var i = 0, e = chunk.children.length; ; ++i) {
2811
+ if (chunk.children[i] == cur) break;
2812
+ no += chunk.children[i].chunkSize();
2813
+ }
2814
+ }
2815
+ return no;
2816
+ }
2817
+ function lineAtHeight(chunk, h) {
2818
+ var n = 0;
2819
+ outer: do {
2820
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
2821
+ var child = chunk.children[i], ch = child.height;
2822
+ if (h < ch) { chunk = child; continue outer; }
2823
+ h -= ch;
2824
+ n += child.chunkSize();
2825
+ }
2826
+ return n;
2827
+ } while (!chunk.lines);
2828
+ for (var i = 0, e = chunk.lines.length; i < e; ++i) {
2829
+ var line = chunk.lines[i], lh = line.height;
2830
+ if (h < lh) break;
2831
+ h -= lh;
2832
+ }
2833
+ return n + i;
2834
+ }
2835
+ function heightAtLine(chunk, n) {
2836
+ var h = 0;
2837
+ outer: do {
2838
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
2839
+ var child = chunk.children[i], sz = child.chunkSize();
2840
+ if (n < sz) { chunk = child; continue outer; }
2841
+ n -= sz;
2842
+ h += child.height;
2843
+ }
2844
+ return h;
2845
+ } while (!chunk.lines);
2846
+ for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
2847
+ return h;
2848
+ }
2849
+
2850
+ // The history object 'chunks' changes that are made close together
2851
+ // and at almost the same time into bigger undoable units.
2852
+ function History() {
2853
+ this.time = 0;
2854
+ this.done = []; this.undone = [];
2855
+ this.compound = 0;
2856
+ this.closed = false;
2857
+ }
2858
+ History.prototype = {
2859
+ addChange: function(start, added, old) {
2860
+ this.undone.length = 0;
2861
+ var time = +new Date, cur = lst(this.done), last = cur && lst(cur);
2862
+ var dtime = time - this.time;
2863
+
2864
+ if (this.compound && cur && !this.closed) {
2865
+ cur.push({start: start, added: added, old: old});
2866
+ } else if (dtime > 400 || !last || this.closed ||
2867
+ last.start > start + old.length || last.start + last.added < start) {
2868
+ this.done.push([{start: start, added: added, old: old}]);
2869
+ this.closed = false;
2870
+ } else {
2871
+ var startBefore = Math.max(0, last.start - start),
2872
+ endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
2873
+ for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
2874
+ for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
2875
+ if (startBefore) last.start = start;
2876
+ last.added += added - (old.length - startBefore - endAfter);
2877
+ }
2878
+ this.time = time;
2879
+ },
2880
+ startCompound: function() {
2881
+ if (!this.compound++) this.closed = true;
2882
+ },
2883
+ endCompound: function() {
2884
+ if (!--this.compound) this.closed = true;
2885
+ }
2886
+ };
2887
+
2888
+ function stopMethod() {e_stop(this);}
2889
+ // Ensure an event has a stop method.
2890
+ function addStop(event) {
2891
+ if (!event.stop) event.stop = stopMethod;
2892
+ return event;
2893
+ }
2894
+
2895
+ function e_preventDefault(e) {
2896
+ if (e.preventDefault) e.preventDefault();
2897
+ else e.returnValue = false;
2898
+ }
2899
+ function e_stopPropagation(e) {
2900
+ if (e.stopPropagation) e.stopPropagation();
2901
+ else e.cancelBubble = true;
2902
+ }
2903
+ function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2904
+ CodeMirror.e_stop = e_stop;
2905
+ CodeMirror.e_preventDefault = e_preventDefault;
2906
+ CodeMirror.e_stopPropagation = e_stopPropagation;
2907
+
2908
+ function e_target(e) {return e.target || e.srcElement;}
2909
+ function e_button(e) {
2910
+ var b = e.which;
2911
+ if (b == null) {
2912
+ if (e.button & 1) b = 1;
2913
+ else if (e.button & 2) b = 3;
2914
+ else if (e.button & 4) b = 2;
2915
+ }
2916
+ if (mac && e.ctrlKey && b == 1) b = 3;
2917
+ return b;
2918
+ }
2919
+
2920
+ // Allow 3rd-party code to override event properties by adding an override
2921
+ // object to an event object.
2922
+ function e_prop(e, prop) {
2923
+ var overridden = e.override && e.override.hasOwnProperty(prop);
2924
+ return overridden ? e.override[prop] : e[prop];
2925
+ }
2926
+
2927
+ // Event handler registration. If disconnect is true, it'll return a
2928
+ // function that unregisters the handler.
2929
+ function connect(node, type, handler, disconnect) {
2930
+ if (typeof node.addEventListener == "function") {
2931
+ node.addEventListener(type, handler, false);
2932
+ if (disconnect) return function() {node.removeEventListener(type, handler, false);};
2933
+ } else {
2934
+ var wrapHandler = function(event) {handler(event || window.event);};
2935
+ node.attachEvent("on" + type, wrapHandler);
2936
+ if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
2937
+ }
2938
+ }
2939
+ CodeMirror.connect = connect;
2940
+
2941
+ function Delayed() {this.id = null;}
2942
+ Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
2943
+
2944
+ var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
2945
+
2946
+ // Detect drag-and-drop
2947
+ var dragAndDrop = function() {
2948
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
2949
+ // couldn't get it to work yet.
2950
+ if (ie_lt9) return false;
2951
+ var div = elt('div');
2952
+ return "draggable" in div || "dragDrop" in div;
2953
+ }();
2954
+
2955
+ // Feature-detect whether newlines in textareas are converted to \r\n
2956
+ var lineSep = function () {
2957
+ var te = elt("textarea");
2958
+ te.value = "foo\nbar";
2959
+ if (te.value.indexOf("\r") > -1) return "\r\n";
2960
+ return "\n";
2961
+ }();
2962
+
2963
+ // For a reason I have yet to figure out, some browsers disallow
2964
+ // word wrapping between certain characters *only* if a new inline
2965
+ // element is started between them. This makes it hard to reliably
2966
+ // measure the position of things, since that requires inserting an
2967
+ // extra span. This terribly fragile set of regexps matches the
2968
+ // character combinations that suffer from this phenomenon on the
2969
+ // various browsers.
2970
+ var spanAffectsWrapping = /^$/; // Won't match any two-character string
2971
+ if (gecko) spanAffectsWrapping = /$'/;
2972
+ else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
2973
+ else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
2974
+
2975
+ // Counts the column offset in a string, taking tabs into account.
2976
+ // Used mostly to find indentation.
2977
+ function countColumn(string, end, tabSize) {
2978
+ if (end == null) {
2979
+ end = string.search(/[^\s\u00a0]/);
2980
+ if (end == -1) end = string.length;
2981
+ }
2982
+ for (var i = 0, n = 0; i < end; ++i) {
2983
+ if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
2984
+ else ++n;
2985
+ }
2986
+ return n;
2987
+ }
2988
+
2989
+ function eltOffset(node, screen) {
2990
+ // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
2991
+ // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
2992
+ try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
2993
+ catch(e) { box = {top: 0, left: 0}; }
2994
+ if (!screen) {
2995
+ // Get the toplevel scroll, working around browser differences.
2996
+ if (window.pageYOffset == null) {
2997
+ var t = document.documentElement || document.body.parentNode;
2998
+ if (t.scrollTop == null) t = document.body;
2999
+ box.top += t.scrollTop; box.left += t.scrollLeft;
3000
+ } else {
3001
+ box.top += window.pageYOffset; box.left += window.pageXOffset;
3002
+ }
3003
+ }
3004
+ return box;
3005
+ }
3006
+
3007
+ function eltText(node) {
3008
+ return node.textContent || node.innerText || node.nodeValue || "";
3009
+ }
3010
+
3011
+ var spaceStrs = [""];
3012
+ function spaceStr(n) {
3013
+ while (spaceStrs.length <= n)
3014
+ spaceStrs.push(lst(spaceStrs) + " ");
3015
+ return spaceStrs[n];
3016
+ }
3017
+
3018
+ function lst(arr) { return arr[arr.length-1]; }
3019
+
3020
+ function selectInput(node) {
3021
+ if (ios) { // Mobile Safari apparently has a bug where select() is broken.
3022
+ node.selectionStart = 0;
3023
+ node.selectionEnd = node.value.length;
3024
+ } else node.select();
3025
+ }
3026
+
3027
+ // Operations on {line, ch} objects.
3028
+ function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
3029
+ function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
3030
+ function copyPos(x) {return {line: x.line, ch: x.ch};}
3031
+
3032
+ function elt(tag, content, className, style) {
3033
+ var e = document.createElement(tag);
3034
+ if (className) e.className = className;
3035
+ if (style) e.style.cssText = style;
3036
+ if (typeof content == "string") setTextContent(e, content);
3037
+ else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
3038
+ return e;
3039
+ }
3040
+ function removeChildren(e) {
3041
+ e.innerHTML = "";
3042
+ return e;
3043
+ }
3044
+ function removeChildrenAndAdd(parent, e) {
3045
+ removeChildren(parent).appendChild(e);
3046
+ }
3047
+ function setTextContent(e, str) {
3048
+ if (ie_lt9) {
3049
+ e.innerHTML = "";
3050
+ e.appendChild(document.createTextNode(str));
3051
+ } else e.textContent = str;
3052
+ }
3053
+
3054
+ // Used to position the cursor after an undo/redo by finding the
3055
+ // last edited character.
3056
+ function editEnd(from, to) {
3057
+ if (!to) return 0;
3058
+ if (!from) return to.length;
3059
+ for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
3060
+ if (from.charAt(i) != to.charAt(j)) break;
3061
+ return j + 1;
3062
+ }
3063
+
3064
+ function indexOf(collection, elt) {
3065
+ if (collection.indexOf) return collection.indexOf(elt);
3066
+ for (var i = 0, e = collection.length; i < e; ++i)
3067
+ if (collection[i] == elt) return i;
3068
+ return -1;
3069
+ }
3070
+ function isWordChar(ch) {
3071
+ return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase();
3072
+ }
3073
+
3074
+ // See if "".split is the broken IE version, if so, provide an
3075
+ // alternative way to split lines.
3076
+ var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
3077
+ var pos = 0, result = [], l = string.length;
3078
+ while (pos <= l) {
3079
+ var nl = string.indexOf("\n", pos);
3080
+ if (nl == -1) nl = string.length;
3081
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
3082
+ var rt = line.indexOf("\r");
3083
+ if (rt != -1) {
3084
+ result.push(line.slice(0, rt));
3085
+ pos += rt + 1;
3086
+ } else {
3087
+ result.push(line);
3088
+ pos = nl + 1;
3089
+ }
3090
+ }
3091
+ return result;
3092
+ } : function(string){return string.split(/\r\n?|\n/);};
3093
+ CodeMirror.splitLines = splitLines;
3094
+
3095
+ var hasSelection = window.getSelection ? function(te) {
3096
+ try { return te.selectionStart != te.selectionEnd; }
3097
+ catch(e) { return false; }
3098
+ } : function(te) {
3099
+ try {var range = te.ownerDocument.selection.createRange();}
3100
+ catch(e) {}
3101
+ if (!range || range.parentElement() != te) return false;
3102
+ return range.compareEndPoints("StartToEnd", range) != 0;
3103
+ };
3104
+
3105
+ CodeMirror.defineMode("null", function() {
3106
+ return {token: function(stream) {stream.skipToEnd();}};
3107
+ });
3108
+ CodeMirror.defineMIME("text/plain", "null");
3109
+
3110
+ var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
3111
+ 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
3112
+ 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
3113
+ 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
3114
+ 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
3115
+ 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
3116
+ 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
3117
+ CodeMirror.keyNames = keyNames;
3118
+ (function() {
3119
+ // Number keys
3120
+ for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
3121
+ // Alphabetic keys
3122
+ for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
3123
+ // Function keys
3124
+ for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
3125
+ })();
3126
+
3127
+ CodeMirror.version = "2.33 +";
3128
+
3129
+ return CodeMirror;
3130
+ })();