luca 0.9.9 → 0.9.42

Sign up to get free protection for your applications and to get access to all the features.
Files changed (604) hide show
  1. data/CHANGELOG +9 -153
  2. data/Gemfile +1 -10
  3. data/Gemfile.lock +40 -113
  4. data/Guardfile +25 -3
  5. data/README.md +57 -54
  6. data/ROADMAP +32 -15
  7. data/Rakefile +75 -24
  8. data/app.rb +46 -12
  9. data/{app/assets → assets}/images/glyphicons-halflings-white.png +0 -0
  10. data/{app/assets → assets}/images/glyphicons-halflings.png +0 -0
  11. data/assets/javascripts/dependencies/backbone-min.js +37 -0
  12. data/assets/javascripts/dependencies/backbone-query.min.js +1 -0
  13. data/assets/javascripts/dependencies/bootstrap.min.js +7 -0
  14. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-coffeescript.js +0 -0
  15. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-css.js +0 -0
  16. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-html.js +0 -0
  17. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-javascript.js +0 -0
  18. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-less.js +0 -0
  19. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-vim.js +0 -0
  20. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror.js +0 -0
  21. data/assets/javascripts/dependencies/coffee-script.js +12189 -0
  22. data/{spec → assets}/javascripts/dependencies/jasmine-html.js +0 -0
  23. data/{spec → assets}/javascripts/dependencies/jasmine.js +0 -0
  24. data/assets/javascripts/dependencies/jquery.js +4 -0
  25. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/modal.js +0 -0
  26. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/modernizr.min.js +0 -0
  27. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/prettify.js +0 -0
  28. data/{spec → assets}/javascripts/dependencies/sinon.js +0 -0
  29. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/spin-min.js +0 -0
  30. data/assets/javascripts/dependencies/underscore-min.js +31 -0
  31. data/assets/javascripts/dependencies/underscore-string.min.js +14 -0
  32. data/assets/javascripts/dependencies.coffee +5 -0
  33. data/assets/javascripts/luca/index.coffee +1 -0
  34. data/assets/javascripts/luca-templates.js +1 -0
  35. data/assets/javascripts/luca-ui-base.coffee +1 -0
  36. data/assets/javascripts/luca-ui-bootstrap.js +1 -0
  37. data/assets/javascripts/luca-ui-development-tools.coffee +9 -0
  38. data/assets/javascripts/luca-ui-full.js +3 -0
  39. data/assets/javascripts/luca-ui-spec.coffee +2 -0
  40. data/assets/javascripts/luca-ui.js +3 -0
  41. data/assets/javascripts/sandbox/application.coffee +57 -0
  42. data/assets/javascripts/sandbox/config.coffee +7 -0
  43. data/assets/javascripts/sandbox/router.coffee +24 -0
  44. data/assets/javascripts/sandbox/templates/builder/component_list.luca +1 -0
  45. data/assets/javascripts/sandbox/templates/builder.luca +2 -0
  46. data/assets/javascripts/sandbox/templates/main.luca +53 -0
  47. data/assets/javascripts/sandbox/templates/sandbox/docs_index.luca +1 -0
  48. data/assets/javascripts/sandbox/templates/sandbox/navigation.luca +8 -0
  49. data/assets/javascripts/sandbox/templates/sandbox/readme.luca +30 -0
  50. data/assets/javascripts/sandbox/templates/sandbox.luca +1 -0
  51. data/assets/javascripts/sandbox/views/builder/builder_canvas.coffee +3 -0
  52. data/assets/javascripts/sandbox/views/builder/builder_editor.coffee +6 -0
  53. data/assets/javascripts/sandbox/views/builder/component_list.coffee +38 -0
  54. data/assets/javascripts/sandbox/views/builder/project_browser.coffee +14 -0
  55. data/assets/javascripts/sandbox/views/builder.coffee +133 -0
  56. data/assets/javascripts/sandbox/views/docs_controller.coffee +7 -0
  57. data/assets/javascripts/sandbox/views/inspector/instance_filter.coffee +18 -0
  58. data/{app/assets/stylesheets/luca/containers/modal_view.scss → assets/javascripts/sandbox/views/inspector/instance_list.coffee} +0 -0
  59. data/assets/javascripts/sandbox/views/inspector.coffee +11 -0
  60. data/assets/javascripts/sandbox/views/top_navigation.coffee +4 -0
  61. data/assets/javascripts/sandbox.coffee +7 -0
  62. data/assets/javascripts/spec-dependencies.coffee +4 -0
  63. data/assets/stylesheets/bootstrap-responsive.min.css +2 -0
  64. data/assets/stylesheets/bootstrap.min.css +727 -0
  65. data/{vendor/assets → assets}/stylesheets/codemirror-blackboard.css +0 -0
  66. data/{vendor/assets → assets}/stylesheets/codemirror-monokai.css +0 -0
  67. data/{vendor/assets → assets}/stylesheets/codemirror.css +0 -0
  68. data/{vendor/assets → assets}/stylesheets/jasmine.css +0 -0
  69. data/assets/stylesheets/luca-ui-bootstrap.css +4 -0
  70. data/assets/stylesheets/luca-ui-development-tools.css +5 -0
  71. data/assets/stylesheets/luca-ui-full.css +3 -0
  72. data/assets/stylesheets/luca-ui-spec.css +3 -0
  73. data/assets/stylesheets/luca-ui.css +3 -0
  74. data/assets/stylesheets/prettify.css +40 -0
  75. data/assets/stylesheets/sandbox/builder.scss +79 -0
  76. data/assets/stylesheets/sandbox/sandbox.scss +18 -0
  77. data/assets/stylesheets/sandbox.css +3 -0
  78. data/assets/stylesheets/themes/amelia-bootstrap.css +826 -0
  79. data/assets/stylesheets/themes/slate-bootstrap.css +797 -0
  80. data/assets/stylesheets/themes/superhero-bootstrap.css +830 -0
  81. data/config.ru +2 -1
  82. data/docs/{old/application.md → application.md} +0 -0
  83. data/docs/{old/collection.md → collection.md} +0 -0
  84. data/docs/{old/collection_manager.md → collection_manager.md} +0 -0
  85. data/docs/{old/container_philosophy.md → container_philosophy.md} +0 -0
  86. data/docs/{old/event_binding_helpers.md → event_binding_helpers.md} +0 -0
  87. data/docs/{old/method_caching_and_computed_properties.md → method_caching_and_computed_properties.md} +0 -0
  88. data/docs/{old/view.md → view.md} +0 -0
  89. data/lib/luca/code_browser.rb +55 -0
  90. data/lib/luca/command_line.rb +69 -0
  91. data/lib/luca/rails/engine.rb +0 -12
  92. data/lib/luca/rails/version.rb +2 -1
  93. data/lib/luca/rails.rb +3 -5
  94. data/lib/luca/template.rb +0 -2
  95. data/lib/luca.rb +1 -25
  96. data/luca.gemspec +7 -16
  97. data/site/assets/bootstrap.min.js +7 -0
  98. data/site/assets/dependencies.js +94 -0
  99. data/site/assets/glyphicons-halflings-white.png +0 -0
  100. data/site/assets/glyphicons-halflings.png +0 -0
  101. data/site/assets/luca-ui-bootstrap.css +1331 -0
  102. data/site/assets/luca-ui-bootstrap.js +9 -0
  103. data/site/assets/luca-ui-development-tools.css +234 -0
  104. data/site/assets/luca-ui-development-tools.js +18561 -0
  105. data/site/assets/luca-ui-development-tools.min.js +15 -0
  106. data/site/assets/luca-ui-full.min.js +8 -0
  107. data/site/assets/luca-ui.min.js +4 -0
  108. data/site/assets/sandbox.css +62 -0
  109. data/site/assets/sandbox.js +469 -0
  110. data/site/docs/application.html +41 -0
  111. data/site/docs/caching.html +43 -0
  112. data/site/docs/collection.html +75 -0
  113. data/site/docs/collection_manager.html +71 -0
  114. data/site/docs/containers.html +118 -0
  115. data/site/docs/events.html +153 -0
  116. data/site/docs/view.html +128 -0
  117. data/site/img/glyphicons-halflings-white.png +0 -0
  118. data/site/img/glyphicons-halflings.png +0 -0
  119. data/site/index.html +20 -0
  120. data/site/source-map.js +1 -0
  121. data/spec/{javascripts/components → components}/application_spec.coffee +0 -0
  122. data/spec/{javascripts/components → components}/collection_loader_view_spec.coffee +0 -0
  123. data/{site/source/app/assets/javascripts/docs/lib/util.coffee → spec/components/controller_spec.coffee} +0 -0
  124. data/spec/{javascripts/components → components}/fields/checkbox_array_spec.coffee +0 -0
  125. data/spec/components/form_view_spec.coffee +80 -0
  126. data/spec/{javascripts/components → components}/grid_view_spec.coffee +0 -0
  127. data/spec/{javascripts/components → components}/record_manager_spec.coffee +0 -0
  128. data/spec/{javascripts/components → components}/template_spec.coffee +0 -0
  129. data/spec/containers/card_view_spec.coffee +50 -0
  130. data/{site/source/app/assets/javascripts/docs/views/components/code_editor/index.coffee → spec/containers/column_view_spec.coffee} +0 -0
  131. data/spec/{javascripts/containers → containers}/modal_view_spec.coffee +0 -0
  132. data/spec/{javascripts/containers → containers}/panel_view_spec.coffee +0 -0
  133. data/spec/{javascripts/components/pagination_control_spec.coffee → containers/split_view_spec.coffee} +0 -0
  134. data/spec/{javascripts/containers → containers}/tab_view_spec.coffee +0 -0
  135. data/spec/{javascripts/containers → containers}/viewport_spec.coffee +0 -0
  136. data/spec/{javascripts/core → core}/collection_spec.coffee +1 -26
  137. data/spec/core/container_spec.coffee +66 -0
  138. data/spec/{javascripts/concerns/paginatable_spec.coffee → core/field_spec.coffee} +0 -0
  139. data/spec/{javascripts/core → core}/model_spec.coffee +3 -26
  140. data/spec/{javascripts/core → core}/observer_spec.coffee +0 -0
  141. data/spec/{javascripts/core → core}/view_spec.coffee +38 -114
  142. data/spec/{javascripts/core/framework_spec.coffee → framework_spec.coffee} +2 -31
  143. data/spec/{javascripts/helper.coffee → helper.coffee} +0 -0
  144. data/spec/{javascripts/managers → managers}/collection_manager_spec.coffee +0 -0
  145. data/spec/{javascripts/managers → managers}/socket_manager_spec.coffee +0 -0
  146. data/src/components/application.coffee +318 -0
  147. data/src/components/base_toolbar.coffee +15 -0
  148. data/{app/assets/javascripts/luca → src}/components/collection_loader_view.coffee +1 -3
  149. data/src/components/collection_view.coffee +131 -0
  150. data/src/components/controller.coffee +63 -0
  151. data/src/components/fields/button_field.coffee +38 -0
  152. data/{app/assets/javascripts/luca → src}/components/fields/checkbox_array.coffee +7 -18
  153. data/{app/assets/javascripts/luca → src}/components/fields/checkbox_field.coffee +10 -19
  154. data/{app/assets/javascripts/luca → src}/components/fields/file_upload_field.coffee +4 -5
  155. data/src/components/fields/hidden_field.coffee +12 -0
  156. data/src/components/fields/label_field.coffee +14 -0
  157. data/src/components/fields/select_field.coffee +97 -0
  158. data/src/components/fields/text_area_field.coffee +40 -0
  159. data/{app/assets/javascripts/luca → src}/components/fields/text_field.coffee +5 -16
  160. data/src/components/fields/type_ahead_field.coffee +22 -0
  161. data/src/components/form_button_toolbar.coffee +25 -0
  162. data/src/components/form_view.coffee +275 -0
  163. data/src/components/grid_view.coffee +269 -0
  164. data/src/components/index.coffee +1 -0
  165. data/src/components/load_mask.coffee +3 -0
  166. data/src/components/nav_bar.coffee +22 -0
  167. data/src/components/page_controller.coffee +2 -0
  168. data/{app/assets/javascripts/luca → src}/components/record_manager.coffee +0 -0
  169. data/{app/assets/javascripts/luca → src}/components/router.coffee +1 -6
  170. data/src/components/table_view.coffee +53 -0
  171. data/src/components/template.coffee +5 -0
  172. data/src/components/toolbar_dialog.coffee +25 -0
  173. data/src/containers/card_view.coffee +93 -0
  174. data/src/containers/column_view.coffee +42 -0
  175. data/{app/assets/javascripts/luca → src}/containers/modal_view.coffee +9 -9
  176. data/src/containers/page_view.coffee +2 -0
  177. data/{app/assets/javascripts/luca → src}/containers/panel_toolbar.coffee +84 -89
  178. data/src/containers/panel_view.coffee +23 -0
  179. data/src/containers/split_view.coffee +8 -0
  180. data/{app/assets/javascripts/luca → src}/containers/tab_view.coffee +21 -27
  181. data/{app/assets/javascripts/luca → src}/containers/viewport.coffee +18 -14
  182. data/{app/assets/javascripts/luca → src}/core/collection.coffee +51 -97
  183. data/src/core/container.coffee +374 -0
  184. data/{tutorials/component-definitions.md → src/core/core.coffee} +0 -0
  185. data/src/core/field.coffee +79 -0
  186. data/src/core/model.coffee +43 -0
  187. data/{app/assets/javascripts/luca → src}/core/observer.coffee +0 -0
  188. data/src/core/panel.coffee +118 -0
  189. data/{app/assets/javascripts/luca/core/registry → src/core}/registry.coffee +43 -63
  190. data/{app/assets/javascripts/luca/util → src/core}/script_loader.coffee +0 -0
  191. data/src/core/view.coffee +264 -0
  192. data/src/define.coffee +110 -0
  193. data/{app/assets/javascripts/luca → src}/framework.coffee +103 -98
  194. data/src/index.coffee +23 -0
  195. data/{app/assets/javascripts/luca → src}/managers/collection_manager.coffee +13 -37
  196. data/src/managers/socket_manager.coffee +54 -0
  197. data/{app/assets/javascripts/luca/concerns → src/modules}/deferrable.coffee +4 -5
  198. data/src/modules/grid_layout.coffee +3 -0
  199. data/{app/assets/javascripts/luca/concerns/loadmaskable.coffee → src/modules/load_mask.coffee} +11 -23
  200. data/{app/assets/javascripts/luca/concerns → src/modules}/local_storage.coffee +0 -0
  201. data/src/plugins/development_tool_helpers.coffee +21 -0
  202. data/src/plugins/events.coffee +54 -0
  203. data/src/samples/definition.coffee +49 -0
  204. data/{tutorials/component-definitions/01_intro.md → src/stylesheets/base.scss} +0 -0
  205. data/{app/assets/stylesheets/luca → src/stylesheets}/components/checkbox_array.scss +1 -1
  206. data/src/stylesheets/components/form_view.scss +59 -0
  207. data/{app/assets/stylesheets/luca → src/stylesheets}/components/grid_view.scss +0 -0
  208. data/{app/assets/stylesheets/luca → src/stylesheets}/components/load_mask.scss +0 -0
  209. data/src/stylesheets/components/toolbar.scss +0 -0
  210. data/{app/assets/stylesheets/luca → src/stylesheets}/components/viewport.scss +4 -1
  211. data/src/stylesheets/containers/container.scss +16 -0
  212. data/src/stylesheets/containers/modal_view.scss +0 -0
  213. data/{app/assets/stylesheets/luca → src/stylesheets}/containers/panels.scss +0 -0
  214. data/{app/assets/stylesheets/luca → src/stylesheets}/containers/tab_view.scss +5 -5
  215. data/{app/assets/stylesheets/luca → src/stylesheets}/normalize.scss +0 -0
  216. data/src/stylesheets/tools/class_browser.scss +32 -0
  217. data/src/stylesheets/tools/code_editor.scss +24 -0
  218. data/src/stylesheets/tools/component_tester.scss +26 -0
  219. data/{app/assets/stylesheets/luca/development → src/stylesheets/tools}/console.scss +0 -0
  220. data/src/templates/components/bootstrap_form_controls.luca +7 -0
  221. data/src/templates/components/collection_loader_view.luca +5 -0
  222. data/src/templates/components/form_alert +0 -0
  223. data/src/templates/components/form_alert.luca +3 -0
  224. data/src/templates/components/grid_view.luca +7 -0
  225. data/src/templates/components/grid_view_empty_text.luca +3 -0
  226. data/src/templates/components/load_mask.luca +3 -0
  227. data/src/templates/components/nav_bar.luca +2 -0
  228. data/src/templates/containers/basic.luca +1 -0
  229. data/src/templates/containers/tab_selector_container.luca +8 -0
  230. data/src/templates/containers/tab_view.luca +2 -0
  231. data/src/templates/containers/toolbar_wrapper.luca +1 -0
  232. data/src/templates/fields/button_field.luca +2 -0
  233. data/src/templates/fields/button_field_link.luca +5 -0
  234. data/src/templates/fields/checkbox_array.luca +4 -0
  235. data/src/templates/fields/checkbox_array_item.luca +4 -0
  236. data/src/templates/fields/checkbox_field.luca +9 -0
  237. data/src/templates/fields/file_upload_field.luca +8 -0
  238. data/src/templates/fields/hidden_field.luca +1 -0
  239. data/src/templates/fields/select_field.luca +8 -0
  240. data/src/templates/fields/text_area_field.luca +8 -0
  241. data/src/templates/fields/text_field.luca +17 -0
  242. data/src/templates/sample/contents.luca +1 -0
  243. data/src/templates/sample/welcome.luca +1 -0
  244. data/src/templates/table_view.luca +4 -0
  245. data/src/tools/application_inspector.coffee +2 -0
  246. data/src/tools/code_editor.coffee +258 -0
  247. data/{app/assets/javascripts/luca/development → src/tools}/code_mirror_field.coffee +1 -2
  248. data/src/tools/coffee_script_editor.coffee +82 -0
  249. data/src/tools/collection_inspector.coffee +4 -0
  250. data/src/tools/collections/components.coffee +59 -0
  251. data/src/tools/collections/instances.coffee +15 -0
  252. data/src/tools/component_tester.coffee +462 -0
  253. data/{app/assets/javascripts/luca/development → src/tools}/console.coffee +24 -55
  254. data/src/tools/models/components.coffee +25 -0
  255. data/src/tools/models/instance.coffee +2 -0
  256. data/src/tools/templates/component_tester/help.luca +14 -0
  257. data/{app/assets/javascripts/luca/util/luca.coffee → src/util.coffee} +6 -90
  258. data/vendor/assets/javascripts/luca-ui-development-tools.js +18533 -0
  259. data/vendor/assets/javascripts/luca-ui-development-tools.min.js +15 -0
  260. data/vendor/assets/javascripts/luca-ui-full.js +4806 -0
  261. data/vendor/assets/javascripts/luca-ui-full.min.js +8 -0
  262. data/vendor/assets/javascripts/luca-ui-templates.js +24 -0
  263. data/vendor/assets/javascripts/luca-ui.js +1703 -4394
  264. data/vendor/assets/javascripts/luca-ui.min.js +4 -5
  265. data/vendor/assets/luca-ui/base.css +85 -0
  266. data/vendor/assets/luca-ui/components/application.js +91 -0
  267. data/vendor/assets/luca-ui/components/base_toolbar.js +23 -0
  268. data/vendor/assets/luca-ui/components/controller.js +38 -0
  269. data/vendor/assets/luca-ui/components/fields/button_field.js +45 -0
  270. data/vendor/assets/luca-ui/components/fields/checkbox_field.js +43 -0
  271. data/vendor/assets/luca-ui/components/fields/file_upload_field.js +20 -0
  272. data/vendor/assets/luca-ui/components/fields/hidden_field.js +20 -0
  273. data/vendor/assets/luca-ui/components/fields/select_field.js +97 -0
  274. data/vendor/assets/luca-ui/components/fields/text_area_field.js +48 -0
  275. data/vendor/assets/luca-ui/components/fields/text_field.js +46 -0
  276. data/vendor/assets/luca-ui/components/fields/type_ahead_field.js +13 -0
  277. data/vendor/assets/luca-ui/components/form_button_toolbar.js +32 -0
  278. data/vendor/assets/luca-ui/components/form_view.css +32 -0
  279. data/vendor/assets/luca-ui/components/form_view.js +207 -0
  280. data/{app/assets/stylesheets/luca/components/table_view.scss → vendor/assets/luca-ui/components/grid_view.css} +51 -60
  281. data/vendor/assets/luca-ui/components/grid_view.js +202 -0
  282. data/vendor/assets/luca-ui/components/record_manager.js +207 -0
  283. data/vendor/assets/luca-ui/components/router.js +36 -0
  284. data/vendor/assets/luca-ui/components/template.js +26 -0
  285. data/vendor/assets/luca-ui/components/toolbar.css +11 -0
  286. data/vendor/assets/luca-ui/containers/card_view.js +98 -0
  287. data/vendor/assets/luca-ui/containers/column_view.js +52 -0
  288. data/vendor/assets/luca-ui/containers/container.css +3 -0
  289. data/vendor/assets/luca-ui/containers/modal_view.css +0 -0
  290. data/vendor/assets/luca-ui/containers/modal_view.js +87 -0
  291. data/vendor/assets/luca-ui/containers/panel_view.js +34 -0
  292. data/vendor/assets/luca-ui/containers/split_view.js +13 -0
  293. data/vendor/assets/luca-ui/containers/tab_view.css +16 -0
  294. data/vendor/assets/luca-ui/containers/tab_view.js +80 -0
  295. data/vendor/assets/luca-ui/containers/viewport.js +18 -0
  296. data/vendor/assets/luca-ui/core/collection.js +221 -0
  297. data/vendor/assets/luca-ui/core/container.js +205 -0
  298. data/vendor/assets/luca-ui/core/field.js +59 -0
  299. data/vendor/assets/luca-ui/core/observer.js +42 -0
  300. data/vendor/assets/luca-ui/core/view.js +127 -0
  301. data/vendor/assets/luca-ui/framework.js +110 -0
  302. data/vendor/assets/luca-ui/index.js +5 -0
  303. data/vendor/assets/luca-ui/managers/collection_manager.js +98 -0
  304. data/vendor/assets/luca-ui/managers/socket_manager.js +52 -0
  305. data/vendor/assets/luca-ui/modules/deferrable.js +21 -0
  306. data/vendor/assets/luca-ui/modules/local_storage.js +81 -0
  307. data/vendor/assets/luca-ui/normalize.css +359 -0
  308. data/vendor/assets/luca-ui/stylesheets/base.css +85 -0
  309. data/vendor/assets/luca-ui/stylesheets/components/form_view.css +32 -0
  310. data/vendor/assets/luca-ui/stylesheets/components/grid_view.css +76 -0
  311. data/vendor/assets/luca-ui/stylesheets/components/toolbar.css +11 -0
  312. data/vendor/assets/luca-ui/stylesheets/containers/container.css +3 -0
  313. data/vendor/assets/luca-ui/stylesheets/containers/modal_view.css +0 -0
  314. data/vendor/assets/luca-ui/stylesheets/containers/tab_view.css +16 -0
  315. data/vendor/assets/luca-ui/stylesheets/normalize.css +359 -0
  316. data/vendor/assets/luca-ui/templates/components/bootstrap_form_controls.js +4 -0
  317. data/vendor/assets/luca-ui/templates/components/form_view.js +4 -0
  318. data/vendor/assets/luca-ui/templates/components/grid_view.js +4 -0
  319. data/vendor/assets/luca-ui/templates/components/grid_view_empty_text.js +4 -0
  320. data/vendor/assets/luca-ui/templates/containers/basic.js +4 -0
  321. data/vendor/assets/luca-ui/templates/containers/tab_selector_container.js +4 -0
  322. data/vendor/assets/luca-ui/templates/containers/tab_view.js +4 -0
  323. data/vendor/assets/luca-ui/templates/containers/toolbar_wrapper.js +4 -0
  324. data/vendor/assets/luca-ui/templates/fields/button_field.js +4 -0
  325. data/vendor/assets/luca-ui/templates/fields/button_field_link.js +4 -0
  326. data/vendor/assets/luca-ui/templates/fields/checkbox_field.js +4 -0
  327. data/vendor/assets/luca-ui/templates/fields/file_upload_field.js +4 -0
  328. data/vendor/assets/luca-ui/templates/fields/hidden_field.js +4 -0
  329. data/vendor/assets/luca-ui/templates/fields/select_field.js +4 -0
  330. data/vendor/assets/luca-ui/templates/fields/text_area_field.js +4 -0
  331. data/vendor/assets/luca-ui/templates/fields/text_field.js +4 -0
  332. data/vendor/assets/luca-ui/templates/sample/contents.js +4 -0
  333. data/vendor/assets/luca-ui/templates/sample/welcome.js +4 -0
  334. data/vendor/assets/stylesheets/luca-ui.css +471 -68
  335. data/views/jasmine.erb +2 -2
  336. metadata +328 -496
  337. data/app/assets/javascripts/luca/basic.coffee +0 -8
  338. data/app/assets/javascripts/luca/components/application.coffee +0 -530
  339. data/app/assets/javascripts/luca/components/collection_view.coffee +0 -211
  340. data/app/assets/javascripts/luca/components/controller.coffee +0 -176
  341. data/app/assets/javascripts/luca/components/fields/base.coffee +0 -144
  342. data/app/assets/javascripts/luca/components/fields/button_field.coffee +0 -91
  343. data/app/assets/javascripts/luca/components/fields/hidden_field.coffee +0 -11
  344. data/app/assets/javascripts/luca/components/fields/label_field.coffee +0 -12
  345. data/app/assets/javascripts/luca/components/fields/select_field.coffee +0 -159
  346. data/app/assets/javascripts/luca/components/fields/text_area_field.coffee +0 -57
  347. data/app/assets/javascripts/luca/components/fields/type_ahead_field.coffee +0 -20
  348. data/app/assets/javascripts/luca/components/form_view.coffee +0 -421
  349. data/app/assets/javascripts/luca/components/grid_layout_view.coffee +0 -42
  350. data/app/assets/javascripts/luca/components/index.coffee +0 -7
  351. data/app/assets/javascripts/luca/components/load_mask.coffee +0 -8
  352. data/app/assets/javascripts/luca/components/multi_collection_view.coffee +0 -92
  353. data/app/assets/javascripts/luca/components/nav_bar.coffee +0 -80
  354. data/app/assets/javascripts/luca/components/page.coffee +0 -70
  355. data/app/assets/javascripts/luca/components/pagination_control.coffee +0 -104
  356. data/app/assets/javascripts/luca/components/simple_collection_view.coffee +0 -10
  357. data/app/assets/javascripts/luca/components/table_view.coffee +0 -93
  358. data/app/assets/javascripts/luca/components/table_view_scrollable.coffee +0 -23
  359. data/app/assets/javascripts/luca/concerns/application_event_bindings.coffee +0 -19
  360. data/app/assets/javascripts/luca/concerns/collection_event_bindings.coffee +0 -47
  361. data/app/assets/javascripts/luca/concerns/development_tool_helpers.coffee +0 -30
  362. data/app/assets/javascripts/luca/concerns/dom_helpers.coffee +0 -61
  363. data/app/assets/javascripts/luca/concerns/enhanced_properties.coffee +0 -23
  364. data/app/assets/javascripts/luca/concerns/filterable.coffee +0 -101
  365. data/app/assets/javascripts/luca/concerns/form_model_bindings.coffee +0 -20
  366. data/app/assets/javascripts/luca/concerns/grid_layout.coffee +0 -15
  367. data/app/assets/javascripts/luca/concerns/modal_view.coffee +0 -63
  368. data/app/assets/javascripts/luca/concerns/model_presenter.coffee +0 -23
  369. data/app/assets/javascripts/luca/concerns/paginatable.coffee +0 -79
  370. data/app/assets/javascripts/luca/concerns/query_collection_bindings.coffee +0 -52
  371. data/app/assets/javascripts/luca/concerns/sortable.coffee +0 -69
  372. data/app/assets/javascripts/luca/concerns/state_model.coffee +0 -58
  373. data/app/assets/javascripts/luca/concerns/templating.coffee +0 -13
  374. data/app/assets/javascripts/luca/config.coffee +0 -54
  375. data/app/assets/javascripts/luca/containers/card_view.coffee +0 -178
  376. data/app/assets/javascripts/luca/containers/container.coffee +0 -711
  377. data/app/assets/javascripts/luca/containers/index.coffee +0 -2
  378. data/app/assets/javascripts/luca/containers/page_controller.coffee +0 -25
  379. data/app/assets/javascripts/luca/core/events.coffee +0 -114
  380. data/app/assets/javascripts/luca/core/index.coffee +0 -12
  381. data/app/assets/javascripts/luca/core/model.coffee +0 -56
  382. data/app/assets/javascripts/luca/core/panel.coffee +0 -124
  383. data/app/assets/javascripts/luca/core/registry/component_definition.coffee +0 -319
  384. data/app/assets/javascripts/luca/core/registry/concerns.coffee +0 -70
  385. data/app/assets/javascripts/luca/core/registry/index.coffee +0 -4
  386. data/app/assets/javascripts/luca/core/registry/meta_data.coffee +0 -99
  387. data/app/assets/javascripts/luca/core/templates.coffee +0 -51
  388. data/app/assets/javascripts/luca/core/view.coffee +0 -383
  389. data/app/assets/javascripts/luca/dependencies.coffee +0 -9
  390. data/app/assets/javascripts/luca/development/code_sync_manager.coffee +0 -173
  391. data/app/assets/javascripts/luca/development/component.coffee +0 -76
  392. data/app/assets/javascripts/luca/development/components.coffee +0 -57
  393. data/app/assets/javascripts/luca/development/index.coffee +0 -5
  394. data/app/assets/javascripts/luca/index.coffee +0 -9
  395. data/app/assets/javascripts/luca/managers/index.coffee +0 -2
  396. data/app/assets/javascripts/luca/managers/socket_manager.coffee +0 -89
  397. data/app/assets/javascripts/luca/templates/components/bootstrap_form_controls.jst.ejs +0 -10
  398. data/app/assets/javascripts/luca/templates/components/collection_loader_view.jst.ejs +0 -6
  399. data/app/assets/javascripts/luca/templates/components/form_alert.jst.ejs +0 -4
  400. data/app/assets/javascripts/luca/templates/components/grid_view.jst.ejs +0 -11
  401. data/app/assets/javascripts/luca/templates/components/grid_view_empty_text.jst.ejs +0 -3
  402. data/app/assets/javascripts/luca/templates/components/load_mask.jst.ejs +0 -5
  403. data/app/assets/javascripts/luca/templates/components/nav_bar.jst.ejs +0 -19
  404. data/app/assets/javascripts/luca/templates/components/pagination.jst.ejs +0 -10
  405. data/app/assets/javascripts/luca/templates/components/table_view.jst.ejs +0 -4
  406. data/app/assets/javascripts/luca/templates/containers/basic.jst.ejs +0 -1
  407. data/app/assets/javascripts/luca/templates/containers/tab_selector_container.jst.ejs +0 -12
  408. data/app/assets/javascripts/luca/templates/containers/tab_view.jst.ejs +0 -2
  409. data/app/assets/javascripts/luca/templates/containers/toolbar_wrapper.jst.ejs +0 -1
  410. data/app/assets/javascripts/luca/templates/fields/button_field.jst.ejs +0 -2
  411. data/app/assets/javascripts/luca/templates/fields/button_field_link.jst.ejs +0 -6
  412. data/app/assets/javascripts/luca/templates/fields/checkbox_array.jst.ejs +0 -4
  413. data/app/assets/javascripts/luca/templates/fields/checkbox_array_item.jst.ejs +0 -3
  414. data/app/assets/javascripts/luca/templates/fields/checkbox_field.jst.ejs +0 -10
  415. data/app/assets/javascripts/luca/templates/fields/file_upload_field.jst.ejs +0 -10
  416. data/app/assets/javascripts/luca/templates/fields/hidden_field.jst.ejs +0 -1
  417. data/app/assets/javascripts/luca/templates/fields/select_field.jst.ejs +0 -11
  418. data/app/assets/javascripts/luca/templates/fields/text_area_field.jst.ejs +0 -11
  419. data/app/assets/javascripts/luca/templates/fields/text_field.jst.ejs +0 -16
  420. data/app/assets/javascripts/luca/util/deprecations.coffee +0 -18
  421. data/app/assets/javascripts/luca/util/index.coffee +0 -4
  422. data/app/assets/javascripts/luca/util/keybindings.coffee +0 -24
  423. data/app/assets/javascripts/luca/util/logging.coffee +0 -30
  424. data/app/assets/javascripts/luca-ui.js +0 -1
  425. data/app/assets/stylesheets/luca/components/form_view.scss +0 -7
  426. data/app/assets/stylesheets/luca/containers/container.scss +0 -19
  427. data/app/assets/stylesheets/luca/development/index.css +0 -3
  428. data/app/assets/stylesheets/luca/index.css +0 -4
  429. data/bin/luca +0 -14
  430. data/docs/framework.json +0 -1
  431. data/docs/luca-framework-documentation.js +0 -1
  432. data/lib/generators/luca/application/application_generator.rb +0 -75
  433. data/lib/generators/luca/application/templates/controller.rb +0 -4
  434. data/lib/generators/luca/application/templates/index.html.erb +0 -19
  435. data/lib/generators/luca/application/templates/index.html.haml +0 -7
  436. data/lib/generators/luca/application/templates/javascripts/application.coffee +0 -18
  437. data/lib/generators/luca/application/templates/javascripts/collection_manager.coffee +0 -2
  438. data/lib/generators/luca/application/templates/javascripts/config.coffee +0 -3
  439. data/lib/generators/luca/application/templates/javascripts/dependencies.coffee +0 -3
  440. data/lib/generators/luca/application/templates/javascripts/home.jst.ejs +0 -2
  441. data/lib/generators/luca/application/templates/javascripts/index.coffee +0 -15
  442. data/lib/generators/luca/application/templates/javascripts/router.coffee +0 -4
  443. data/lib/guard/luca.rb +0 -84
  444. data/lib/luca/asset_compiler.rb +0 -117
  445. data/lib/luca/cli/generate.rb +0 -37
  446. data/lib/luca/cli/server.rb +0 -20
  447. data/lib/luca/cli/sync.rb +0 -40
  448. data/lib/luca/cli/watch.rb +0 -16
  449. data/lib/luca/cli.rb +0 -68
  450. data/lib/luca/collection/endpoint.rb +0 -38
  451. data/lib/luca/collection/file_backend.rb +0 -121
  452. data/lib/luca/collection/redis_backend.rb +0 -153
  453. data/lib/luca/collection.rb +0 -64
  454. data/lib/luca/compiled_asset.rb +0 -61
  455. data/lib/luca/component_definition.rb +0 -356
  456. data/lib/luca/luca_application.rb +0 -258
  457. data/lib/luca/project.rb +0 -73
  458. data/lib/luca/project_harness.rb +0 -96
  459. data/lib/luca/server.rb +0 -7
  460. data/lib/luca/stylesheet.rb +0 -35
  461. data/lib/luca/template_asset.rb +0 -64
  462. data/lib/luca/version.rb +0 -3
  463. data/lib/luca/watcher.rb +0 -72
  464. data/lib/railties/luca/tasks.rake +0 -38
  465. data/site/.bundle/config +0 -2
  466. data/site/.gitignore +0 -5
  467. data/site/.rvmrc +0 -1
  468. data/site/CHANGELOG.md +0 -41
  469. data/site/DOCS.md +0 -41
  470. data/site/Gemfile +0 -8
  471. data/site/Gemfile.lock +0 -134
  472. data/site/LICENSE.md +0 -19
  473. data/site/config.rb +0 -84
  474. data/site/helpers/site_helpers.rb +0 -20
  475. data/site/html5bp-docs/README.md +0 -38
  476. data/site/html5bp-docs/contribute.md +0 -104
  477. data/site/html5bp-docs/crossdomain.md +0 -21
  478. data/site/html5bp-docs/css.md +0 -135
  479. data/site/html5bp-docs/extend.md +0 -507
  480. data/site/html5bp-docs/faq.md +0 -77
  481. data/site/html5bp-docs/htaccess.md +0 -323
  482. data/site/html5bp-docs/html.md +0 -170
  483. data/site/html5bp-docs/js.md +0 -31
  484. data/site/html5bp-docs/misc.md +0 -25
  485. data/site/html5bp-docs/usage.md +0 -109
  486. data/site/readme.md +0 -47
  487. data/site/source/.htaccess +0 -540
  488. data/site/source/404.html +0 -157
  489. data/site/source/app/assets/javascripts/dependencies.js.coffee +0 -6
  490. data/site/source/app/assets/javascripts/docs/application.coffee +0 -64
  491. data/site/source/app/assets/javascripts/docs/collections/docs_documentation.coffee +0 -17
  492. data/site/source/app/assets/javascripts/docs/collections/github_repositories.coffee +0 -7
  493. data/site/source/app/assets/javascripts/docs/collections/index.coffee +0 -1
  494. data/site/source/app/assets/javascripts/docs/collections/luca_documentation.coffee +0 -17
  495. data/site/source/app/assets/javascripts/docs/collections/public_gists.coffee +0 -4
  496. data/site/source/app/assets/javascripts/docs/config.coffee +0 -5
  497. data/site/source/app/assets/javascripts/docs/index.coffee +0 -12
  498. data/site/source/app/assets/javascripts/docs/lib/router.coffee +0 -3
  499. data/site/source/app/assets/javascripts/docs/models/component.coffee +0 -99
  500. data/site/source/app/assets/javascripts/docs/models/github_repository.coffee +0 -3
  501. data/site/source/app/assets/javascripts/docs/models/index.coffee +0 -1
  502. data/site/source/app/assets/javascripts/docs/templates/component_documentation.jst.ejs +0 -55
  503. data/site/source/app/assets/javascripts/docs/templates/examples_browser/overview.jst.ejs +0 -4
  504. data/site/source/app/assets/javascripts/docs/templates/examples_browser/selector.jst.ejs +0 -11
  505. data/site/source/app/assets/javascripts/docs/templates/github_repository.jst.ejs +0 -4
  506. data/site/source/app/assets/javascripts/docs/templates/layouts/main.jst.ejs +0 -4
  507. data/site/source/app/assets/javascripts/docs/templates/left_navigation.jst.ejs +0 -5
  508. data/site/source/app/assets/javascripts/docs/templates/pages/getting_started.jst.ejs +0 -78
  509. data/site/source/app/assets/javascripts/docs/templates/pages/home.jst.ejs +0 -57
  510. data/site/source/app/assets/javascripts/docs/views/components/code_editor.coffee +0 -45
  511. data/site/source/app/assets/javascripts/docs/views/components/component_documentation.coffee +0 -72
  512. data/site/source/app/assets/javascripts/docs/views/index.coffee +0 -3
  513. data/site/source/app/assets/javascripts/docs/views/pages/browse_source/details.coffee +0 -37
  514. data/site/source/app/assets/javascripts/docs/views/pages/browse_source/list.coffee +0 -31
  515. data/site/source/app/assets/javascripts/docs/views/pages/browse_source.coffee +0 -46
  516. data/site/source/app/assets/javascripts/docs/views/pages/component_editor.coffee +0 -10
  517. data/site/source/app/assets/javascripts/docs/views/pages/examples_browser/docs.coffee +0 -12
  518. data/site/source/app/assets/javascripts/docs/views/pages/examples_browser/source.coffee +0 -13
  519. data/site/source/app/assets/javascripts/docs/views/pages/examples_browser.coffee +0 -102
  520. data/site/source/app/assets/javascripts/docs/views/pages/home.coffee +0 -10
  521. data/site/source/app/assets/javascripts/docs/views/views/api_browser/index.coffee +0 -43
  522. data/site/source/app/assets/javascripts/docs/views/views/collection_view_examples/grid_layout_view_example.coffee +0 -14
  523. data/site/source/app/assets/javascripts/docs/views/views/collection_view_examples/table_view_example.coffee +0 -39
  524. data/site/source/app/assets/javascripts/docs/views/views/form_view_examples/basic_example.coffee +0 -38
  525. data/site/source/app/assets/javascripts/docs/views/views/form_view_examples/complex_layout.coffee +0 -110
  526. data/site/source/app/assets/javascripts/docs/views/views/top_navigation.coffee +0 -6
  527. data/site/source/app/assets/javascripts/docs-docs.js +0 -1
  528. data/site/source/app/assets/javascripts/luca-docs.js +0 -1
  529. data/site/source/app/assets/javascripts/luca-framework-documentation.js +0 -1
  530. data/site/source/app/assets/javascripts/site.js.coffee +0 -4
  531. data/site/source/app/assets/javascripts/vendor/codemirror.js +0 -4786
  532. data/site/source/app/assets/javascripts/vendor/coffeescript.js +0 -346
  533. data/site/source/app/assets/javascripts/vendor/css.js +0 -465
  534. data/site/source/app/assets/javascripts/vendor/htmlmixed.js +0 -84
  535. data/site/source/app/assets/javascripts/vendor/javascript.js +0 -422
  536. data/site/source/app/assets/javascripts/vendor/js-beautify.js +0 -1353
  537. data/site/source/app/assets/javascripts/vendor/modernizr-2.6.1.min.js +0 -4
  538. data/site/source/app/assets/javascripts/vendor/vim.js +0 -2511
  539. data/site/source/app/assets/stylesheets/docs/api-browser.css.scss +0 -5
  540. data/site/source/app/assets/stylesheets/docs/application.css.scss +0 -35
  541. data/site/source/app/assets/stylesheets/docs/browse-source.css.scss +0 -5
  542. data/site/source/app/assets/stylesheets/docs/scrollable-table.css.scss +0 -5
  543. data/site/source/app/assets/stylesheets/site.css.scss +0 -2
  544. data/site/source/app/assets/stylesheets/vendor/codemirror.css +0 -240
  545. data/site/source/app/assets/stylesheets/vendor/prettify-tomorrow-night-bright.css +0 -160
  546. data/site/source/app/assets/stylesheets/vendor/twilight.css +0 -26
  547. data/site/source/crossdomain.xml +0 -15
  548. data/site/source/documentation.html.haml +0 -1
  549. data/site/source/favicon_base.png +0 -0
  550. data/site/source/humans.txt +0 -15
  551. data/site/source/images/background.png +0 -0
  552. data/site/source/images/middleman.png +0 -0
  553. data/site/source/index.html.haml +0 -1
  554. data/site/source/layouts/layout.haml +0 -55
  555. data/site/source/readme.md +0 -63
  556. data/site/source/robots.txt +0 -3
  557. data/spec/javascripts/components/collection_view_spec.coffee +0 -59
  558. data/spec/javascripts/components/controller_spec.coffee +0 -62
  559. data/spec/javascripts/components/form_view_spec.coffee +0 -162
  560. data/spec/javascripts/components/multi_collection_view_spec.coffee +0 -5
  561. data/spec/javascripts/components/table_view_spec.coffee +0 -17
  562. data/spec/javascripts/concerns/collection_event_bindings_spec.coffee +0 -15
  563. data/spec/javascripts/concerns/dom_helpers_spec.coffee +0 -16
  564. data/spec/javascripts/concerns/filterable_spec.coffee +0 -25
  565. data/spec/javascripts/concerns/model_presenter_spec.coffee +0 -31
  566. data/spec/javascripts/concerns/state_model_spec.coffee +0 -55
  567. data/spec/javascripts/containers/card_view_spec.coffee +0 -108
  568. data/spec/javascripts/core/concerns_spec.coffee +0 -88
  569. data/spec/javascripts/core/container_spec.coffee +0 -287
  570. data/spec/javascripts/core/define_spec.coffee +0 -116
  571. data/spec/javascripts/core/events_spec.coffee +0 -26
  572. data/spec/javascripts/core/field_spec.coffee +0 -4
  573. data/spec/javascripts/core/util_spec.coffee +0 -24
  574. data/spec/javascripts/dependencies/index.coffee +0 -3
  575. data/spec/lib/component_definition_spec.rb +0 -63
  576. data/spec/lib/input_compiler_spec.rb +0 -9
  577. data/spec/lib/luca_application_spec.rb +0 -30
  578. data/spec/support/fixtures/application.coffee +0 -45
  579. data/spec/support/fixtures/component.coffee +0 -34
  580. data/tutorials/component-driven-design.md +0 -140
  581. data/tutorials/structure-of-a-project.md +0 -63
  582. data/vendor/assets/javascripts/backbone-ext.js +0 -21
  583. data/vendor/assets/javascripts/backbone-min.js +0 -42
  584. data/vendor/assets/javascripts/backbone-query.min.js +0 -1
  585. data/vendor/assets/javascripts/bootstrap.min.js +0 -7
  586. data/vendor/assets/javascripts/codemirror-ui.js +0 -503
  587. data/vendor/assets/javascripts/hogan.js +0 -707
  588. data/vendor/assets/javascripts/inflections.js +0 -656
  589. data/vendor/assets/javascripts/jasmine-html.js +0 -190
  590. data/vendor/assets/javascripts/jasmine.js +0 -2476
  591. data/vendor/assets/javascripts/jquery.js +0 -5
  592. data/vendor/assets/javascripts/keymaster.min.js +0 -4
  593. data/vendor/assets/javascripts/luca-dependencies.min.js +0 -8
  594. data/vendor/assets/javascripts/luca-development.min.js +0 -1
  595. data/vendor/assets/javascripts/luca-spec.js +0 -11
  596. data/vendor/assets/javascripts/luca.full.min.js +0 -12
  597. data/vendor/assets/javascripts/luca.min.js +0 -5
  598. data/vendor/assets/javascripts/sinon.js +0 -3469
  599. data/vendor/assets/javascripts/underscore-min.js +0 -1
  600. data/vendor/assets/javascripts/underscore-string.min.js +0 -1
  601. data/vendor/assets/stylesheets/bootstrap-responsive.min.css +0 -9
  602. data/vendor/assets/stylesheets/bootstrap.min.css +0 -9
  603. data/vendor/assets/stylesheets/luca-components.css +0 -202
  604. data/vendor/assets/stylesheets/luca-development.css +0 -23
@@ -1,4786 +0,0 @@
1
- // CodeMirror version 3.02
2
- //
3
- // CodeMirror is the only global var we claim
4
- window.CodeMirror = (function() {
5
- "use strict";
6
-
7
- // BROWSER SNIFFING
8
-
9
- // Crude, but necessary to handle a number of hard-to-feature-detect
10
- // bugs and behavior differences.
11
- var gecko = /gecko\/\d/i.test(navigator.userAgent);
12
- var ie = /MSIE \d/.test(navigator.userAgent);
13
- var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
14
- var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
15
- var webkit = /WebKit\//.test(navigator.userAgent);
16
- var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
17
- var chrome = /Chrome\//.test(navigator.userAgent);
18
- var opera = /Opera\//.test(navigator.userAgent);
19
- var safari = /Apple Computer/.test(navigator.vendor);
20
- var khtml = /KHTML\//.test(navigator.userAgent);
21
- var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
22
- var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
23
- var phantom = /PhantomJS/.test(navigator.userAgent);
24
-
25
- var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
26
- // This is woefully incomplete. Suggestions for alternative methods welcome.
27
- var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
28
- var mac = ios || /Mac/.test(navigator.platform);
29
- var windows = /windows/i.test(navigator.platform);
30
-
31
- var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
32
- if (opera_version) opera_version = Number(opera_version[1]);
33
- // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
34
- var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
35
-
36
- // Optimize some code when these features are not used
37
- var sawReadOnlySpans = false, sawCollapsedSpans = false;
38
-
39
- // CONSTRUCTOR
40
-
41
- function CodeMirror(place, options) {
42
- if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
43
-
44
- this.options = options = options || {};
45
- // Determine effective options based on given values and defaults.
46
- for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
47
- options[opt] = defaults[opt];
48
- setGuttersForLineNumbers(options);
49
-
50
- var display = this.display = makeDisplay(place);
51
- display.wrapper.CodeMirror = this;
52
- updateGutters(this);
53
- if (options.autofocus && !mobile) focusInput(this);
54
-
55
- this.view = makeView(new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]));
56
- this.nextOpId = 0;
57
- loadMode(this);
58
- themeChanged(this);
59
- if (options.lineWrapping)
60
- this.display.wrapper.className += " CodeMirror-wrap";
61
-
62
- // Initialize the content.
63
- this.setValue(options.value || "");
64
- // Override magic textarea content restore that IE sometimes does
65
- // on our hidden textarea on reload
66
- if (ie) setTimeout(bind(resetInput, this, true), 20);
67
- this.view.history = makeHistory();
68
-
69
- registerEventHandlers(this);
70
- // IE throws unspecified error in certain cases, when
71
- // trying to access activeElement before onload
72
- var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
73
- if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
74
- else onBlur(this);
75
-
76
- operation(this, function() {
77
- for (var opt in optionHandlers)
78
- if (optionHandlers.propertyIsEnumerable(opt))
79
- optionHandlers[opt](this, options[opt], Init);
80
- for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
81
- })();
82
- }
83
-
84
- // DISPLAY CONSTRUCTOR
85
-
86
- function makeDisplay(place) {
87
- var d = {};
88
- var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
89
- if (webkit) input.style.width = "1000px";
90
- else input.setAttribute("wrap", "off");
91
- input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
92
- // Wraps and hides input textarea
93
- d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
94
- // The actual fake scrollbars.
95
- d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
96
- d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
97
- d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
98
- // DIVs containing the selection and the actual code
99
- d.lineDiv = elt("div");
100
- d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
101
- // Blinky cursor, and element used to ensure cursor fits at the end of a line
102
- d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
103
- // Secondary cursor, shown when on a 'jump' in bi-directional text
104
- d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
105
- // Used to measure text size
106
- d.measure = elt("div", null, "CodeMirror-measure");
107
- // Wraps everything that needs to exist inside the vertically-padded coordinate system
108
- d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
109
- null, "position: relative; outline: none");
110
- // Moved around its parent to cover visible view
111
- d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
112
- // Set to the height of the text, causes scrolling
113
- d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
114
- // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
115
- d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px");
116
- // Will contain the gutters, if any
117
- d.gutters = elt("div", null, "CodeMirror-gutters");
118
- d.lineGutter = null;
119
- // Helper element to properly size the gutter backgrounds
120
- var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
121
- // Provides scrolling
122
- d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
123
- d.scroller.setAttribute("tabIndex", "-1");
124
- // The element in which the editor lives.
125
- d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
126
- d.scrollbarFiller, d.scroller], "CodeMirror");
127
- // Work around IE7 z-index bug
128
- if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
129
- if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
130
-
131
- // Needed to hide big blue blinking cursor on Mobile Safari
132
- if (ios) input.style.width = "0px";
133
- if (!webkit) d.scroller.draggable = true;
134
- // Needed to handle Tab key in KHTML
135
- if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
136
- // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
137
- else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
138
-
139
- // Current visible range (may be bigger than the view window).
140
- d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0;
141
-
142
- // Used to only resize the line number gutter when necessary (when
143
- // the amount of lines crosses a boundary that makes its width change)
144
- d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
145
- // See readInput and resetInput
146
- d.prevInput = "";
147
- // Set to true when a non-horizontal-scrolling widget is added. As
148
- // an optimization, widget aligning is skipped when d is false.
149
- d.alignWidgets = false;
150
- // Flag that indicates whether we currently expect input to appear
151
- // (after some event like 'keypress' or 'input') and are polling
152
- // intensively.
153
- d.pollingFast = false;
154
- // Self-resetting timeout for the poller
155
- d.poll = new Delayed();
156
- // True when a drag from the editor is active
157
- d.draggingText = false;
158
-
159
- d.cachedCharWidth = d.cachedTextHeight = null;
160
- d.measureLineCache = [];
161
- d.measureLineCachePos = 0;
162
-
163
- // Tracks when resetInput has punted to just putting a short
164
- // string instead of the (large) selection.
165
- d.inaccurateSelection = false;
166
-
167
- // Used to adjust overwrite behaviour when a paste has been
168
- // detected
169
- d.pasteIncoming = false;
170
-
171
- // Used for measuring wheel scrolling granularity
172
- d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
173
-
174
- return d;
175
- }
176
-
177
- // VIEW CONSTRUCTOR
178
-
179
- function makeView(doc) {
180
- var selPos = {line: 0, ch: 0};
181
- return {
182
- doc: doc,
183
- // frontier is the point up to which the content has been parsed,
184
- frontier: 0, highlight: new Delayed(),
185
- sel: {from: selPos, to: selPos, head: selPos, anchor: selPos, shift: false, extend: false},
186
- scrollTop: 0, scrollLeft: 0,
187
- overwrite: false, focused: false,
188
- // Tracks the maximum line length so that
189
- // the horizontal scrollbar can be kept
190
- // static when scrolling.
191
- maxLine: getLine(doc, 0),
192
- maxLineLength: 0,
193
- maxLineChanged: false,
194
- suppressEdits: false,
195
- goalColumn: null,
196
- cantEdit: false,
197
- keyMaps: [],
198
- overlays: [],
199
- modeGen: 0
200
- };
201
- }
202
-
203
- // STATE UPDATES
204
-
205
- // Used to get the editor into a consistent state again when options change.
206
-
207
- function loadMode(cm) {
208
- var doc = cm.view.doc;
209
- cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode);
210
- doc.iter(0, doc.size, function(line) {
211
- if (line.stateAfter) line.stateAfter = null;
212
- if (line.styles) line.styles = null;
213
- });
214
- cm.view.frontier = 0;
215
- startWorker(cm, 100);
216
- cm.view.modeGen++;
217
- if (cm.curOp) regChange(cm, 0, doc.size);
218
- }
219
-
220
- function wrappingChanged(cm) {
221
- var doc = cm.view.doc, th = textHeight(cm.display);
222
- if (cm.options.lineWrapping) {
223
- cm.display.wrapper.className += " CodeMirror-wrap";
224
- var perLine = cm.display.scroller.clientWidth / charWidth(cm.display) - 3;
225
- doc.iter(0, doc.size, function(line) {
226
- if (line.height == 0) return;
227
- var guess = Math.ceil(line.text.length / perLine) || 1;
228
- if (guess != 1) updateLineHeight(line, guess * th);
229
- });
230
- cm.display.sizer.style.minWidth = "";
231
- } else {
232
- cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
233
- computeMaxLength(cm.view);
234
- doc.iter(0, doc.size, function(line) {
235
- if (line.height != 0) updateLineHeight(line, th);
236
- });
237
- }
238
- regChange(cm, 0, doc.size);
239
- clearCaches(cm);
240
- setTimeout(function(){updateScrollbars(cm.display, cm.view.doc.height);}, 100);
241
- }
242
-
243
- function keyMapChanged(cm) {
244
- var style = keyMap[cm.options.keyMap].style;
245
- cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
246
- (style ? " cm-keymap-" + style : "");
247
- }
248
-
249
- function themeChanged(cm) {
250
- cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
251
- cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
252
- clearCaches(cm);
253
- }
254
-
255
- function guttersChanged(cm) {
256
- updateGutters(cm);
257
- updateDisplay(cm, true);
258
- }
259
-
260
- function updateGutters(cm) {
261
- var gutters = cm.display.gutters, specs = cm.options.gutters;
262
- removeChildren(gutters);
263
- for (var i = 0; i < specs.length; ++i) {
264
- var gutterClass = specs[i];
265
- var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
266
- if (gutterClass == "CodeMirror-linenumbers") {
267
- cm.display.lineGutter = gElt;
268
- gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
269
- }
270
- }
271
- gutters.style.display = i ? "" : "none";
272
- }
273
-
274
- function lineLength(doc, line) {
275
- if (line.height == 0) return 0;
276
- var len = line.text.length, merged, cur = line;
277
- while (merged = collapsedSpanAtStart(cur)) {
278
- var found = merged.find();
279
- cur = getLine(doc, found.from.line);
280
- len += found.from.ch - found.to.ch;
281
- }
282
- cur = line;
283
- while (merged = collapsedSpanAtEnd(cur)) {
284
- var found = merged.find();
285
- len -= cur.text.length - found.from.ch;
286
- cur = getLine(doc, found.to.line);
287
- len += cur.text.length - found.to.ch;
288
- }
289
- return len;
290
- }
291
-
292
- function computeMaxLength(view) {
293
- view.maxLine = getLine(view.doc, 0);
294
- view.maxLineLength = lineLength(view.doc, view.maxLine);
295
- view.maxLineChanged = true;
296
- view.doc.iter(1, view.doc.size, function(line) {
297
- var len = lineLength(view.doc, line);
298
- if (len > view.maxLineLength) {
299
- view.maxLineLength = len;
300
- view.maxLine = line;
301
- }
302
- });
303
- }
304
-
305
- // Make sure the gutters options contains the element
306
- // "CodeMirror-linenumbers" when the lineNumbers option is true.
307
- function setGuttersForLineNumbers(options) {
308
- var found = false;
309
- for (var i = 0; i < options.gutters.length; ++i) {
310
- if (options.gutters[i] == "CodeMirror-linenumbers") {
311
- if (options.lineNumbers) found = true;
312
- else options.gutters.splice(i--, 1);
313
- }
314
- }
315
- if (!found && options.lineNumbers)
316
- options.gutters.push("CodeMirror-linenumbers");
317
- }
318
-
319
- // SCROLLBARS
320
-
321
- // Re-synchronize the fake scrollbars with the actual size of the
322
- // content. Optionally force a scrollTop.
323
- function updateScrollbars(d /* display */, docHeight) {
324
- var totalHeight = docHeight + 2 * paddingTop(d);
325
- d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
326
- var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
327
- var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
328
- var needsV = scrollHeight > d.scroller.clientHeight;
329
- if (needsV) {
330
- d.scrollbarV.style.display = "block";
331
- d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
332
- d.scrollbarV.firstChild.style.height =
333
- (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
334
- } else d.scrollbarV.style.display = "";
335
- if (needsH) {
336
- d.scrollbarH.style.display = "block";
337
- d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
338
- d.scrollbarH.firstChild.style.width =
339
- (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
340
- } else d.scrollbarH.style.display = "";
341
- if (needsH && needsV) {
342
- d.scrollbarFiller.style.display = "block";
343
- d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
344
- } else d.scrollbarFiller.style.display = "";
345
-
346
- if (mac_geLion && scrollbarWidth(d.measure) === 0)
347
- d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
348
- }
349
-
350
- function visibleLines(display, doc, viewPort) {
351
- var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
352
- if (typeof viewPort == "number") top = viewPort;
353
- else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
354
- top = Math.floor(top - paddingTop(display));
355
- var bottom = Math.ceil(top + height);
356
- return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
357
- }
358
-
359
- // LINE NUMBERS
360
-
361
- function alignHorizontally(cm) {
362
- var display = cm.display;
363
- if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
364
- var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.view.scrollLeft;
365
- var gutterW = display.gutters.offsetWidth, l = comp + "px";
366
- for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
367
- for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
368
- }
369
- if (cm.options.fixedGutter)
370
- display.gutters.style.left = (comp + gutterW) + "px";
371
- }
372
-
373
- function maybeUpdateLineNumberWidth(cm) {
374
- if (!cm.options.lineNumbers) return false;
375
- var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display;
376
- if (last.length != display.lineNumChars) {
377
- var test = display.measure.appendChild(elt("div", [elt("div", last)],
378
- "CodeMirror-linenumber CodeMirror-gutter-elt"));
379
- var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
380
- display.lineGutter.style.width = "";
381
- display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
382
- display.lineNumWidth = display.lineNumInnerWidth + padding;
383
- display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
384
- display.lineGutter.style.width = display.lineNumWidth + "px";
385
- return true;
386
- }
387
- return false;
388
- }
389
-
390
- function lineNumberFor(options, i) {
391
- return String(options.lineNumberFormatter(i + options.firstLineNumber));
392
- }
393
- function compensateForHScroll(display) {
394
- return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
395
- }
396
-
397
- // DISPLAY DRAWING
398
-
399
- function updateDisplay(cm, changes, viewPort) {
400
- var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
401
- var updated = updateDisplayInner(cm, changes, viewPort);
402
- if (updated) {
403
- signalLater(cm, cm, "update", cm);
404
- if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
405
- signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
406
- }
407
- updateSelection(cm);
408
- updateScrollbars(cm.display, cm.view.doc.height);
409
-
410
- return updated;
411
- }
412
-
413
- // Uses a set of changes plus the current scroll position to
414
- // determine which DOM updates have to be made, and makes the
415
- // updates.
416
- function updateDisplayInner(cm, changes, viewPort) {
417
- var display = cm.display, doc = cm.view.doc;
418
- if (!display.wrapper.clientWidth) {
419
- display.showingFrom = display.showingTo = display.viewOffset = 0;
420
- return;
421
- }
422
-
423
- // Compute the new visible window
424
- // If scrollTop is specified, use that to determine which lines
425
- // to render instead of the current scrollbar position.
426
- var visible = visibleLines(display, doc, viewPort);
427
- // Bail out if the visible area is already rendered and nothing changed.
428
- if (changes !== true && changes.length == 0 &&
429
- visible.from > display.showingFrom && visible.to < display.showingTo)
430
- return;
431
-
432
- if (changes && maybeUpdateLineNumberWidth(cm))
433
- changes = true;
434
- var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
435
- display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
436
-
437
- // When merged lines are present, the line that needs to be
438
- // redrawn might not be the one that was changed.
439
- if (changes !== true && sawCollapsedSpans)
440
- for (var i = 0; i < changes.length; ++i) {
441
- var ch = changes[i], merged;
442
- while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) {
443
- var from = merged.find().from.line;
444
- if (ch.diff) ch.diff -= ch.from - from;
445
- ch.from = from;
446
- }
447
- }
448
-
449
- // Used to determine which lines need their line numbers updated
450
- var positionsChangedFrom = changes === true ? 0 : Infinity;
451
- if (cm.options.lineNumbers && changes && changes !== true)
452
- for (var i = 0; i < changes.length; ++i)
453
- if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
454
-
455
- var from = Math.max(visible.from - cm.options.viewportMargin, 0);
456
- var to = Math.min(doc.size, visible.to + cm.options.viewportMargin);
457
- if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom;
458
- if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo);
459
- if (sawCollapsedSpans) {
460
- from = lineNo(visualLine(doc, getLine(doc, from)));
461
- while (to < doc.size && lineIsHidden(getLine(doc, to))) ++to;
462
- }
463
-
464
- // Create a range of theoretically intact lines, and punch holes
465
- // in that using the change info.
466
- var intact = changes === true ? [] :
467
- computeIntact([{from: display.showingFrom, to: display.showingTo}], changes);
468
- // Clip off the parts that won't be visible
469
- var intactLines = 0;
470
- for (var i = 0; i < intact.length; ++i) {
471
- var range = intact[i];
472
- if (range.from < from) range.from = from;
473
- if (range.to > to) range.to = to;
474
- if (range.from >= range.to) intact.splice(i--, 1);
475
- else intactLines += range.to - range.from;
476
- }
477
- if (intactLines == to - from && from == display.showingFrom && to == display.showingTo)
478
- return;
479
- intact.sort(function(a, b) {return a.from - b.from;});
480
-
481
- var focused = document.activeElement;
482
- if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
483
- patchDisplay(cm, from, to, intact, positionsChangedFrom);
484
- display.lineDiv.style.display = "";
485
- if (document.activeElement != focused && focused.offsetHeight) focused.focus();
486
-
487
- var different = from != display.showingFrom || to != display.showingTo ||
488
- display.lastSizeC != display.wrapper.clientHeight;
489
- // This is just a bogus formula that detects when the editor is
490
- // resized or the font size changes.
491
- if (different) display.lastSizeC = display.wrapper.clientHeight;
492
- display.showingFrom = from; display.showingTo = to;
493
- startWorker(cm, 100);
494
-
495
- var prevBottom = display.lineDiv.offsetTop;
496
- for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
497
- if (ie_lt8) {
498
- var bot = node.offsetTop + node.offsetHeight;
499
- height = bot - prevBottom;
500
- prevBottom = bot;
501
- } else {
502
- var box = node.getBoundingClientRect();
503
- height = box.bottom - box.top;
504
- }
505
- var diff = node.lineObj.height - height;
506
- if (height < 2) height = textHeight(display);
507
- if (diff > .001 || diff < -.001) {
508
- updateLineHeight(node.lineObj, height);
509
- var widgets = node.lineObj.widgets;
510
- if (widgets) for (var i = 0; i < widgets.length; ++i)
511
- widgets[i].height = widgets[i].node.offsetHeight;
512
- }
513
- }
514
- display.viewOffset = heightAtLine(cm, getLine(doc, from));
515
- // Position the mover div to align with the current virtual scroll position
516
- display.mover.style.top = display.viewOffset + "px";
517
-
518
- if (visibleLines(display, doc, viewPort).to >= to)
519
- updateDisplayInner(cm, [], viewPort);
520
- return true;
521
- }
522
-
523
- function computeIntact(intact, changes) {
524
- for (var i = 0, l = changes.length || 0; i < l; ++i) {
525
- var change = changes[i], intact2 = [], diff = change.diff || 0;
526
- for (var j = 0, l2 = intact.length; j < l2; ++j) {
527
- var range = intact[j];
528
- if (change.to <= range.from && change.diff) {
529
- intact2.push({from: range.from + diff, to: range.to + diff});
530
- } else if (change.to <= range.from || change.from >= range.to) {
531
- intact2.push(range);
532
- } else {
533
- if (change.from > range.from)
534
- intact2.push({from: range.from, to: change.from});
535
- if (change.to < range.to)
536
- intact2.push({from: change.to + diff, to: range.to + diff});
537
- }
538
- }
539
- intact = intact2;
540
- }
541
- return intact;
542
- }
543
-
544
- function getDimensions(cm) {
545
- var d = cm.display, left = {}, width = {};
546
- for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
547
- left[cm.options.gutters[i]] = n.offsetLeft;
548
- width[cm.options.gutters[i]] = n.offsetWidth;
549
- }
550
- return {fixedPos: compensateForHScroll(d),
551
- gutterTotalWidth: d.gutters.offsetWidth,
552
- gutterLeft: left,
553
- gutterWidth: width,
554
- wrapperWidth: d.wrapper.clientWidth};
555
- }
556
-
557
- function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
558
- var dims = getDimensions(cm);
559
- var display = cm.display, lineNumbers = cm.options.lineNumbers;
560
- if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
561
- removeChildren(display.lineDiv);
562
- var container = display.lineDiv, cur = container.firstChild;
563
-
564
- function rm(node) {
565
- var next = node.nextSibling;
566
- if (webkit && mac && cm.display.currentWheelTarget == node) {
567
- node.style.display = "none";
568
- node.lineObj = null;
569
- } else {
570
- node.parentNode.removeChild(node);
571
- }
572
- return next;
573
- }
574
-
575
- var nextIntact = intact.shift(), lineNo = from;
576
- cm.view.doc.iter(from, to, function(line) {
577
- if (nextIntact && nextIntact.to == lineNo) nextIntact = intact.shift();
578
- if (lineIsHidden(line)) {
579
- if (line.height != 0) updateLineHeight(line, 0);
580
- if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
581
- if (line.widgets[i].showIfHidden) {
582
- var prev = cur.previousSibling;
583
- if (prev.nodeType == "pre") {
584
- var wrap = elt("div", null, null, "position: relative");
585
- prev.parentNode.replaceChild(wrap, prev);
586
- wrap.appendChild(prev);
587
- prev = wrap;
588
- }
589
- prev.appendChild(buildLineWidget(line.widgets[i], prev, dims));
590
- }
591
- } else if (nextIntact && nextIntact.from <= lineNo && nextIntact.to > lineNo) {
592
- // This line is intact. Skip to the actual node. Update its
593
- // line number if needed.
594
- while (cur.lineObj != line) cur = rm(cur);
595
- if (lineNumbers && updateNumbersFrom <= lineNo && cur.lineNumber)
596
- setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineNo));
597
- cur = cur.nextSibling;
598
- } else {
599
- // This line needs to be generated.
600
- var lineNode = buildLineElement(cm, line, lineNo, dims);
601
- container.insertBefore(lineNode, cur);
602
- lineNode.lineObj = line;
603
- }
604
- ++lineNo;
605
- });
606
- while (cur) cur = rm(cur);
607
- }
608
-
609
- function buildLineElement(cm, line, lineNo, dims) {
610
- var lineElement = lineContent(cm, line);
611
- var markers = line.gutterMarkers, display = cm.display;
612
-
613
- if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass &&
614
- (!line.widgets || !line.widgets.length)) return lineElement;
615
-
616
- // Lines with gutter elements or a background class need
617
- // to be wrapped again, and have the extra elements added
618
- // to the wrapper div
619
-
620
- var wrap = elt("div", null, line.wrapClass, "position: relative");
621
- if (cm.options.lineNumbers || markers) {
622
- var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " +
623
- (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
624
- if (cm.options.fixedGutter) wrap.alignable = [gutterWrap];
625
- if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
626
- wrap.lineNumber = gutterWrap.appendChild(
627
- elt("div", lineNumberFor(cm.options, lineNo),
628
- "CodeMirror-linenumber CodeMirror-gutter-elt",
629
- "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
630
- + display.lineNumInnerWidth + "px"));
631
- if (markers)
632
- for (var k = 0; k < cm.options.gutters.length; ++k) {
633
- var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
634
- if (found)
635
- gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
636
- dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
637
- }
638
- }
639
- // Kludge to make sure the styled element lies behind the selection (by z-index)
640
- if (line.bgClass)
641
- wrap.appendChild(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground"));
642
- wrap.appendChild(lineElement);
643
- if (line.widgets) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
644
- var widget = ws[i], node = buildLineWidget(widget, wrap, dims);
645
- if (widget.above)
646
- wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
647
- else
648
- wrap.appendChild(node);
649
- }
650
- if (ie_lt8) wrap.style.zIndex = 2;
651
- return wrap;
652
- }
653
-
654
- function buildLineWidget(widget, wrap, dims) {
655
- var node = elt("div", [widget.node], "CodeMirror-linewidget");
656
- node.widget = widget;
657
- if (widget.noHScroll) {
658
- (wrap.alignable || (wrap.alignable = [])).push(node);
659
- var width = dims.wrapperWidth;
660
- node.style.left = dims.fixedPos + "px";
661
- if (!widget.coverGutter) {
662
- width -= dims.gutterTotalWidth;
663
- node.style.paddingLeft = dims.gutterTotalWidth + "px";
664
- }
665
- node.style.width = width + "px";
666
- }
667
- if (widget.coverGutter) {
668
- node.style.zIndex = 5;
669
- node.style.position = "relative";
670
- if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
671
- }
672
- return node;
673
- }
674
-
675
- // SELECTION / CURSOR
676
-
677
- function updateSelection(cm) {
678
- var display = cm.display;
679
- var collapsed = posEq(cm.view.sel.from, cm.view.sel.to);
680
- if (collapsed || cm.options.showCursorWhenSelecting)
681
- updateSelectionCursor(cm);
682
- else
683
- display.cursor.style.display = display.otherCursor.style.display = "none";
684
- if (!collapsed)
685
- updateSelectionRange(cm);
686
- else
687
- display.selectionDiv.style.display = "none";
688
-
689
- // Move the hidden textarea near the cursor to prevent scrolling artifacts
690
- var headPos = cursorCoords(cm, cm.view.sel.head, "div");
691
- var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
692
- display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
693
- headPos.top + lineOff.top - wrapOff.top)) + "px";
694
- display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
695
- headPos.left + lineOff.left - wrapOff.left)) + "px";
696
- }
697
-
698
- // No selection, plain cursor
699
- function updateSelectionCursor(cm) {
700
- var display = cm.display, pos = cursorCoords(cm, cm.view.sel.head, "div");
701
- display.cursor.style.left = pos.left + "px";
702
- display.cursor.style.top = pos.top + "px";
703
- display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
704
- display.cursor.style.display = "";
705
-
706
- if (pos.other) {
707
- display.otherCursor.style.display = "";
708
- display.otherCursor.style.left = pos.other.left + "px";
709
- display.otherCursor.style.top = pos.other.top + "px";
710
- display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
711
- } else { display.otherCursor.style.display = "none"; }
712
- }
713
-
714
- // Highlight selection
715
- function updateSelectionRange(cm) {
716
- var display = cm.display, doc = cm.view.doc, sel = cm.view.sel;
717
- var fragment = document.createDocumentFragment();
718
- var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
719
-
720
- function add(left, top, width, bottom) {
721
- if (top < 0) top = 0;
722
- fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
723
- "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
724
- "px; height: " + (bottom - top) + "px"));
725
- }
726
-
727
- function drawForLine(line, fromArg, toArg, retTop) {
728
- var lineObj = getLine(doc, line);
729
- var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
730
- function coords(ch) {
731
- return charCoords(cm, {line: line, ch: ch}, "div", lineObj);
732
- }
733
-
734
- iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
735
- var leftPos = coords(dir == "rtl" ? to - 1 : from);
736
- var rightPos = coords(dir == "rtl" ? from : to - 1);
737
- var left = leftPos.left, right = rightPos.right;
738
- if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
739
- add(left, leftPos.top, null, leftPos.bottom);
740
- left = pl;
741
- if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
742
- }
743
- if (toArg == null && to == lineLen) right = clientWidth;
744
- if (fromArg == null && from == 0) left = pl;
745
- rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
746
- if (left < pl + 1) left = pl;
747
- add(left, rightPos.top, right - left, rightPos.bottom);
748
- });
749
- return rVal;
750
- }
751
-
752
- if (sel.from.line == sel.to.line) {
753
- drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
754
- } else {
755
- var fromObj = getLine(doc, sel.from.line);
756
- var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
757
- while (merged = collapsedSpanAtEnd(cur)) {
758
- var found = merged.find();
759
- path.push(found.from.ch, found.to.line, found.to.ch);
760
- if (found.to.line == sel.to.line) {
761
- path.push(sel.to.ch);
762
- singleLine = true;
763
- break;
764
- }
765
- cur = getLine(doc, found.to.line);
766
- }
767
-
768
- // This is a single, merged line
769
- if (singleLine) {
770
- for (var i = 0; i < path.length; i += 3)
771
- drawForLine(path[i], path[i+1], path[i+2]);
772
- } else {
773
- var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
774
- if (sel.from.ch)
775
- // Draw the first line of selection.
776
- middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
777
- else
778
- // Simply include it in the middle block.
779
- middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
780
-
781
- if (!sel.to.ch)
782
- middleBot = heightAtLine(cm, toObj) - display.viewOffset;
783
- else
784
- middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
785
-
786
- if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
787
- }
788
- }
789
-
790
- removeChildrenAndAdd(display.selectionDiv, fragment);
791
- display.selectionDiv.style.display = "";
792
- }
793
-
794
- // Cursor-blinking
795
- function restartBlink(cm) {
796
- var display = cm.display;
797
- clearInterval(display.blinker);
798
- var on = true;
799
- display.cursor.style.visibility = display.otherCursor.style.visibility = "";
800
- display.blinker = setInterval(function() {
801
- if (!display.cursor.offsetHeight) return;
802
- display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
803
- }, cm.options.cursorBlinkRate);
804
- }
805
-
806
- // HIGHLIGHT WORKER
807
-
808
- function startWorker(cm, time) {
809
- if (cm.view.mode.startState && cm.view.frontier < cm.display.showingTo)
810
- cm.view.highlight.set(time, bind(highlightWorker, cm));
811
- }
812
-
813
- function highlightWorker(cm) {
814
- var view = cm.view, doc = view.doc;
815
- if (view.frontier >= cm.display.showingTo) return;
816
- var end = +new Date + cm.options.workTime;
817
- var state = copyState(view.mode, getStateBefore(cm, view.frontier));
818
- var changed = [], prevChange;
819
- doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) {
820
- if (view.frontier >= cm.display.showingFrom) { // Visible
821
- var oldStyles = line.styles;
822
- line.styles = highlightLine(cm, line, state);
823
- var ischange = !oldStyles || oldStyles.length != line.styles.length;
824
- for (var i = 0; !ischange && i < oldStyles.length; ++i)
825
- ischange = oldStyles[i] != line.styles[i];
826
- if (ischange) {
827
- if (prevChange && prevChange.end == view.frontier) prevChange.end++;
828
- else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1});
829
- }
830
- line.stateAfter = copyState(view.mode, state);
831
- } else {
832
- processLine(cm, line, state);
833
- line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null;
834
- }
835
- ++view.frontier;
836
- if (+new Date > end) {
837
- startWorker(cm, cm.options.workDelay);
838
- return true;
839
- }
840
- });
841
- if (changed.length)
842
- operation(cm, function() {
843
- for (var i = 0; i < changed.length; ++i)
844
- regChange(this, changed[i].start, changed[i].end);
845
- })();
846
- }
847
-
848
- // Finds the line to start with when starting a parse. Tries to
849
- // find a line with a stateAfter, so that it can start with a
850
- // valid state. If that fails, it returns the line with the
851
- // smallest indentation, which tends to need the least context to
852
- // parse correctly.
853
- function findStartLine(cm, n) {
854
- var minindent, minline, doc = cm.view.doc;
855
- for (var search = n, lim = n - 100; search > lim; --search) {
856
- if (search == 0) return 0;
857
- var line = getLine(doc, search-1);
858
- if (line.stateAfter) return search;
859
- var indented = countColumn(line.text, null, cm.options.tabSize);
860
- if (minline == null || minindent > indented) {
861
- minline = search - 1;
862
- minindent = indented;
863
- }
864
- }
865
- return minline;
866
- }
867
-
868
- function getStateBefore(cm, n) {
869
- var view = cm.view;
870
- if (!view.mode.startState) return true;
871
- var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter;
872
- if (!state) state = startState(view.mode);
873
- else state = copyState(view.mode, state);
874
- view.doc.iter(pos, n, function(line) {
875
- processLine(cm, line, state);
876
- var save = pos == n - 1 || pos % 5 == 0 || pos >= view.showingFrom && pos < view.showingTo;
877
- line.stateAfter = save ? copyState(view.mode, state) : null;
878
- ++pos;
879
- });
880
- return state;
881
- }
882
-
883
- // POSITION MEASUREMENT
884
-
885
- function paddingTop(display) {return display.lineSpace.offsetTop;}
886
- function paddingLeft(display) {
887
- var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x"));
888
- return e.offsetLeft;
889
- }
890
-
891
- function measureChar(cm, line, ch, data) {
892
- var dir = -1;
893
- data = data || measureLine(cm, line);
894
-
895
- for (var pos = ch;; pos += dir) {
896
- var r = data[pos];
897
- if (r) break;
898
- if (dir < 0 && pos == 0) dir = 1;
899
- }
900
- return {left: pos < ch ? r.right : r.left,
901
- right: pos > ch ? r.left : r.right,
902
- top: r.top, bottom: r.bottom};
903
- }
904
-
905
- function measureLine(cm, line) {
906
- // First look in the cache
907
- var display = cm.display, cache = cm.display.measureLineCache;
908
- for (var i = 0; i < cache.length; ++i) {
909
- var memo = cache[i];
910
- if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
911
- display.scroller.clientWidth == memo.width)
912
- return memo.measure;
913
- }
914
-
915
- var measure = measureLineInner(cm, line);
916
- // Store result in the cache
917
- var memo = {text: line.text, width: display.scroller.clientWidth,
918
- markedSpans: line.markedSpans, measure: measure};
919
- if (cache.length == 16) cache[++display.measureLineCachePos % 16] = memo;
920
- else cache.push(memo);
921
- return measure;
922
- }
923
-
924
- function measureLineInner(cm, line) {
925
- var display = cm.display, measure = emptyArray(line.text.length);
926
- var pre = lineContent(cm, line, measure);
927
-
928
- // IE does not cache element positions of inline elements between
929
- // calls to getBoundingClientRect. This makes the loop below,
930
- // which gathers the positions of all the characters on the line,
931
- // do an amount of layout work quadratic to the number of
932
- // characters. When line wrapping is off, we try to improve things
933
- // by first subdividing the line into a bunch of inline blocks, so
934
- // that IE can reuse most of the layout information from caches
935
- // for those blocks. This does interfere with line wrapping, so it
936
- // doesn't work when wrapping is on, but in that case the
937
- // situation is slightly better, since IE does cache line-wrapping
938
- // information and only recomputes per-line.
939
- if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
940
- var fragment = document.createDocumentFragment();
941
- var chunk = 10, n = pre.childNodes.length;
942
- for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
943
- var wrap = elt("div", null, null, "display: inline-block");
944
- for (var j = 0; j < chunk && n; ++j) {
945
- wrap.appendChild(pre.firstChild);
946
- --n;
947
- }
948
- fragment.appendChild(wrap);
949
- }
950
- pre.appendChild(fragment);
951
- }
952
-
953
- removeChildrenAndAdd(display.measure, pre);
954
-
955
- var outer = display.lineDiv.getBoundingClientRect();
956
- var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
957
- for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
958
- var size = cur.getBoundingClientRect();
959
- var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
960
- for (var j = 0; j < vranges.length; j += 2) {
961
- var rtop = vranges[j], rbot = vranges[j+1];
962
- if (rtop > bot || rbot < top) continue;
963
- if (rtop <= top && rbot >= bot ||
964
- top <= rtop && bot >= rbot ||
965
- Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
966
- vranges[j] = Math.min(top, rtop);
967
- vranges[j+1] = Math.max(bot, rbot);
968
- break;
969
- }
970
- }
971
- if (j == vranges.length) vranges.push(top, bot);
972
- data[i] = {left: size.left - outer.left, right: size.right - outer.left, top: j};
973
- }
974
- for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
975
- var vr = cur.top;
976
- cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
977
- }
978
- return data;
979
- }
980
-
981
- function clearCaches(cm) {
982
- cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
983
- cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
984
- cm.view.maxLineChanged = true;
985
- }
986
-
987
- // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
988
- function intoCoordSystem(cm, lineObj, rect, context) {
989
- if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
990
- var size = widgetHeight(lineObj.widgets[i]);
991
- rect.top += size; rect.bottom += size;
992
- }
993
- if (context == "line") return rect;
994
- if (!context) context = "local";
995
- var yOff = heightAtLine(cm, lineObj);
996
- if (context != "local") yOff -= cm.display.viewOffset;
997
- if (context == "page") {
998
- var lOff = cm.display.lineSpace.getBoundingClientRect();
999
- yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
1000
- var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
1001
- rect.left += xOff; rect.right += xOff;
1002
- }
1003
- rect.top += yOff; rect.bottom += yOff;
1004
- return rect;
1005
- }
1006
-
1007
- function charCoords(cm, pos, context, lineObj) {
1008
- if (!lineObj) lineObj = getLine(cm.view.doc, pos.line);
1009
- return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
1010
- }
1011
-
1012
- function cursorCoords(cm, pos, context, lineObj, measurement) {
1013
- lineObj = lineObj || getLine(cm.view.doc, pos.line);
1014
- if (!measurement) measurement = measureLine(cm, lineObj);
1015
- function get(ch, right) {
1016
- var m = measureChar(cm, lineObj, ch, measurement);
1017
- if (right) m.left = m.right; else m.right = m.left;
1018
- return intoCoordSystem(cm, lineObj, m, context);
1019
- }
1020
- var order = getOrder(lineObj), ch = pos.ch;
1021
- if (!order) return get(ch);
1022
- var main, other, linedir = order[0].level;
1023
- for (var i = 0; i < order.length; ++i) {
1024
- var part = order[i], rtl = part.level % 2, nb, here;
1025
- if (part.from < ch && part.to > ch) return get(ch, rtl);
1026
- var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
1027
- if (left == ch) {
1028
- // Opera and IE return bogus offsets and widths for edges
1029
- // where the direction flips, but only for the side with the
1030
- // lower level. So we try to use the side with the higher
1031
- // level.
1032
- if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
1033
- else here = get(rtl && part.from != part.to ? ch - 1 : ch);
1034
- if (rtl == linedir) main = here; else other = here;
1035
- } else if (right == ch) {
1036
- var nb = i < order.length - 1 && order[i+1];
1037
- if (!rtl && nb && nb.from == nb.to) continue;
1038
- if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
1039
- else here = get(rtl ? ch : ch - 1, true);
1040
- if (rtl == linedir) main = here; else other = here;
1041
- }
1042
- }
1043
- if (linedir && !ch) other = get(order[0].to - 1);
1044
- if (!main) return other;
1045
- if (other) main.other = other;
1046
- return main;
1047
- }
1048
-
1049
- // Coords must be lineSpace-local
1050
- function coordsChar(cm, x, y) {
1051
- var doc = cm.view.doc;
1052
- y += cm.display.viewOffset;
1053
- if (y < 0) return {line: 0, ch: 0, outside: true};
1054
- var lineNo = lineAtHeight(doc, y);
1055
- if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length};
1056
- if (x < 0) x = 0;
1057
-
1058
- for (;;) {
1059
- var lineObj = getLine(doc, lineNo);
1060
- var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1061
- var merged = collapsedSpanAtEnd(lineObj);
1062
- var mergedPos = merged && merged.find();
1063
- if (merged && found.ch >= mergedPos.from.ch)
1064
- lineNo = mergedPos.to.line;
1065
- else
1066
- return found;
1067
- }
1068
- }
1069
-
1070
- function coordsCharInner(cm, lineObj, lineNo, x, y) {
1071
- var innerOff = y - heightAtLine(cm, lineObj);
1072
- var wrongLine = false, cWidth = cm.display.wrapper.clientWidth;
1073
- var measurement = measureLine(cm, lineObj);
1074
-
1075
- function getX(ch) {
1076
- var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line",
1077
- lineObj, measurement);
1078
- wrongLine = true;
1079
- if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth);
1080
- else if (innerOff < sp.top) return sp.left + cWidth;
1081
- else wrongLine = false;
1082
- return sp.left;
1083
- }
1084
-
1085
- var bidi = getOrder(lineObj), dist = lineObj.text.length;
1086
- var from = lineLeft(lineObj), to = lineRight(lineObj);
1087
- var fromX = paddingLeft(cm.display), toX = getX(to);
1088
-
1089
- if (x > toX) return {line: lineNo, ch: to, outside: wrongLine};
1090
- // Do a binary search between these bounds.
1091
- for (;;) {
1092
- if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1093
- var after = x - fromX < toX - x, ch = after ? from : to;
1094
- while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1095
- return {line: lineNo, ch: ch, after: after, outside: wrongLine};
1096
- }
1097
- var step = Math.ceil(dist / 2), middle = from + step;
1098
- if (bidi) {
1099
- middle = from;
1100
- for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1101
- }
1102
- var middleX = getX(middle);
1103
- if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;}
1104
- else {from = middle; fromX = middleX; dist = step;}
1105
- }
1106
- }
1107
-
1108
- var measureText;
1109
- function textHeight(display) {
1110
- if (display.cachedTextHeight != null) return display.cachedTextHeight;
1111
- if (measureText == null) {
1112
- measureText = elt("pre");
1113
- // Measure a bunch of lines, for browsers that compute
1114
- // fractional heights.
1115
- for (var i = 0; i < 49; ++i) {
1116
- measureText.appendChild(document.createTextNode("x"));
1117
- measureText.appendChild(elt("br"));
1118
- }
1119
- measureText.appendChild(document.createTextNode("x"));
1120
- }
1121
- removeChildrenAndAdd(display.measure, measureText);
1122
- var height = measureText.offsetHeight / 50;
1123
- if (height > 3) display.cachedTextHeight = height;
1124
- removeChildren(display.measure);
1125
- return height || 1;
1126
- }
1127
-
1128
- function charWidth(display) {
1129
- if (display.cachedCharWidth != null) return display.cachedCharWidth;
1130
- var anchor = elt("span", "x");
1131
- var pre = elt("pre", [anchor]);
1132
- removeChildrenAndAdd(display.measure, pre);
1133
- var width = anchor.offsetWidth;
1134
- if (width > 2) display.cachedCharWidth = width;
1135
- return width || 10;
1136
- }
1137
-
1138
- // OPERATIONS
1139
-
1140
- // Operations are used to wrap changes in such a way that each
1141
- // change won't have to update the cursor and display (which would
1142
- // be awkward, slow, and error-prone), but instead updates are
1143
- // batched and then all combined and executed at once.
1144
-
1145
- function startOperation(cm) {
1146
- if (cm.curOp) ++cm.curOp.depth;
1147
- else cm.curOp = {
1148
- // Nested operations delay update until the outermost one
1149
- // finishes.
1150
- depth: 1,
1151
- // An array of ranges of lines that have to be updated. See
1152
- // updateDisplay.
1153
- changes: [],
1154
- delayedCallbacks: [],
1155
- updateInput: null,
1156
- userSelChange: null,
1157
- textChanged: null,
1158
- selectionChanged: false,
1159
- updateMaxLine: false,
1160
- id: ++cm.nextOpId
1161
- };
1162
- }
1163
-
1164
- function endOperation(cm) {
1165
- var op = cm.curOp;
1166
- if (--op.depth) return;
1167
- cm.curOp = null;
1168
- var view = cm.view, display = cm.display;
1169
- if (op.updateMaxLine) computeMaxLength(view);
1170
- if (view.maxLineChanged && !cm.options.lineWrapping) {
1171
- var width = measureChar(cm, view.maxLine, view.maxLine.text.length).right;
1172
- display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px";
1173
- view.maxLineChanged = false;
1174
- var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
1175
- if (maxScrollLeft < view.scrollLeft)
1176
- setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
1177
- }
1178
- var newScrollPos, updated;
1179
- if (op.selectionChanged) {
1180
- var coords = cursorCoords(cm, view.sel.head);
1181
- newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1182
- }
1183
- if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
1184
- updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
1185
- if (!updated && op.selectionChanged) updateSelection(cm);
1186
- if (newScrollPos) scrollCursorIntoView(cm);
1187
- if (op.selectionChanged) restartBlink(cm);
1188
-
1189
- if (view.focused && op.updateInput)
1190
- resetInput(cm, op.userSelChange);
1191
-
1192
- if (op.textChanged)
1193
- signal(cm, "change", cm, op.textChanged);
1194
- if (op.selectionChanged) signal(cm, "cursorActivity", cm);
1195
- for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm);
1196
- }
1197
-
1198
- // Wraps a function in an operation. Returns the wrapped function.
1199
- function operation(cm1, f) {
1200
- return function() {
1201
- var cm = cm1 || this;
1202
- startOperation(cm);
1203
- try {var result = f.apply(cm, arguments);}
1204
- finally {endOperation(cm);}
1205
- return result;
1206
- };
1207
- }
1208
-
1209
- function regChange(cm, from, to, lendiff) {
1210
- cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1211
- }
1212
-
1213
- // INPUT HANDLING
1214
-
1215
- function slowPoll(cm) {
1216
- if (cm.view.pollingFast) return;
1217
- cm.display.poll.set(cm.options.pollInterval, function() {
1218
- readInput(cm);
1219
- if (cm.view.focused) slowPoll(cm);
1220
- });
1221
- }
1222
-
1223
- function fastPoll(cm) {
1224
- var missed = false;
1225
- cm.display.pollingFast = true;
1226
- function p() {
1227
- var changed = readInput(cm);
1228
- if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
1229
- else {cm.display.pollingFast = false; slowPoll(cm);}
1230
- }
1231
- cm.display.poll.set(20, p);
1232
- }
1233
-
1234
- // prevInput is a hack to work with IME. If we reset the textarea
1235
- // on every change, that breaks IME. So we look for changes
1236
- // compared to the previous content instead. (Modern browsers have
1237
- // events that indicate IME taking place, but these are not widely
1238
- // supported or compatible enough yet to rely on.)
1239
- function readInput(cm) {
1240
- var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel;
1241
- if (!view.focused || hasSelection(input) || isReadOnly(cm)) return false;
1242
- var text = input.value;
1243
- if (text == prevInput && posEq(sel.from, sel.to)) return false;
1244
- startOperation(cm);
1245
- view.sel.shift = false;
1246
- var same = 0, l = Math.min(prevInput.length, text.length);
1247
- while (same < l && prevInput[same] == text[same]) ++same;
1248
- var from = sel.from, to = sel.to;
1249
- if (same < prevInput.length)
1250
- from = {line: from.line, ch: from.ch - (prevInput.length - same)};
1251
- else if (view.overwrite && posEq(from, to) && !cm.display.pasteIncoming)
1252
- to = {line: to.line, ch: Math.min(getLine(cm.view.doc, to.line).text.length, to.ch + (text.length - same))};
1253
- var updateInput = cm.curOp.updateInput;
1254
- updateDoc(cm, from, to, splitLines(text.slice(same)), "end",
1255
- cm.display.pasteIncoming ? "paste" : "input", {from: from, to: to});
1256
- cm.curOp.updateInput = updateInput;
1257
- if (text.length > 1000) input.value = cm.display.prevInput = "";
1258
- else cm.display.prevInput = text;
1259
- endOperation(cm);
1260
- cm.display.pasteIncoming = false;
1261
- return true;
1262
- }
1263
-
1264
- function resetInput(cm, user) {
1265
- var view = cm.view, minimal, selected;
1266
- if (!posEq(view.sel.from, view.sel.to)) {
1267
- cm.display.prevInput = "";
1268
- minimal = hasCopyEvent &&
1269
- (view.sel.to.line - view.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1270
- if (minimal) cm.display.input.value = "-";
1271
- else cm.display.input.value = selected || cm.getSelection();
1272
- if (view.focused) selectInput(cm.display.input);
1273
- } else if (user) cm.display.prevInput = cm.display.input.value = "";
1274
- cm.display.inaccurateSelection = minimal;
1275
- }
1276
-
1277
- function focusInput(cm) {
1278
- if (cm.options.readOnly != "nocursor" && (ie || document.activeElement != cm.display.input))
1279
- cm.display.input.focus();
1280
- }
1281
-
1282
- function isReadOnly(cm) {
1283
- return cm.options.readOnly || cm.view.cantEdit;
1284
- }
1285
-
1286
- // EVENT HANDLERS
1287
-
1288
- function registerEventHandlers(cm) {
1289
- var d = cm.display;
1290
- on(d.scroller, "mousedown", operation(cm, onMouseDown));
1291
- on(d.scroller, "dblclick", operation(cm, e_preventDefault));
1292
- on(d.lineSpace, "selectstart", function(e) {
1293
- if (!eventInWidget(d, e)) e_preventDefault(e);
1294
- });
1295
- // Gecko browsers fire contextmenu *after* opening the menu, at
1296
- // which point we can't mess with it anymore. Context menu is
1297
- // handled in onMouseDown for Gecko.
1298
- if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1299
-
1300
- on(d.scroller, "scroll", function() {
1301
- setScrollTop(cm, d.scroller.scrollTop);
1302
- setScrollLeft(cm, d.scroller.scrollLeft, true);
1303
- signal(cm, "scroll", cm);
1304
- });
1305
- on(d.scrollbarV, "scroll", function() {
1306
- setScrollTop(cm, d.scrollbarV.scrollTop);
1307
- });
1308
- on(d.scrollbarH, "scroll", function() {
1309
- setScrollLeft(cm, d.scrollbarH.scrollLeft);
1310
- });
1311
-
1312
- on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1313
- on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1314
-
1315
- function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); }
1316
- on(d.scrollbarH, "mousedown", reFocus);
1317
- on(d.scrollbarV, "mousedown", reFocus);
1318
- // Prevent wrapper from ever scrolling
1319
- on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1320
-
1321
- if (!window.registered) window.registered = 0;
1322
- ++window.registered;
1323
- function onResize() {
1324
- // Might be a text scaling operation, clear size caches.
1325
- d.cachedCharWidth = d.cachedTextHeight = null;
1326
- clearCaches(cm);
1327
- updateDisplay(cm, true);
1328
- }
1329
- on(window, "resize", onResize);
1330
- // Above handler holds on to the editor and its data structures.
1331
- // Here we poll to unregister it when the editor is no longer in
1332
- // the document, so that it can be garbage-collected.
1333
- setTimeout(function unregister() {
1334
- for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1335
- if (p) setTimeout(unregister, 5000);
1336
- else {--window.registered; off(window, "resize", onResize);}
1337
- }, 5000);
1338
-
1339
- on(d.input, "keyup", operation(cm, function(e) {
1340
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1341
- if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = false;
1342
- }));
1343
- on(d.input, "input", bind(fastPoll, cm));
1344
- on(d.input, "keydown", operation(cm, onKeyDown));
1345
- on(d.input, "keypress", operation(cm, onKeyPress));
1346
- on(d.input, "focus", bind(onFocus, cm));
1347
- on(d.input, "blur", bind(onBlur, cm));
1348
-
1349
- function drag_(e) {
1350
- if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1351
- e_stop(e);
1352
- }
1353
- if (cm.options.dragDrop) {
1354
- on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
1355
- on(d.scroller, "dragenter", drag_);
1356
- on(d.scroller, "dragover", drag_);
1357
- on(d.scroller, "drop", operation(cm, onDrop));
1358
- }
1359
- on(d.scroller, "paste", function(e){
1360
- if (eventInWidget(d, e)) return;
1361
- focusInput(cm);
1362
- fastPoll(cm);
1363
- });
1364
- on(d.input, "paste", function() {
1365
- d.pasteIncoming = true;
1366
- fastPoll(cm);
1367
- });
1368
-
1369
- function prepareCopy() {
1370
- if (d.inaccurateSelection) {
1371
- d.prevInput = "";
1372
- d.inaccurateSelection = false;
1373
- d.input.value = cm.getSelection();
1374
- selectInput(d.input);
1375
- }
1376
- }
1377
- on(d.input, "cut", prepareCopy);
1378
- on(d.input, "copy", prepareCopy);
1379
-
1380
- // Needed to handle Tab key in KHTML
1381
- if (khtml) on(d.sizer, "mouseup", function() {
1382
- if (document.activeElement == d.input) d.input.blur();
1383
- focusInput(cm);
1384
- });
1385
- }
1386
-
1387
- function eventInWidget(display, e) {
1388
- for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1389
- if (!n) return true;
1390
- if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
1391
- n.parentNode == display.sizer && n != display.mover) return true;
1392
- }
1393
- }
1394
-
1395
- function posFromMouse(cm, e, liberal) {
1396
- var display = cm.display;
1397
- if (!liberal) {
1398
- var target = e_target(e);
1399
- if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
1400
- target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1401
- target == display.scrollbarFiller) return null;
1402
- }
1403
- var x, y, space = display.lineSpace.getBoundingClientRect();
1404
- // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1405
- try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1406
- return coordsChar(cm, x - space.left, y - space.top);
1407
- }
1408
-
1409
- var lastClick, lastDoubleClick;
1410
- function onMouseDown(e) {
1411
- var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc;
1412
- sel.shift = e_prop(e, "shiftKey");
1413
-
1414
- if (eventInWidget(display, e)) {
1415
- if (!webkit) {
1416
- display.scroller.draggable = false;
1417
- setTimeout(function(){display.scroller.draggable = true;}, 100);
1418
- }
1419
- return;
1420
- }
1421
- if (clickInGutter(cm, e)) return;
1422
- var start = posFromMouse(cm, e);
1423
-
1424
- switch (e_button(e)) {
1425
- case 3:
1426
- if (gecko) onContextMenu.call(cm, cm, e);
1427
- return;
1428
- case 2:
1429
- if (start) extendSelection(cm, start);
1430
- setTimeout(bind(focusInput, cm), 20);
1431
- e_preventDefault(e);
1432
- return;
1433
- }
1434
- // For button 1, if it was clicked inside the editor
1435
- // (posFromMouse returning non-null), we have to adjust the
1436
- // selection.
1437
- if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1438
-
1439
- if (!view.focused) onFocus(cm);
1440
-
1441
- var now = +new Date, type = "single";
1442
- if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
1443
- type = "triple";
1444
- e_preventDefault(e);
1445
- setTimeout(bind(focusInput, cm), 20);
1446
- selectLine(cm, start.line);
1447
- } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
1448
- type = "double";
1449
- lastDoubleClick = {time: now, pos: start};
1450
- e_preventDefault(e);
1451
- var word = findWordAt(getLine(doc, start.line).text, start);
1452
- extendSelection(cm, word.from, word.to);
1453
- } else { lastClick = {time: now, pos: start}; }
1454
-
1455
- var last = start;
1456
- if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
1457
- !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1458
- var dragEnd = operation(cm, function(e2) {
1459
- if (webkit) display.scroller.draggable = false;
1460
- view.draggingText = false;
1461
- off(document, "mouseup", dragEnd);
1462
- off(display.scroller, "drop", dragEnd);
1463
- if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1464
- e_preventDefault(e2);
1465
- extendSelection(cm, start);
1466
- focusInput(cm);
1467
- }
1468
- });
1469
- // Let the drag handler handle this.
1470
- if (webkit) display.scroller.draggable = true;
1471
- view.draggingText = dragEnd;
1472
- // IE's approach to draggable
1473
- if (display.scroller.dragDrop) display.scroller.dragDrop();
1474
- on(document, "mouseup", dragEnd);
1475
- on(display.scroller, "drop", dragEnd);
1476
- return;
1477
- }
1478
- e_preventDefault(e);
1479
- if (type == "single") extendSelection(cm, clipPos(doc, start));
1480
-
1481
- var startstart = sel.from, startend = sel.to;
1482
-
1483
- function doSelect(cur) {
1484
- if (type == "single") {
1485
- extendSelection(cm, clipPos(doc, start), cur);
1486
- return;
1487
- }
1488
-
1489
- startstart = clipPos(doc, startstart);
1490
- startend = clipPos(doc, startend);
1491
- if (type == "double") {
1492
- var word = findWordAt(getLine(doc, cur.line).text, cur);
1493
- if (posLess(cur, startstart)) extendSelection(cm, word.from, startend);
1494
- else extendSelection(cm, startstart, word.to);
1495
- } else if (type == "triple") {
1496
- if (posLess(cur, startstart)) extendSelection(cm, startend, clipPos(doc, {line: cur.line, ch: 0}));
1497
- else extendSelection(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0}));
1498
- }
1499
- }
1500
-
1501
- var editorSize = display.wrapper.getBoundingClientRect();
1502
- // Used to ensure timeout re-tries don't fire when another extend
1503
- // happened in the meantime (clearTimeout isn't reliable -- at
1504
- // least on Chrome, the timeouts still happen even when cleared,
1505
- // if the clear happens after their scheduled firing time).
1506
- var counter = 0;
1507
-
1508
- function extend(e) {
1509
- var curCount = ++counter;
1510
- var cur = posFromMouse(cm, e, true);
1511
- if (!cur) return;
1512
- if (!posEq(cur, last)) {
1513
- if (!view.focused) onFocus(cm);
1514
- last = cur;
1515
- doSelect(cur);
1516
- var visible = visibleLines(display, doc);
1517
- if (cur.line >= visible.to || cur.line < visible.from)
1518
- setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
1519
- } else {
1520
- var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
1521
- if (outside) setTimeout(operation(cm, function() {
1522
- if (counter != curCount) return;
1523
- display.scroller.scrollTop += outside;
1524
- extend(e);
1525
- }), 50);
1526
- }
1527
- }
1528
-
1529
- function done(e) {
1530
- counter = Infinity;
1531
- var cur = posFromMouse(cm, e);
1532
- if (cur) doSelect(cur);
1533
- e_preventDefault(e);
1534
- focusInput(cm);
1535
- off(document, "mousemove", move);
1536
- off(document, "mouseup", up);
1537
- }
1538
-
1539
- var move = operation(cm, function(e) {
1540
- if (!ie && !e_button(e)) done(e);
1541
- else extend(e);
1542
- });
1543
- var up = operation(cm, done);
1544
- on(document, "mousemove", move);
1545
- on(document, "mouseup", up);
1546
- }
1547
-
1548
- function onDrop(e) {
1549
- var cm = this;
1550
- if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1551
- return;
1552
- e_preventDefault(e);
1553
- var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1554
- if (!pos || isReadOnly(cm)) return;
1555
- if (files && files.length && window.FileReader && window.File) {
1556
- var n = files.length, text = Array(n), read = 0;
1557
- var loadFile = function(file, i) {
1558
- var reader = new FileReader;
1559
- reader.onload = function() {
1560
- text[i] = reader.result;
1561
- if (++read == n) {
1562
- pos = clipPos(cm.view.doc, pos);
1563
- operation(cm, function() {
1564
- var end = replaceRange(cm, text.join(""), pos, pos, "paste");
1565
- setSelection(cm, pos, end);
1566
- })();
1567
- }
1568
- };
1569
- reader.readAsText(file);
1570
- };
1571
- for (var i = 0; i < n; ++i) loadFile(files[i], i);
1572
- } else {
1573
- // Don't do a replace if the drop happened inside of the selected text.
1574
- if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) {
1575
- cm.view.draggingText(e);
1576
- // Ensure the editor is re-focused
1577
- setTimeout(bind(focusInput, cm), 20);
1578
- return;
1579
- }
1580
- try {
1581
- var text = e.dataTransfer.getData("Text");
1582
- if (text) {
1583
- var curFrom = cm.view.sel.from, curTo = cm.view.sel.to;
1584
- setSelection(cm, pos, pos);
1585
- if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo, "paste");
1586
- cm.replaceSelection(text, null, "paste");
1587
- focusInput(cm);
1588
- onFocus(cm);
1589
- }
1590
- }
1591
- catch(e){}
1592
- }
1593
- }
1594
-
1595
- function clickInGutter(cm, e) {
1596
- var display = cm.display;
1597
- try { var mX = e.clientX, mY = e.clientY; }
1598
- catch(e) { return false; }
1599
-
1600
- if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false;
1601
- e_preventDefault(e);
1602
- if (!hasHandler(cm, "gutterClick")) return true;
1603
-
1604
- var lineBox = display.lineDiv.getBoundingClientRect();
1605
- if (mY > lineBox.bottom) return true;
1606
- mY -= lineBox.top - display.viewOffset;
1607
-
1608
- for (var i = 0; i < cm.options.gutters.length; ++i) {
1609
- var g = display.gutters.childNodes[i];
1610
- if (g && g.getBoundingClientRect().right >= mX) {
1611
- var line = lineAtHeight(cm.view.doc, mY);
1612
- var gutter = cm.options.gutters[i];
1613
- signalLater(cm, cm, "gutterClick", cm, line, gutter, e);
1614
- break;
1615
- }
1616
- }
1617
- return true;
1618
- }
1619
-
1620
- function onDragStart(cm, e) {
1621
- if (eventInWidget(cm.display, e)) return;
1622
-
1623
- var txt = cm.getSelection();
1624
- e.dataTransfer.setData("Text", txt);
1625
-
1626
- // Use dummy image instead of default browsers image.
1627
- // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1628
- if (e.dataTransfer.setDragImage && !safari) {
1629
- var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1630
- if (opera) {
1631
- img.width = img.height = 1;
1632
- cm.display.wrapper.appendChild(img);
1633
- // Force a relayout, or Opera won't use our image for some obscure reason
1634
- img._top = img.offsetTop;
1635
- }
1636
- e.dataTransfer.setDragImage(img, 0, 0);
1637
- if (opera) img.parentNode.removeChild(img);
1638
- }
1639
- }
1640
-
1641
- function setScrollTop(cm, val) {
1642
- if (Math.abs(cm.view.scrollTop - val) < 2) return;
1643
- cm.view.scrollTop = val;
1644
- if (!gecko) updateDisplay(cm, [], val);
1645
- if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1646
- if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1647
- if (gecko) updateDisplay(cm, []);
1648
- }
1649
- function setScrollLeft(cm, val, isScroller) {
1650
- if (isScroller ? val == cm.view.scrollLeft : Math.abs(cm.view.scrollLeft - val) < 2) return;
1651
- val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
1652
- cm.view.scrollLeft = val;
1653
- alignHorizontally(cm);
1654
- if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1655
- if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
1656
- }
1657
-
1658
- // Since the delta values reported on mouse wheel events are
1659
- // unstandardized between browsers and even browser versions, and
1660
- // generally horribly unpredictable, this code starts by measuring
1661
- // the scroll effect that the first few mouse wheel events have,
1662
- // and, from that, detects the way it can convert deltas to pixel
1663
- // offsets afterwards.
1664
- //
1665
- // The reason we want to know the amount a wheel event will scroll
1666
- // is that it gives us a chance to update the display before the
1667
- // actual scrolling happens, reducing flickering.
1668
-
1669
- var wheelSamples = 0, wheelPixelsPerUnit = null;
1670
- // Fill in a browser-detected starting value on browsers where we
1671
- // know one. These don't have to be accurate -- the result of them
1672
- // being wrong would just be a slight flicker on the first wheel
1673
- // scroll (if it is large enough).
1674
- if (ie) wheelPixelsPerUnit = -.53;
1675
- else if (gecko) wheelPixelsPerUnit = 15;
1676
- else if (chrome) wheelPixelsPerUnit = -.7;
1677
- else if (safari) wheelPixelsPerUnit = -1/3;
1678
-
1679
- function onScrollWheel(cm, e) {
1680
- var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
1681
- if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
1682
- if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
1683
- else if (dy == null) dy = e.wheelDelta;
1684
-
1685
- // Webkit browsers on OS X abort momentum scrolls when the target
1686
- // of the scroll event is removed from the scrollable element.
1687
- // This hack (see related code in patchDisplay) makes sure the
1688
- // element is kept around.
1689
- if (dy && mac && webkit) {
1690
- for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
1691
- if (cur.lineObj) {
1692
- cm.display.currentWheelTarget = cur;
1693
- break;
1694
- }
1695
- }
1696
- }
1697
-
1698
- var display = cm.display, scroll = display.scroller;
1699
- // On some browsers, horizontal scrolling will cause redraws to
1700
- // happen before the gutter has been realigned, causing it to
1701
- // wriggle around in a most unseemly way. When we have an
1702
- // estimated pixels/delta value, we just handle horizontal
1703
- // scrolling entirely here. It'll be slightly off from native, but
1704
- // better than glitching out.
1705
- if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
1706
- if (dy)
1707
- setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1708
- setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
1709
- e_preventDefault(e);
1710
- display.wheelStartX = null; // Abort measurement, if in progress
1711
- return;
1712
- }
1713
-
1714
- if (dy && wheelPixelsPerUnit != null) {
1715
- var pixels = dy * wheelPixelsPerUnit;
1716
- var top = cm.view.scrollTop, bot = top + display.wrapper.clientHeight;
1717
- if (pixels < 0) top = Math.max(0, top + pixels - 50);
1718
- else bot = Math.min(cm.view.doc.height, bot + pixels + 50);
1719
- updateDisplay(cm, [], {top: top, bottom: bot});
1720
- }
1721
-
1722
- if (wheelSamples < 20) {
1723
- if (display.wheelStartX == null) {
1724
- display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
1725
- display.wheelDX = dx; display.wheelDY = dy;
1726
- setTimeout(function() {
1727
- if (display.wheelStartX == null) return;
1728
- var movedX = scroll.scrollLeft - display.wheelStartX;
1729
- var movedY = scroll.scrollTop - display.wheelStartY;
1730
- var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
1731
- (movedX && display.wheelDX && movedX / display.wheelDX);
1732
- display.wheelStartX = display.wheelStartY = null;
1733
- if (!sample) return;
1734
- wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
1735
- ++wheelSamples;
1736
- }, 200);
1737
- } else {
1738
- display.wheelDX += dx; display.wheelDY += dy;
1739
- }
1740
- }
1741
- }
1742
-
1743
- function doHandleBinding(cm, bound, dropShift) {
1744
- if (typeof bound == "string") {
1745
- bound = commands[bound];
1746
- if (!bound) return false;
1747
- }
1748
- // Ensure previous input has been read, so that the handler sees a
1749
- // consistent view of the document
1750
- if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
1751
- var view = cm.view, prevShift = view.sel.shift;
1752
- try {
1753
- if (isReadOnly(cm)) view.suppressEdits = true;
1754
- if (dropShift) view.sel.shift = false;
1755
- bound(cm);
1756
- } catch(e) {
1757
- if (e != Pass) throw e;
1758
- return false;
1759
- } finally {
1760
- view.sel.shift = prevShift;
1761
- view.suppressEdits = false;
1762
- }
1763
- return true;
1764
- }
1765
-
1766
- function allKeyMaps(cm) {
1767
- var maps = cm.view.keyMaps.slice(0);
1768
- maps.push(cm.options.keyMap);
1769
- if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys);
1770
- return maps;
1771
- }
1772
-
1773
- var maybeTransition;
1774
- function handleKeyBinding(cm, e) {
1775
- // Handle auto keymap transitions
1776
- var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
1777
- clearTimeout(maybeTransition);
1778
- if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
1779
- if (getKeyMap(cm.options.keyMap) == startMap)
1780
- cm.options.keyMap = (next.call ? next.call(null, cm) : next);
1781
- }, 50);
1782
-
1783
- var name = keyNames[e_prop(e, "keyCode")], handled = false;
1784
- if (name == null || e.altGraphKey) return false;
1785
- if (e_prop(e, "altKey")) name = "Alt-" + name;
1786
- if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
1787
- if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
1788
-
1789
- var stopped = false;
1790
- function stop() { stopped = true; }
1791
- var keymaps = allKeyMaps(cm);
1792
-
1793
- if (e_prop(e, "shiftKey")) {
1794
- handled = lookupKey("Shift-" + name, keymaps,
1795
- function(b) {return doHandleBinding(cm, b, true);}, stop)
1796
- || lookupKey(name, keymaps, function(b) {
1797
- if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
1798
- }, stop);
1799
- } else {
1800
- handled = lookupKey(name, keymaps,
1801
- function(b) { return doHandleBinding(cm, b); }, stop);
1802
- }
1803
- if (stopped) handled = false;
1804
- if (handled) {
1805
- e_preventDefault(e);
1806
- restartBlink(cm);
1807
- if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
1808
- }
1809
- return handled;
1810
- }
1811
-
1812
- function handleCharBinding(cm, e, ch) {
1813
- var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
1814
- function(b) { return doHandleBinding(cm, b, true); });
1815
- if (handled) {
1816
- e_preventDefault(e);
1817
- restartBlink(cm);
1818
- }
1819
- return handled;
1820
- }
1821
-
1822
- var lastStoppedKey = null;
1823
- function onKeyDown(e) {
1824
- var cm = this;
1825
- if (!cm.view.focused) onFocus(cm);
1826
- if (ie && e.keyCode == 27) { e.returnValue = false; }
1827
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1828
- var code = e_prop(e, "keyCode");
1829
- // IE does strange things with escape.
1830
- cm.view.sel.shift = code == 16 || e_prop(e, "shiftKey");
1831
- // First give onKeyEvent option a chance to handle this.
1832
- var handled = handleKeyBinding(cm, e);
1833
- if (opera) {
1834
- lastStoppedKey = handled ? code : null;
1835
- // Opera has no cut event... we try to at least catch the key combo
1836
- if (!handled && code == 88 && !hasCopyEvent && e_prop(e, mac ? "metaKey" : "ctrlKey"))
1837
- cm.replaceSelection("");
1838
- }
1839
- }
1840
-
1841
- function onKeyPress(e) {
1842
- var cm = this;
1843
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1844
- var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
1845
- if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
1846
- if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
1847
- var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
1848
- if (this.options.electricChars && this.view.mode.electricChars &&
1849
- this.options.smartIndent && !isReadOnly(this) &&
1850
- this.view.mode.electricChars.indexOf(ch) > -1)
1851
- setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75);
1852
- if (handleCharBinding(cm, e, ch)) return;
1853
- fastPoll(cm);
1854
- }
1855
-
1856
- function onFocus(cm) {
1857
- if (cm.options.readOnly == "nocursor") return;
1858
- if (!cm.view.focused) {
1859
- signal(cm, "focus", cm);
1860
- cm.view.focused = true;
1861
- if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1)
1862
- cm.display.scroller.className += " CodeMirror-focused";
1863
- resetInput(cm, true);
1864
- }
1865
- slowPoll(cm);
1866
- restartBlink(cm);
1867
- }
1868
- function onBlur(cm) {
1869
- if (cm.view.focused) {
1870
- signal(cm, "blur", cm);
1871
- cm.view.focused = false;
1872
- cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", "");
1873
- }
1874
- clearInterval(cm.display.blinker);
1875
- setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = false;}, 150);
1876
- }
1877
-
1878
- var detectingSelectAll;
1879
- function onContextMenu(cm, e) {
1880
- var display = cm.display;
1881
- if (eventInWidget(display, e)) return;
1882
-
1883
- var sel = cm.view.sel;
1884
- var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1885
- if (!pos || opera) return; // Opera is difficult.
1886
- if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1887
- operation(cm, setSelection)(cm, pos, pos);
1888
-
1889
- var oldCSS = display.input.style.cssText;
1890
- display.inputDiv.style.position = "absolute";
1891
- display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1892
- "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
1893
- "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1894
- focusInput(cm);
1895
- resetInput(cm, true);
1896
- // Adds "Select all" to context menu in FF
1897
- if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
1898
-
1899
- function rehide() {
1900
- display.inputDiv.style.position = "relative";
1901
- display.input.style.cssText = oldCSS;
1902
- if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
1903
- slowPoll(cm);
1904
-
1905
- // Try to detect the user choosing select-all
1906
- if (display.input.selectionStart != null) {
1907
- clearTimeout(detectingSelectAll);
1908
- var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
1909
- display.prevInput = " ";
1910
- display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
1911
- detectingSelectAll = setTimeout(function poll(){
1912
- if (display.prevInput == " " && display.input.selectionStart == 0)
1913
- operation(cm, commands.selectAll)(cm);
1914
- else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
1915
- else resetInput(cm);
1916
- }, 200);
1917
- }
1918
- }
1919
-
1920
- if (gecko) {
1921
- e_stop(e);
1922
- on(window, "mouseup", function mouseup() {
1923
- off(window, "mouseup", mouseup);
1924
- setTimeout(rehide, 20);
1925
- });
1926
- } else {
1927
- setTimeout(rehide, 50);
1928
- }
1929
- }
1930
-
1931
- // UPDATING
1932
-
1933
- // Replace the range from from to to by the strings in newText.
1934
- // Afterwards, set the selection to selFrom, selTo.
1935
- function updateDoc(cm, from, to, newText, selUpdate, origin) {
1936
- // Possibly split or suppress the update based on the presence
1937
- // of read-only spans in its range.
1938
- var split = sawReadOnlySpans &&
1939
- removeReadOnlyRanges(cm.view.doc, from, to);
1940
- if (split) {
1941
- for (var i = split.length - 1; i >= 1; --i)
1942
- updateDocInner(cm, split[i].from, split[i].to, [""], origin);
1943
- if (split.length)
1944
- return updateDocInner(cm, split[0].from, split[0].to, newText, selUpdate, origin);
1945
- } else {
1946
- return updateDocInner(cm, from, to, newText, selUpdate, origin);
1947
- }
1948
- }
1949
-
1950
- function updateDocInner(cm, from, to, newText, selUpdate, origin) {
1951
- if (cm.view.suppressEdits) return;
1952
-
1953
- var view = cm.view, doc = view.doc, old = [];
1954
- doc.iter(from.line, to.line + 1, function(line) {
1955
- old.push(newHL(line.text, line.markedSpans));
1956
- });
1957
- var startSelFrom = view.sel.from, startSelTo = view.sel.to;
1958
- var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
1959
- var retval = updateDocNoUndo(cm, from, to, lines, selUpdate, origin);
1960
- if (view.history) addChange(cm, from.line, newText.length, old, origin,
1961
- startSelFrom, startSelTo, view.sel.from, view.sel.to);
1962
- return retval;
1963
- }
1964
-
1965
- function unredoHelper(cm, type) {
1966
- var doc = cm.view.doc, hist = cm.view.history;
1967
- var set = (type == "undo" ? hist.done : hist.undone).pop();
1968
- if (!set) return;
1969
- var anti = {events: [], fromBefore: set.fromAfter, toBefore: set.toAfter,
1970
- fromAfter: set.fromBefore, toAfter: set.toBefore};
1971
- for (var i = set.events.length - 1; i >= 0; i -= 1) {
1972
- hist.dirtyCounter += type == "undo" ? -1 : 1;
1973
- var change = set.events[i];
1974
- var replaced = [], end = change.start + change.added;
1975
- doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
1976
- anti.events.push({start: change.start, added: change.old.length, old: replaced});
1977
- var selPos = i ? null : {from: set.fromBefore, to: set.toBefore};
1978
- updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length},
1979
- change.old, selPos, type);
1980
- }
1981
- (type == "undo" ? hist.undone : hist.done).push(anti);
1982
- }
1983
-
1984
- function updateDocNoUndo(cm, from, to, lines, selUpdate, origin) {
1985
- var view = cm.view, doc = view.doc, display = cm.display;
1986
- if (view.suppressEdits) return;
1987
-
1988
- var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
1989
- var recomputeMaxLength = false, checkWidthStart = from.line;
1990
- if (!cm.options.lineWrapping) {
1991
- checkWidthStart = lineNo(visualLine(doc, firstLine));
1992
- doc.iter(checkWidthStart, to.line + 1, function(line) {
1993
- if (line == view.maxLine) {
1994
- recomputeMaxLength = true;
1995
- return true;
1996
- }
1997
- });
1998
- }
1999
-
2000
- var lastHL = lst(lines), th = textHeight(display);
2001
-
2002
- // First adjust the line structure
2003
- if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
2004
- // This is a whole-line replace. Treated specially to make
2005
- // sure line objects move the way they are supposed to.
2006
- var added = [];
2007
- for (var i = 0, e = lines.length - 1; i < e; ++i)
2008
- added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
2009
- updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL));
2010
- if (nlines) doc.remove(from.line, nlines, cm);
2011
- if (added.length) doc.insert(from.line, added);
2012
- } else if (firstLine == lastLine) {
2013
- if (lines.length == 1) {
2014
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
2015
- firstLine.text.slice(to.ch), hlSpans(lines[0]));
2016
- } else {
2017
- for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
2018
- added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
2019
- added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th));
2020
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
2021
- doc.insert(from.line + 1, added);
2022
- }
2023
- } else if (lines.length == 1) {
2024
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
2025
- lastLine.text.slice(to.ch), hlSpans(lines[0]));
2026
- doc.remove(from.line + 1, nlines, cm);
2027
- } else {
2028
- var added = [];
2029
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
2030
- updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
2031
- for (var i = 1, e = lines.length - 1; i < e; ++i)
2032
- added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
2033
- if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm);
2034
- doc.insert(from.line + 1, added);
2035
- }
2036
-
2037
- if (cm.options.lineWrapping) {
2038
- var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3);
2039
- doc.iter(from.line, from.line + lines.length, function(line) {
2040
- if (line.height == 0) return;
2041
- var guess = (Math.ceil(line.text.length / perLine) || 1) * th;
2042
- if (guess != line.height) updateLineHeight(line, guess);
2043
- });
2044
- } else {
2045
- doc.iter(checkWidthStart, from.line + lines.length, function(line) {
2046
- var len = lineLength(doc, line);
2047
- if (len > view.maxLineLength) {
2048
- view.maxLine = line;
2049
- view.maxLineLength = len;
2050
- view.maxLineChanged = true;
2051
- recomputeMaxLength = false;
2052
- }
2053
- });
2054
- if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
2055
- }
2056
-
2057
- // Adjust frontier, schedule worker
2058
- view.frontier = Math.min(view.frontier, from.line);
2059
- startWorker(cm, 400);
2060
-
2061
- var lendiff = lines.length - nlines - 1;
2062
- // Remember that these lines changed, for updating the display
2063
- regChange(cm, from.line, to.line + 1, lendiff);
2064
- if (hasHandler(cm, "change")) {
2065
- // Normalize lines to contain only strings, since that's what
2066
- // the change event handler expects
2067
- for (var i = 0; i < lines.length; ++i)
2068
- if (typeof lines[i] != "string") lines[i] = lines[i].text;
2069
- var changeObj = {from: from, to: to, text: lines, origin: origin};
2070
- if (cm.curOp.textChanged) {
2071
- for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
2072
- cur.next = changeObj;
2073
- } else cm.curOp.textChanged = changeObj;
2074
- }
2075
-
2076
- // Update the selection
2077
- var newSelFrom, newSelTo, end = {line: from.line + lines.length - 1,
2078
- ch: hlText(lastHL).length + (lines.length == 1 ? from.ch : 0)};
2079
- if (selUpdate && typeof selUpdate != "string") {
2080
- if (selUpdate.from) { newSelFrom = selUpdate.from; newSelTo = selUpdate.to; }
2081
- else newSelFrom = newSelTo = selUpdate;
2082
- } else if (selUpdate == "end") {
2083
- newSelFrom = newSelTo = end;
2084
- } else if (selUpdate == "start") {
2085
- newSelFrom = newSelTo = from;
2086
- } else if (selUpdate == "around") {
2087
- newSelFrom = from; newSelTo = end;
2088
- } else {
2089
- var adjustPos = function(pos) {
2090
- if (posLess(pos, from)) return pos;
2091
- if (!posLess(to, pos)) return end;
2092
- var line = pos.line + lendiff;
2093
- var ch = pos.ch;
2094
- if (pos.line == to.line)
2095
- ch += hlText(lastHL).length - (to.ch - (to.line == from.line ? from.ch : 0));
2096
- return {line: line, ch: ch};
2097
- };
2098
- newSelFrom = adjustPos(view.sel.from);
2099
- newSelTo = adjustPos(view.sel.to);
2100
- }
2101
- setSelection(cm, newSelFrom, newSelTo, null, true);
2102
- return end;
2103
- }
2104
-
2105
- function replaceRange(cm, code, from, to, origin) {
2106
- if (!to) to = from;
2107
- if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
2108
- return updateDoc(cm, from, to, splitLines(code), null, origin);
2109
- }
2110
-
2111
- // SELECTION
2112
-
2113
- function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2114
- function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2115
- function copyPos(x) {return {line: x.line, ch: x.ch};}
2116
-
2117
- function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));}
2118
- function clipPos(doc, pos) {
2119
- if (pos.line < 0) return {line: 0, ch: 0};
2120
- if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length};
2121
- var ch = pos.ch, linelen = getLine(doc, pos.line).text.length;
2122
- if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
2123
- else if (ch < 0) return {line: pos.line, ch: 0};
2124
- else return pos;
2125
- }
2126
- function isLine(doc, l) {return l >= 0 && l < doc.size;}
2127
-
2128
- // If shift is held, this will move the selection anchor. Otherwise,
2129
- // it'll set the whole selection.
2130
- function extendSelection(cm, pos, other, bias) {
2131
- var sel = cm.view.sel;
2132
- if (sel.shift || sel.extend) {
2133
- var anchor = sel.anchor;
2134
- if (other) {
2135
- var posBefore = posLess(pos, anchor);
2136
- if (posBefore != posLess(other, anchor)) {
2137
- anchor = pos;
2138
- pos = other;
2139
- } else if (posBefore != posLess(pos, other)) {
2140
- pos = other;
2141
- }
2142
- }
2143
- setSelection(cm, anchor, pos, bias);
2144
- } else {
2145
- setSelection(cm, pos, other || pos, bias);
2146
- }
2147
- cm.curOp.userSelChange = true;
2148
- }
2149
-
2150
- // Update the selection. Last two args are only used by
2151
- // updateDoc, since they have to be expressed in the line
2152
- // numbers before the update.
2153
- function setSelection(cm, anchor, head, bias, checkAtomic) {
2154
- cm.view.goalColumn = null;
2155
- var sel = cm.view.sel;
2156
- // Skip over atomic spans.
2157
- if (checkAtomic || !posEq(anchor, sel.anchor))
2158
- anchor = skipAtomic(cm, anchor, bias, checkAtomic != "push");
2159
- if (checkAtomic || !posEq(head, sel.head))
2160
- head = skipAtomic(cm, head, bias, checkAtomic != "push");
2161
-
2162
- if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2163
-
2164
- sel.anchor = anchor; sel.head = head;
2165
- var inv = posLess(head, anchor);
2166
- sel.from = inv ? head : anchor;
2167
- sel.to = inv ? anchor : head;
2168
-
2169
- cm.curOp.updateInput = true;
2170
- cm.curOp.selectionChanged = true;
2171
- }
2172
-
2173
- function reCheckSelection(cm) {
2174
- setSelection(cm, cm.view.sel.from, cm.view.sel.to, null, "push");
2175
- }
2176
-
2177
- function skipAtomic(cm, pos, bias, mayClear) {
2178
- var doc = cm.view.doc, flipped = false, curPos = pos;
2179
- var dir = bias || 1;
2180
- cm.view.cantEdit = false;
2181
- search: for (;;) {
2182
- var line = getLine(doc, curPos.line), toClear;
2183
- if (line.markedSpans) {
2184
- for (var i = 0; i < line.markedSpans.length; ++i) {
2185
- var sp = line.markedSpans[i], m = sp.marker;
2186
- if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2187
- (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2188
- if (mayClear && m.clearOnEnter) {
2189
- (toClear || (toClear = [])).push(m);
2190
- continue;
2191
- } else if (!m.atomic) continue;
2192
- var newPos = m.find()[dir < 0 ? "from" : "to"];
2193
- if (posEq(newPos, curPos)) {
2194
- newPos.ch += dir;
2195
- if (newPos.ch < 0) {
2196
- if (newPos.line) newPos = clipPos(doc, {line: newPos.line - 1});
2197
- else newPos = null;
2198
- } else if (newPos.ch > line.text.length) {
2199
- if (newPos.line < doc.size - 1) newPos = {line: newPos.line + 1, ch: 0};
2200
- else newPos = null;
2201
- }
2202
- if (!newPos) {
2203
- if (flipped) {
2204
- // Driven in a corner -- no valid cursor position found at all
2205
- // -- try again *with* clearing, if we didn't already
2206
- if (!mayClear) return skipAtomic(cm, pos, bias, true);
2207
- // Otherwise, turn off editing until further notice, and return the start of the doc
2208
- cm.view.cantEdit = true;
2209
- return {line: 0, ch: 0};
2210
- }
2211
- flipped = true; newPos = pos; dir = -dir;
2212
- }
2213
- }
2214
- curPos = newPos;
2215
- continue search;
2216
- }
2217
- }
2218
- if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear();
2219
- }
2220
- return curPos;
2221
- }
2222
- }
2223
-
2224
- // SCROLLING
2225
-
2226
- function scrollCursorIntoView(cm) {
2227
- var view = cm.view;
2228
- var coords = scrollPosIntoView(cm, view.sel.head);
2229
- if (!view.focused) return;
2230
- var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
2231
- if (coords.top + box.top < 0) doScroll = true;
2232
- else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2233
- if (doScroll != null && !phantom) {
2234
- var hidden = display.cursor.style.display == "none";
2235
- if (hidden) {
2236
- display.cursor.style.display = "";
2237
- display.cursor.style.left = coords.left + "px";
2238
- display.cursor.style.top = (coords.top - display.viewOffset) + "px";
2239
- }
2240
- display.cursor.scrollIntoView(doScroll);
2241
- if (hidden) display.cursor.style.display = "none";
2242
- }
2243
- }
2244
-
2245
- function scrollPosIntoView(cm, pos) {
2246
- for (;;) {
2247
- var changed = false, coords = cursorCoords(cm, pos);
2248
- var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
2249
- var startTop = cm.view.scrollTop, startLeft = cm.view.scrollLeft;
2250
- if (scrollPos.scrollTop != null) {
2251
- setScrollTop(cm, scrollPos.scrollTop);
2252
- if (Math.abs(cm.view.scrollTop - startTop) > 1) changed = true;
2253
- }
2254
- if (scrollPos.scrollLeft != null) {
2255
- setScrollLeft(cm, scrollPos.scrollLeft);
2256
- if (Math.abs(cm.view.scrollLeft - startLeft) > 1) changed = true;
2257
- }
2258
- if (!changed) return coords;
2259
- }
2260
- }
2261
-
2262
- function scrollIntoView(cm, x1, y1, x2, y2) {
2263
- var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
2264
- if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
2265
- if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
2266
- }
2267
-
2268
- function calculateScrollPos(cm, x1, y1, x2, y2) {
2269
- var display = cm.display, pt = paddingTop(display);
2270
- y1 += pt; y2 += pt;
2271
- var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2272
- var docBottom = cm.view.doc.height + 2 * pt;
2273
- var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
2274
- if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
2275
- else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
2276
-
2277
- var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
2278
- x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
2279
- var gutterw = display.gutters.offsetWidth;
2280
- var atLeft = x1 < gutterw + 10;
2281
- if (x1 < screenleft + gutterw || atLeft) {
2282
- if (atLeft) x1 = 0;
2283
- result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
2284
- } else if (x2 > screenw + screenleft - 3) {
2285
- result.scrollLeft = x2 + 10 - screenw;
2286
- }
2287
- return result;
2288
- }
2289
-
2290
- // API UTILITIES
2291
-
2292
- function indentLine(cm, n, how, aggressive) {
2293
- var doc = cm.view.doc;
2294
- if (!how) how = "add";
2295
- if (how == "smart") {
2296
- if (!cm.view.mode.indent) how = "prev";
2297
- else var state = getStateBefore(cm, n);
2298
- }
2299
-
2300
- var tabSize = cm.options.tabSize;
2301
- var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
2302
- var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2303
- if (how == "smart") {
2304
- indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
2305
- if (indentation == Pass) {
2306
- if (!aggressive) return;
2307
- how = "prev";
2308
- }
2309
- }
2310
- if (how == "prev") {
2311
- if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
2312
- else indentation = 0;
2313
- }
2314
- else if (how == "add") indentation = curSpace + cm.options.indentUnit;
2315
- else if (how == "subtract") indentation = curSpace - cm.options.indentUnit;
2316
- indentation = Math.max(0, indentation);
2317
-
2318
- var indentString = "", pos = 0;
2319
- if (cm.options.indentWithTabs)
2320
- for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
2321
- if (pos < indentation) indentString += spaceStr(indentation - pos);
2322
-
2323
- if (indentString != curSpaceString)
2324
- replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}, "input");
2325
- line.stateAfter = null;
2326
- }
2327
-
2328
- function changeLine(cm, handle, op) {
2329
- var no = handle, line = handle, doc = cm.view.doc;
2330
- if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2331
- else no = lineNo(handle);
2332
- if (no == null) return null;
2333
- if (op(line, no)) regChange(cm, no, no + 1);
2334
- else return null;
2335
- return line;
2336
- }
2337
-
2338
- function findPosH(cm, dir, unit, visually) {
2339
- var doc = cm.view.doc, end = cm.view.sel.head, line = end.line, ch = end.ch;
2340
- var lineObj = getLine(doc, line);
2341
- function findNextLine() {
2342
- var l = line + dir;
2343
- if (l < 0 || l == doc.size) return false;
2344
- line = l;
2345
- return lineObj = getLine(doc, l);
2346
- }
2347
- function moveOnce(boundToLine) {
2348
- var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
2349
- if (next == null) {
2350
- if (!boundToLine && findNextLine()) {
2351
- if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
2352
- else ch = dir < 0 ? lineObj.text.length : 0;
2353
- } else return false;
2354
- } else ch = next;
2355
- return true;
2356
- }
2357
- if (unit == "char") moveOnce();
2358
- else if (unit == "column") moveOnce(true);
2359
- else if (unit == "word") {
2360
- var sawWord = false;
2361
- for (;;) {
2362
- if (dir < 0) if (!moveOnce()) break;
2363
- if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
2364
- else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
2365
- if (dir > 0) if (!moveOnce()) break;
2366
- }
2367
- }
2368
- return skipAtomic(cm, {line: line, ch: ch}, dir, true);
2369
- }
2370
-
2371
- function findWordAt(line, pos) {
2372
- var start = pos.ch, end = pos.ch;
2373
- if (line) {
2374
- if (pos.after === false || end == line.length) --start; else ++end;
2375
- var startChar = line.charAt(start);
2376
- var check = isWordChar(startChar) ? isWordChar :
2377
- /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
2378
- function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
2379
- while (start > 0 && check(line.charAt(start - 1))) --start;
2380
- while (end < line.length && check(line.charAt(end))) ++end;
2381
- }
2382
- return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
2383
- }
2384
-
2385
- function selectLine(cm, line) {
2386
- extendSelection(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0}));
2387
- }
2388
-
2389
- // PROTOTYPE
2390
-
2391
- // The publicly visible API. Note that operation(null, f) means
2392
- // 'wrap f in an operation, performed on its `this` parameter'
2393
-
2394
- CodeMirror.prototype = {
2395
- getValue: function(lineSep) {
2396
- var text = [], doc = this.view.doc;
2397
- doc.iter(0, doc.size, function(line) { text.push(line.text); });
2398
- return text.join(lineSep || "\n");
2399
- },
2400
-
2401
- setValue: operation(null, function(code) {
2402
- var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length;
2403
- updateDocInner(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top, "setValue");
2404
- }),
2405
-
2406
- getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); },
2407
-
2408
- replaceSelection: operation(null, function(code, collapse, origin) {
2409
- var sel = this.view.sel;
2410
- updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around", origin);
2411
- }),
2412
-
2413
- focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
2414
-
2415
- setOption: function(option, value) {
2416
- var options = this.options, old = options[option];
2417
- if (options[option] == value && option != "mode") return;
2418
- options[option] = value;
2419
- if (optionHandlers.hasOwnProperty(option))
2420
- operation(this, optionHandlers[option])(this, value, old);
2421
- },
2422
-
2423
- getOption: function(option) {return this.options[option];},
2424
-
2425
- getMode: function() {return this.view.mode;},
2426
-
2427
- addKeyMap: function(map) {
2428
- this.view.keyMaps.push(map);
2429
- },
2430
-
2431
- removeKeyMap: function(map) {
2432
- var maps = this.view.keyMaps;
2433
- for (var i = 0; i < maps.length; ++i)
2434
- if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
2435
- maps.splice(i, 1);
2436
- return true;
2437
- }
2438
- },
2439
-
2440
- addOverlay: operation(null, function(spec, options) {
2441
- var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2442
- if (mode.startState) throw new Error("Overlays may not be stateful.");
2443
- this.view.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2444
- this.view.modeGen++;
2445
- regChange(this, 0, this.view.doc.size);
2446
- }),
2447
- removeOverlay: operation(null, function(spec) {
2448
- var overlays = this.view.overlays;
2449
- for (var i = 0; i < overlays.length; ++i) {
2450
- if (overlays[i].modeSpec == spec) {
2451
- overlays.splice(i, 1);
2452
- this.view.modeGen++;
2453
- regChange(this, 0, this.view.doc.size);
2454
- return;
2455
- }
2456
- }
2457
- }),
2458
-
2459
- undo: operation(null, function() {unredoHelper(this, "undo");}),
2460
- redo: operation(null, function() {unredoHelper(this, "redo");}),
2461
-
2462
- indentLine: operation(null, function(n, dir, aggressive) {
2463
- if (typeof dir != "string") {
2464
- if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2465
- else dir = dir ? "add" : "subtract";
2466
- }
2467
- if (isLine(this.view.doc, n)) indentLine(this, n, dir, aggressive);
2468
- }),
2469
-
2470
- indentSelection: operation(null, function(how) {
2471
- var sel = this.view.sel;
2472
- if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
2473
- var e = sel.to.line - (sel.to.ch ? 0 : 1);
2474
- for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
2475
- }),
2476
-
2477
- historySize: function() {
2478
- var hist = this.view.history;
2479
- return {undo: hist.done.length, redo: hist.undone.length};
2480
- },
2481
-
2482
- clearHistory: function() {this.view.history = makeHistory();},
2483
-
2484
- markClean: function() {
2485
- this.view.history.dirtyCounter = 0;
2486
- this.view.history.lastOp = this.view.history.lastOrigin = null;
2487
- },
2488
-
2489
- isClean: function () {return this.view.history.dirtyCounter == 0;},
2490
-
2491
- getHistory: function() {
2492
- var hist = this.view.history;
2493
- function cp(arr) {
2494
- for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
2495
- var set = arr[i];
2496
- nw.push({events: nwelt = [], fromBefore: set.fromBefore, toBefore: set.toBefore,
2497
- fromAfter: set.fromAfter, toAfter: set.toAfter});
2498
- for (var j = 0, elt = set.events; j < elt.length; ++j) {
2499
- var old = [], cur = elt[j];
2500
- nwelt.push({start: cur.start, added: cur.added, old: old});
2501
- for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
2502
- }
2503
- }
2504
- return nw;
2505
- }
2506
- return {done: cp(hist.done), undone: cp(hist.undone)};
2507
- },
2508
-
2509
- setHistory: function(histData) {
2510
- var hist = this.view.history = makeHistory();
2511
- hist.done = histData.done;
2512
- hist.undone = histData.undone;
2513
- },
2514
-
2515
- // Fetch the parser token for a given character. Useful for hacks
2516
- // that want to inspect the mode state (say, for completion).
2517
- getTokenAt: function(pos) {
2518
- var doc = this.view.doc;
2519
- pos = clipPos(doc, pos);
2520
- var state = getStateBefore(this, pos.line), mode = this.view.mode;
2521
- var line = getLine(doc, pos.line);
2522
- var stream = new StringStream(line.text, this.options.tabSize);
2523
- while (stream.pos < pos.ch && !stream.eol()) {
2524
- stream.start = stream.pos;
2525
- var style = mode.token(stream, state);
2526
- }
2527
- return {start: stream.start,
2528
- end: stream.pos,
2529
- string: stream.current(),
2530
- className: style || null, // Deprecated, use 'type' instead
2531
- type: style || null,
2532
- state: state};
2533
- },
2534
-
2535
- getStateAfter: function(line) {
2536
- var doc = this.view.doc;
2537
- line = clipLine(doc, line == null ? doc.size - 1: line);
2538
- return getStateBefore(this, line + 1);
2539
- },
2540
-
2541
- cursorCoords: function(start, mode) {
2542
- var pos, sel = this.view.sel;
2543
- if (start == null) pos = sel.head;
2544
- else if (typeof start == "object") pos = clipPos(this.view.doc, start);
2545
- else pos = start ? sel.from : sel.to;
2546
- return cursorCoords(this, pos, mode || "page");
2547
- },
2548
-
2549
- charCoords: function(pos, mode) {
2550
- return charCoords(this, clipPos(this.view.doc, pos), mode || "page");
2551
- },
2552
-
2553
- coordsChar: function(coords) {
2554
- var off = this.display.lineSpace.getBoundingClientRect();
2555
- return coordsChar(this, coords.left - off.left, coords.top - off.top);
2556
- },
2557
-
2558
- defaultTextHeight: function() { return textHeight(this.display); },
2559
-
2560
- markText: operation(null, function(from, to, options) {
2561
- return markText(this, clipPos(this.view.doc, from), clipPos(this.view.doc, to),
2562
- options, "range");
2563
- }),
2564
-
2565
- setBookmark: operation(null, function(pos, widget) {
2566
- pos = clipPos(this.view.doc, pos);
2567
- return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark");
2568
- }),
2569
-
2570
- findMarksAt: function(pos) {
2571
- var doc = this.view.doc;
2572
- pos = clipPos(doc, pos);
2573
- var markers = [], spans = getLine(doc, pos.line).markedSpans;
2574
- if (spans) for (var i = 0; i < spans.length; ++i) {
2575
- var span = spans[i];
2576
- if ((span.from == null || span.from <= pos.ch) &&
2577
- (span.to == null || span.to >= pos.ch))
2578
- markers.push(span.marker);
2579
- }
2580
- return markers;
2581
- },
2582
-
2583
- setGutterMarker: operation(null, function(line, gutterID, value) {
2584
- return changeLine(this, line, function(line) {
2585
- var markers = line.gutterMarkers || (line.gutterMarkers = {});
2586
- markers[gutterID] = value;
2587
- if (!value && isEmpty(markers)) line.gutterMarkers = null;
2588
- return true;
2589
- });
2590
- }),
2591
-
2592
- clearGutter: operation(null, function(gutterID) {
2593
- var i = 0, cm = this, doc = cm.view.doc;
2594
- doc.iter(0, doc.size, function(line) {
2595
- if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
2596
- line.gutterMarkers[gutterID] = null;
2597
- regChange(cm, i, i + 1);
2598
- if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
2599
- }
2600
- ++i;
2601
- });
2602
- }),
2603
-
2604
- addLineClass: operation(null, function(handle, where, cls) {
2605
- return changeLine(this, handle, function(line) {
2606
- var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2607
- if (!line[prop]) line[prop] = cls;
2608
- else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
2609
- else line[prop] += " " + cls;
2610
- return true;
2611
- });
2612
- }),
2613
-
2614
- removeLineClass: operation(null, function(handle, where, cls) {
2615
- return changeLine(this, handle, function(line) {
2616
- var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2617
- var cur = line[prop];
2618
- if (!cur) return false;
2619
- else if (cls == null) line[prop] = null;
2620
- else {
2621
- var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
2622
- if (upd == cur) return false;
2623
- line[prop] = upd || null;
2624
- }
2625
- return true;
2626
- });
2627
- }),
2628
-
2629
- addLineWidget: operation(null, function(handle, node, options) {
2630
- return addLineWidget(this, handle, node, options);
2631
- }),
2632
-
2633
- removeLineWidget: function(widget) { widget.clear(); },
2634
-
2635
- lineInfo: function(line) {
2636
- if (typeof line == "number") {
2637
- if (!isLine(this.view.doc, line)) return null;
2638
- var n = line;
2639
- line = getLine(this.view.doc, line);
2640
- if (!line) return null;
2641
- } else {
2642
- var n = lineNo(line);
2643
- if (n == null) return null;
2644
- }
2645
- return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
2646
- textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
2647
- widgets: line.widgets};
2648
- },
2649
-
2650
- getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
2651
-
2652
- addWidget: function(pos, node, scroll, vert, horiz) {
2653
- var display = this.display;
2654
- pos = cursorCoords(this, clipPos(this.view.doc, pos));
2655
- var top = pos.top, left = pos.left;
2656
- node.style.position = "absolute";
2657
- display.sizer.appendChild(node);
2658
- if (vert == "over") top = pos.top;
2659
- else if (vert == "near") {
2660
- var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height),
2661
- hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
2662
- if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight)
2663
- top = pos.top - node.offsetHeight;
2664
- if (left + node.offsetWidth > hspace)
2665
- left = hspace - node.offsetWidth;
2666
- }
2667
- node.style.top = (top + paddingTop(display)) + "px";
2668
- node.style.left = node.style.right = "";
2669
- if (horiz == "right") {
2670
- left = display.sizer.clientWidth - node.offsetWidth;
2671
- node.style.right = "0px";
2672
- } else {
2673
- if (horiz == "left") left = 0;
2674
- else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
2675
- node.style.left = left + "px";
2676
- }
2677
- if (scroll)
2678
- scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
2679
- },
2680
-
2681
- lineCount: function() {return this.view.doc.size;},
2682
-
2683
- clipPos: function(pos) {return clipPos(this.view.doc, pos);},
2684
-
2685
- getCursor: function(start) {
2686
- var sel = this.view.sel, pos;
2687
- if (start == null || start == "head") pos = sel.head;
2688
- else if (start == "anchor") pos = sel.anchor;
2689
- else if (start == "end" || start === false) pos = sel.to;
2690
- else pos = sel.from;
2691
- return copyPos(pos);
2692
- },
2693
-
2694
- somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);},
2695
-
2696
- setCursor: operation(null, function(line, ch, extend) {
2697
- var pos = clipPos(this.view.doc, typeof line == "number" ? {line: line, ch: ch || 0} : line);
2698
- if (extend) extendSelection(this, pos);
2699
- else setSelection(this, pos, pos);
2700
- }),
2701
-
2702
- setSelection: operation(null, function(anchor, head) {
2703
- var doc = this.view.doc;
2704
- setSelection(this, clipPos(doc, anchor), clipPos(doc, head || anchor));
2705
- }),
2706
-
2707
- extendSelection: operation(null, function(from, to) {
2708
- var doc = this.view.doc;
2709
- extendSelection(this, clipPos(doc, from), to && clipPos(doc, to));
2710
- }),
2711
-
2712
- setExtending: function(val) {this.view.sel.extend = val;},
2713
-
2714
- getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
2715
-
2716
- getLineHandle: function(line) {
2717
- var doc = this.view.doc;
2718
- if (isLine(doc, line)) return getLine(doc, line);
2719
- },
2720
-
2721
- getLineNumber: function(line) {return lineNo(line);},
2722
-
2723
- setLine: operation(null, function(line, text) {
2724
- if (isLine(this.view.doc, line))
2725
- replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length});
2726
- }),
2727
-
2728
- removeLine: operation(null, function(line) {
2729
- if (isLine(this.view.doc, line))
2730
- replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0}));
2731
- }),
2732
-
2733
- replaceRange: operation(null, function(code, from, to) {
2734
- var doc = this.view.doc;
2735
- from = clipPos(doc, from);
2736
- to = to ? clipPos(doc, to) : from;
2737
- return replaceRange(this, code, from, to);
2738
- }),
2739
-
2740
- getRange: function(from, to, lineSep) {
2741
- var doc = this.view.doc;
2742
- from = clipPos(doc, from); to = clipPos(doc, to);
2743
- var l1 = from.line, l2 = to.line;
2744
- if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch);
2745
- var code = [getLine(doc, l1).text.slice(from.ch)];
2746
- doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
2747
- code.push(getLine(doc, l2).text.slice(0, to.ch));
2748
- return code.join(lineSep || "\n");
2749
- },
2750
-
2751
- triggerOnKeyDown: operation(null, onKeyDown),
2752
-
2753
- execCommand: function(cmd) {return commands[cmd](this);},
2754
-
2755
- // Stuff used by commands, probably not much use to outside code.
2756
- moveH: operation(null, function(dir, unit) {
2757
- var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to;
2758
- if (sel.shift || sel.extend || posEq(sel.from, sel.to))
2759
- pos = findPosH(this, dir, unit, this.options.rtlMoveVisually);
2760
- extendSelection(this, pos, pos, dir);
2761
- }),
2762
-
2763
- deleteH: operation(null, function(dir, unit) {
2764
- var sel = this.view.sel;
2765
- if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to, "delete");
2766
- else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false), "delete");
2767
- this.curOp.userSelChange = true;
2768
- }),
2769
-
2770
- moveV: operation(null, function(dir, unit) {
2771
- var view = this.view, doc = view.doc, display = this.display;
2772
- var cur = view.sel.head, pos = cursorCoords(this, cur, "div");
2773
- var x = pos.left, y;
2774
- if (view.goalColumn != null) x = view.goalColumn;
2775
- if (unit == "page") {
2776
- var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
2777
- y = pos.top + dir * pageSize;
2778
- } else if (unit == "line") {
2779
- y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
2780
- }
2781
- do {
2782
- var target = coordsChar(this, x, y);
2783
- y += dir * 5;
2784
- } while (target.outside && (dir < 0 ? y > 0 : y < doc.height));
2785
-
2786
- if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top;
2787
- extendSelection(this, target, target, dir);
2788
- view.goalColumn = x;
2789
- }),
2790
-
2791
- toggleOverwrite: function() {
2792
- if (this.view.overwrite = !this.view.overwrite)
2793
- this.display.cursor.className += " CodeMirror-overwrite";
2794
- else
2795
- this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
2796
- },
2797
-
2798
- posFromIndex: function(off) {
2799
- var lineNo = 0, ch, doc = this.view.doc;
2800
- doc.iter(0, doc.size, function(line) {
2801
- var sz = line.text.length + 1;
2802
- if (sz > off) { ch = off; return true; }
2803
- off -= sz;
2804
- ++lineNo;
2805
- });
2806
- return clipPos(doc, {line: lineNo, ch: ch});
2807
- },
2808
- indexFromPos: function (coords) {
2809
- coords = clipPos(this.view.doc, coords);
2810
- var index = coords.ch;
2811
- this.view.doc.iter(0, coords.line, function (line) {
2812
- index += line.text.length + 1;
2813
- });
2814
- return index;
2815
- },
2816
-
2817
- scrollTo: function(x, y) {
2818
- if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x;
2819
- if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y;
2820
- updateDisplay(this, []);
2821
- },
2822
- getScrollInfo: function() {
2823
- var scroller = this.display.scroller, co = scrollerCutOff;
2824
- return {left: scroller.scrollLeft, top: scroller.scrollTop,
2825
- height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
2826
- clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
2827
- },
2828
-
2829
- scrollIntoView: function(pos) {
2830
- if (typeof pos == "number") pos = {line: pos, ch: 0};
2831
- if (!pos || pos.line != null) {
2832
- pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head;
2833
- scrollPosIntoView(this, pos);
2834
- } else {
2835
- scrollIntoView(this, pos.left, pos.top, pos.right, pos.bottom);
2836
- }
2837
- },
2838
-
2839
- setSize: function(width, height) {
2840
- function interpret(val) {
2841
- return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
2842
- }
2843
- if (width != null) this.display.wrapper.style.width = interpret(width);
2844
- if (height != null) this.display.wrapper.style.height = interpret(height);
2845
- this.refresh();
2846
- },
2847
-
2848
- on: function(type, f) {on(this, type, f);},
2849
- off: function(type, f) {off(this, type, f);},
2850
-
2851
- operation: function(f){return operation(this, f)();},
2852
-
2853
- refresh: function() {
2854
- clearCaches(this);
2855
- var sTop = this.view.scrollTop, sLeft = this.view.scrollLeft;
2856
- if (this.display.scroller.scrollHeight > sTop)
2857
- this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = sTop;
2858
- if (this.display.scroller.scrollWidth > sLeft)
2859
- this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = sLeft;
2860
- updateDisplay(this, true);
2861
- },
2862
-
2863
- getInputField: function(){return this.display.input;},
2864
- getWrapperElement: function(){return this.display.wrapper;},
2865
- getScrollerElement: function(){return this.display.scroller;},
2866
- getGutterElement: function(){return this.display.gutters;}
2867
- };
2868
-
2869
- // OPTION DEFAULTS
2870
-
2871
- var optionHandlers = CodeMirror.optionHandlers = {};
2872
-
2873
- // The default configuration options.
2874
- var defaults = CodeMirror.defaults = {};
2875
-
2876
- function option(name, deflt, handle, notOnInit) {
2877
- CodeMirror.defaults[name] = deflt;
2878
- if (handle) optionHandlers[name] =
2879
- notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
2880
- }
2881
-
2882
- var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
2883
-
2884
- // These two are, on init, called from the constructor because they
2885
- // have to be initialized before the editor can start at all.
2886
- option("value", "", function(cm, val) {cm.setValue(val);}, true);
2887
- option("mode", null, loadMode, true);
2888
-
2889
- option("indentUnit", 2, loadMode, true);
2890
- option("indentWithTabs", false);
2891
- option("smartIndent", true);
2892
- option("tabSize", 4, function(cm) {
2893
- loadMode(cm);
2894
- clearCaches(cm);
2895
- updateDisplay(cm, true);
2896
- }, true);
2897
- option("electricChars", true);
2898
- option("rtlMoveVisually", !windows);
2899
-
2900
- option("theme", "default", function(cm) {
2901
- themeChanged(cm);
2902
- guttersChanged(cm);
2903
- }, true);
2904
- option("keyMap", "default", keyMapChanged);
2905
- option("extraKeys", null);
2906
-
2907
- option("onKeyEvent", null);
2908
- option("onDragEvent", null);
2909
-
2910
- option("lineWrapping", false, wrappingChanged, true);
2911
- option("gutters", [], function(cm) {
2912
- setGuttersForLineNumbers(cm.options);
2913
- guttersChanged(cm);
2914
- }, true);
2915
- option("fixedGutter", true, function(cm, val) {
2916
- cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
2917
- cm.refresh();
2918
- }, true);
2919
- option("lineNumbers", false, function(cm) {
2920
- setGuttersForLineNumbers(cm.options);
2921
- guttersChanged(cm);
2922
- }, true);
2923
- option("firstLineNumber", 1, guttersChanged, true);
2924
- option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
2925
- option("showCursorWhenSelecting", false, updateSelection, true);
2926
-
2927
- option("readOnly", false, function(cm, val) {
2928
- if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
2929
- else if (!val) resetInput(cm, true);
2930
- });
2931
- option("dragDrop", true);
2932
-
2933
- option("cursorBlinkRate", 530);
2934
- option("cursorHeight", 1);
2935
- option("workTime", 100);
2936
- option("workDelay", 100);
2937
- option("flattenSpans", true);
2938
- option("pollInterval", 100);
2939
- option("undoDepth", 40);
2940
- option("viewportMargin", 10, function(cm){cm.refresh();}, true);
2941
-
2942
- option("tabindex", null, function(cm, val) {
2943
- cm.display.input.tabIndex = val || "";
2944
- });
2945
- option("autofocus", null);
2946
-
2947
- // MODE DEFINITION AND QUERYING
2948
-
2949
- // Known modes, by name and by MIME
2950
- var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
2951
-
2952
- CodeMirror.defineMode = function(name, mode) {
2953
- if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
2954
- if (arguments.length > 2) {
2955
- mode.dependencies = [];
2956
- for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
2957
- }
2958
- modes[name] = mode;
2959
- };
2960
-
2961
- CodeMirror.defineMIME = function(mime, spec) {
2962
- mimeModes[mime] = spec;
2963
- };
2964
-
2965
- CodeMirror.resolveMode = function(spec) {
2966
- if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
2967
- spec = mimeModes[spec];
2968
- else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
2969
- return CodeMirror.resolveMode("application/xml");
2970
- if (typeof spec == "string") return {name: spec};
2971
- else return spec || {name: "null"};
2972
- };
2973
-
2974
- CodeMirror.getMode = function(options, spec) {
2975
- spec = CodeMirror.resolveMode(spec);
2976
- var mfactory = modes[spec.name];
2977
- if (!mfactory) return CodeMirror.getMode(options, "text/plain");
2978
- var modeObj = mfactory(options, spec);
2979
- if (modeExtensions.hasOwnProperty(spec.name)) {
2980
- var exts = modeExtensions[spec.name];
2981
- for (var prop in exts) {
2982
- if (!exts.hasOwnProperty(prop)) continue;
2983
- if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
2984
- modeObj[prop] = exts[prop];
2985
- }
2986
- }
2987
- modeObj.name = spec.name;
2988
- return modeObj;
2989
- };
2990
-
2991
- CodeMirror.defineMode("null", function() {
2992
- return {token: function(stream) {stream.skipToEnd();}};
2993
- });
2994
- CodeMirror.defineMIME("text/plain", "null");
2995
-
2996
- var modeExtensions = CodeMirror.modeExtensions = {};
2997
- CodeMirror.extendMode = function(mode, properties) {
2998
- var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2999
- for (var prop in properties) if (properties.hasOwnProperty(prop))
3000
- exts[prop] = properties[prop];
3001
- };
3002
-
3003
- // EXTENSIONS
3004
-
3005
- CodeMirror.defineExtension = function(name, func) {
3006
- CodeMirror.prototype[name] = func;
3007
- };
3008
-
3009
- CodeMirror.defineOption = option;
3010
-
3011
- var initHooks = [];
3012
- CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
3013
-
3014
- // MODE STATE HANDLING
3015
-
3016
- // Utility functions for working with state. Exported because modes
3017
- // sometimes need to do this.
3018
- function copyState(mode, state) {
3019
- if (state === true) return state;
3020
- if (mode.copyState) return mode.copyState(state);
3021
- var nstate = {};
3022
- for (var n in state) {
3023
- var val = state[n];
3024
- if (val instanceof Array) val = val.concat([]);
3025
- nstate[n] = val;
3026
- }
3027
- return nstate;
3028
- }
3029
- CodeMirror.copyState = copyState;
3030
-
3031
- function startState(mode, a1, a2) {
3032
- return mode.startState ? mode.startState(a1, a2) : true;
3033
- }
3034
- CodeMirror.startState = startState;
3035
-
3036
- CodeMirror.innerMode = function(mode, state) {
3037
- while (mode.innerMode) {
3038
- var info = mode.innerMode(state);
3039
- state = info.state;
3040
- mode = info.mode;
3041
- }
3042
- return info || {mode: mode, state: state};
3043
- };
3044
-
3045
- // STANDARD COMMANDS
3046
-
3047
- var commands = CodeMirror.commands = {
3048
- selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
3049
- killLine: function(cm) {
3050
- var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
3051
- if (!sel && cm.getLine(from.line).length == from.ch)
3052
- cm.replaceRange("", from, {line: from.line + 1, ch: 0}, "delete");
3053
- else cm.replaceRange("", from, sel ? to : {line: from.line}, "delete");
3054
- },
3055
- deleteLine: function(cm) {
3056
- var l = cm.getCursor().line;
3057
- cm.replaceRange("", {line: l, ch: 0}, {line: l}, "delete");
3058
- },
3059
- undo: function(cm) {cm.undo();},
3060
- redo: function(cm) {cm.redo();},
3061
- goDocStart: function(cm) {cm.extendSelection({line: 0, ch: 0});},
3062
- goDocEnd: function(cm) {cm.extendSelection({line: cm.lineCount() - 1});},
3063
- goLineStart: function(cm) {
3064
- cm.extendSelection(lineStart(cm, cm.getCursor().line));
3065
- },
3066
- goLineStartSmart: function(cm) {
3067
- var cur = cm.getCursor(), start = lineStart(cm, cur.line);
3068
- var line = cm.getLineHandle(start.line);
3069
- var order = getOrder(line);
3070
- if (!order || order[0].level == 0) {
3071
- var firstNonWS = Math.max(0, line.text.search(/\S/));
3072
- var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
3073
- cm.extendSelection({line: start.line, ch: inWS ? 0 : firstNonWS});
3074
- } else cm.extendSelection(start);
3075
- },
3076
- goLineEnd: function(cm) {
3077
- cm.extendSelection(lineEnd(cm, cm.getCursor().line));
3078
- },
3079
- goLineUp: function(cm) {cm.moveV(-1, "line");},
3080
- goLineDown: function(cm) {cm.moveV(1, "line");},
3081
- goPageUp: function(cm) {cm.moveV(-1, "page");},
3082
- goPageDown: function(cm) {cm.moveV(1, "page");},
3083
- goCharLeft: function(cm) {cm.moveH(-1, "char");},
3084
- goCharRight: function(cm) {cm.moveH(1, "char");},
3085
- goColumnLeft: function(cm) {cm.moveH(-1, "column");},
3086
- goColumnRight: function(cm) {cm.moveH(1, "column");},
3087
- goWordLeft: function(cm) {cm.moveH(-1, "word");},
3088
- goWordRight: function(cm) {cm.moveH(1, "word");},
3089
- delCharBefore: function(cm) {cm.deleteH(-1, "char");},
3090
- delCharAfter: function(cm) {cm.deleteH(1, "char");},
3091
- delWordBefore: function(cm) {cm.deleteH(-1, "word");},
3092
- delWordAfter: function(cm) {cm.deleteH(1, "word");},
3093
- indentAuto: function(cm) {cm.indentSelection("smart");},
3094
- indentMore: function(cm) {cm.indentSelection("add");},
3095
- indentLess: function(cm) {cm.indentSelection("subtract");},
3096
- insertTab: function(cm) {cm.replaceSelection("\t", "end", "input");},
3097
- defaultTab: function(cm) {
3098
- if (cm.somethingSelected()) cm.indentSelection("add");
3099
- else cm.replaceSelection("\t", "end", "input");
3100
- },
3101
- transposeChars: function(cm) {
3102
- var cur = cm.getCursor(), line = cm.getLine(cur.line);
3103
- if (cur.ch > 0 && cur.ch < line.length - 1)
3104
- cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
3105
- {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
3106
- },
3107
- newlineAndIndent: function(cm) {
3108
- operation(cm, function() {
3109
- cm.replaceSelection("\n", "end", "input");
3110
- cm.indentLine(cm.getCursor().line, null, true);
3111
- })();
3112
- },
3113
- toggleOverwrite: function(cm) {cm.toggleOverwrite();}
3114
- };
3115
-
3116
- // STANDARD KEYMAPS
3117
-
3118
- var keyMap = CodeMirror.keyMap = {};
3119
- keyMap.basic = {
3120
- "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
3121
- "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
3122
- "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
3123
- "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
3124
- };
3125
- // Note that the save and find-related commands aren't defined by
3126
- // default. Unknown commands are simply ignored.
3127
- keyMap.pcDefault = {
3128
- "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3129
- "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
3130
- "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3131
- "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3132
- "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3133
- "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
3134
- fallthrough: "basic"
3135
- };
3136
- keyMap.macDefault = {
3137
- "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3138
- "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
3139
- "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore",
3140
- "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find",
3141
- "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3142
- "Cmd-[": "indentLess", "Cmd-]": "indentMore",
3143
- fallthrough: ["basic", "emacsy"]
3144
- };
3145
- keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
3146
- keyMap.emacsy = {
3147
- "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
3148
- "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
3149
- "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
3150
- "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
3151
- };
3152
-
3153
- // KEYMAP DISPATCH
3154
-
3155
- function getKeyMap(val) {
3156
- if (typeof val == "string") return keyMap[val];
3157
- else return val;
3158
- }
3159
-
3160
- function lookupKey(name, maps, handle, stop) {
3161
- function lookup(map) {
3162
- map = getKeyMap(map);
3163
- var found = map[name];
3164
- if (found === false) {
3165
- if (stop) stop();
3166
- return true;
3167
- }
3168
- if (found != null && handle(found)) return true;
3169
- if (map.nofallthrough) {
3170
- if (stop) stop();
3171
- return true;
3172
- }
3173
- var fallthrough = map.fallthrough;
3174
- if (fallthrough == null) return false;
3175
- if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3176
- return lookup(fallthrough);
3177
- for (var i = 0, e = fallthrough.length; i < e; ++i) {
3178
- if (lookup(fallthrough[i])) return true;
3179
- }
3180
- return false;
3181
- }
3182
-
3183
- for (var i = 0; i < maps.length; ++i)
3184
- if (lookup(maps[i])) return true;
3185
- }
3186
- function isModifierKey(event) {
3187
- var name = keyNames[e_prop(event, "keyCode")];
3188
- return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3189
- }
3190
- CodeMirror.isModifierKey = isModifierKey;
3191
-
3192
- // FROMTEXTAREA
3193
-
3194
- CodeMirror.fromTextArea = function(textarea, options) {
3195
- if (!options) options = {};
3196
- options.value = textarea.value;
3197
- if (!options.tabindex && textarea.tabindex)
3198
- options.tabindex = textarea.tabindex;
3199
- // Set autofocus to true if this textarea is focused, or if it has
3200
- // autofocus and no other element is focused.
3201
- if (options.autofocus == null) {
3202
- var hasFocus = document.body;
3203
- // doc.activeElement occasionally throws on IE
3204
- try { hasFocus = document.activeElement; } catch(e) {}
3205
- options.autofocus = hasFocus == textarea ||
3206
- textarea.getAttribute("autofocus") != null && hasFocus == document.body;
3207
- }
3208
-
3209
- function save() {textarea.value = cm.getValue();}
3210
- if (textarea.form) {
3211
- // Deplorable hack to make the submit method do the right thing.
3212
- on(textarea.form, "submit", save);
3213
- var form = textarea.form, realSubmit = form.submit;
3214
- try {
3215
- form.submit = function wrappedSubmit() {
3216
- save();
3217
- form.submit = realSubmit;
3218
- form.submit();
3219
- form.submit = wrappedSubmit;
3220
- };
3221
- } catch(e) {}
3222
- }
3223
-
3224
- textarea.style.display = "none";
3225
- var cm = CodeMirror(function(node) {
3226
- textarea.parentNode.insertBefore(node, textarea.nextSibling);
3227
- }, options);
3228
- cm.save = save;
3229
- cm.getTextArea = function() { return textarea; };
3230
- cm.toTextArea = function() {
3231
- save();
3232
- textarea.parentNode.removeChild(cm.getWrapperElement());
3233
- textarea.style.display = "";
3234
- if (textarea.form) {
3235
- off(textarea.form, "submit", save);
3236
- if (typeof textarea.form.submit == "function")
3237
- textarea.form.submit = realSubmit;
3238
- }
3239
- };
3240
- return cm;
3241
- };
3242
-
3243
- // STRING STREAM
3244
-
3245
- // Fed to the mode parsers, provides helper functions to make
3246
- // parsers more succinct.
3247
-
3248
- // The character stream used by a mode's parser.
3249
- function StringStream(string, tabSize) {
3250
- this.pos = this.start = 0;
3251
- this.string = string;
3252
- this.tabSize = tabSize || 8;
3253
- }
3254
-
3255
- StringStream.prototype = {
3256
- eol: function() {return this.pos >= this.string.length;},
3257
- sol: function() {return this.pos == 0;},
3258
- peek: function() {return this.string.charAt(this.pos) || undefined;},
3259
- next: function() {
3260
- if (this.pos < this.string.length)
3261
- return this.string.charAt(this.pos++);
3262
- },
3263
- eat: function(match) {
3264
- var ch = this.string.charAt(this.pos);
3265
- if (typeof match == "string") var ok = ch == match;
3266
- else var ok = ch && (match.test ? match.test(ch) : match(ch));
3267
- if (ok) {++this.pos; return ch;}
3268
- },
3269
- eatWhile: function(match) {
3270
- var start = this.pos;
3271
- while (this.eat(match)){}
3272
- return this.pos > start;
3273
- },
3274
- eatSpace: function() {
3275
- var start = this.pos;
3276
- while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
3277
- return this.pos > start;
3278
- },
3279
- skipToEnd: function() {this.pos = this.string.length;},
3280
- skipTo: function(ch) {
3281
- var found = this.string.indexOf(ch, this.pos);
3282
- if (found > -1) {this.pos = found; return true;}
3283
- },
3284
- backUp: function(n) {this.pos -= n;},
3285
- column: function() {return countColumn(this.string, this.start, this.tabSize);},
3286
- indentation: function() {return countColumn(this.string, null, this.tabSize);},
3287
- match: function(pattern, consume, caseInsensitive) {
3288
- if (typeof pattern == "string") {
3289
- var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
3290
- if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
3291
- if (consume !== false) this.pos += pattern.length;
3292
- return true;
3293
- }
3294
- } else {
3295
- var match = this.string.slice(this.pos).match(pattern);
3296
- if (match && match.index > 0) return null;
3297
- if (match && consume !== false) this.pos += match[0].length;
3298
- return match;
3299
- }
3300
- },
3301
- current: function(){return this.string.slice(this.start, this.pos);}
3302
- };
3303
- CodeMirror.StringStream = StringStream;
3304
-
3305
- // TEXTMARKERS
3306
-
3307
- function TextMarker(cm, type) {
3308
- this.lines = [];
3309
- this.type = type;
3310
- this.cm = cm;
3311
- }
3312
- CodeMirror.TextMarker = TextMarker;
3313
-
3314
- TextMarker.prototype.clear = function() {
3315
- if (this.explicitlyCleared) return;
3316
- startOperation(this.cm);
3317
- var view = this.cm.view, min = null, max = null;
3318
- for (var i = 0; i < this.lines.length; ++i) {
3319
- var line = this.lines[i];
3320
- var span = getMarkedSpanFor(line.markedSpans, this);
3321
- if (span.to != null) max = lineNo(line);
3322
- line.markedSpans = removeMarkedSpan(line.markedSpans, span);
3323
- if (span.from != null)
3324
- min = lineNo(line);
3325
- else if (this.collapsed && !lineIsHidden(line))
3326
- updateLineHeight(line, textHeight(this.cm.display));
3327
- }
3328
- if (this.collapsed && !this.cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
3329
- var visual = visualLine(view.doc, this.lines[i]), len = lineLength(view.doc, visual);
3330
- if (len > view.maxLineLength) {
3331
- view.maxLine = visual;
3332
- view.maxLineLength = len;
3333
- view.maxLineChanged = true;
3334
- }
3335
- }
3336
-
3337
- if (min != null) regChange(this.cm, min, max + 1);
3338
- this.lines.length = 0;
3339
- this.explicitlyCleared = true;
3340
- if (this.collapsed && this.cm.view.cantEdit) {
3341
- this.cm.view.cantEdit = false;
3342
- reCheckSelection(this.cm);
3343
- }
3344
- endOperation(this.cm);
3345
- signalLater(this.cm, this, "clear");
3346
- };
3347
-
3348
- TextMarker.prototype.find = function() {
3349
- var from, to;
3350
- for (var i = 0; i < this.lines.length; ++i) {
3351
- var line = this.lines[i];
3352
- var span = getMarkedSpanFor(line.markedSpans, this);
3353
- if (span.from != null || span.to != null) {
3354
- var found = lineNo(line);
3355
- if (span.from != null) from = {line: found, ch: span.from};
3356
- if (span.to != null) to = {line: found, ch: span.to};
3357
- }
3358
- }
3359
- if (this.type == "bookmark") return from;
3360
- return from && {from: from, to: to};
3361
- };
3362
-
3363
- TextMarker.prototype.getOptions = function(copyWidget) {
3364
- var repl = this.replacedWith;
3365
- return {className: this.className,
3366
- inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
3367
- atomic: this.atomic,
3368
- collapsed: this.collapsed,
3369
- clearOnEnter: this.clearOnEnter,
3370
- replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
3371
- readOnly: this.readOnly,
3372
- startStyle: this.startStyle, endStyle: this.endStyle};
3373
- };
3374
-
3375
- function markText(cm, from, to, options, type) {
3376
- var doc = cm.view.doc;
3377
- var marker = new TextMarker(cm, type);
3378
- if (type == "range" && !posLess(from, to)) return marker;
3379
- if (options) for (var opt in options) if (options.hasOwnProperty(opt))
3380
- marker[opt] = options[opt];
3381
- if (marker.replacedWith) {
3382
- marker.collapsed = true;
3383
- marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
3384
- }
3385
- if (marker.collapsed) sawCollapsedSpans = true;
3386
-
3387
- var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd;
3388
- doc.iter(curLine, to.line + 1, function(line) {
3389
- if (marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.view.maxLine)
3390
- cm.curOp.updateMaxLine = true;
3391
- var span = {from: null, to: null, marker: marker};
3392
- size += line.text.length;
3393
- if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
3394
- if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
3395
- if (marker.collapsed) {
3396
- if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
3397
- if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
3398
- else updateLineHeight(line, 0);
3399
- }
3400
- addMarkedSpan(line, span);
3401
- ++curLine;
3402
- });
3403
- if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
3404
- if (lineIsHidden(line)) updateLineHeight(line, 0);
3405
- });
3406
-
3407
- if (marker.readOnly) {
3408
- sawReadOnlySpans = true;
3409
- if (cm.view.history.done.length || cm.view.history.undone.length)
3410
- cm.clearHistory();
3411
- }
3412
- if (marker.collapsed) {
3413
- if (collapsedAtStart != collapsedAtEnd)
3414
- throw new Error("Inserting collapsed marker overlapping an existing one");
3415
- marker.size = size;
3416
- marker.atomic = true;
3417
- }
3418
- if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
3419
- regChange(cm, from.line, to.line + 1);
3420
- if (marker.atomic) reCheckSelection(cm);
3421
- return marker;
3422
- }
3423
-
3424
- // TEXTMARKER SPANS
3425
-
3426
- function getMarkedSpanFor(spans, marker) {
3427
- if (spans) for (var i = 0; i < spans.length; ++i) {
3428
- var span = spans[i];
3429
- if (span.marker == marker) return span;
3430
- }
3431
- }
3432
- function removeMarkedSpan(spans, span) {
3433
- for (var r, i = 0; i < spans.length; ++i)
3434
- if (spans[i] != span) (r || (r = [])).push(spans[i]);
3435
- return r;
3436
- }
3437
- function addMarkedSpan(line, span) {
3438
- line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
3439
- span.marker.lines.push(line);
3440
- }
3441
-
3442
- function markedSpansBefore(old, startCh) {
3443
- if (old) for (var i = 0, nw; i < old.length; ++i) {
3444
- var span = old[i], marker = span.marker;
3445
- var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
3446
- if (startsBefore || marker.type == "bookmark" && span.from == startCh) {
3447
- var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
3448
- (nw || (nw = [])).push({from: span.from,
3449
- to: endsAfter ? null : span.to,
3450
- marker: marker});
3451
- }
3452
- }
3453
- return nw;
3454
- }
3455
-
3456
- function markedSpansAfter(old, startCh, endCh) {
3457
- if (old) for (var i = 0, nw; i < old.length; ++i) {
3458
- var span = old[i], marker = span.marker;
3459
- var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
3460
- if (endsAfter || marker.type == "bookmark" && span.from == endCh && span.from != startCh) {
3461
- var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
3462
- (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
3463
- to: span.to == null ? null : span.to - endCh,
3464
- marker: marker});
3465
- }
3466
- }
3467
- return nw;
3468
- }
3469
-
3470
- function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
3471
- if (!oldFirst && !oldLast) return newText;
3472
- // Get the spans that 'stick out' on both sides
3473
- var first = markedSpansBefore(oldFirst, startCh);
3474
- var last = markedSpansAfter(oldLast, startCh, endCh);
3475
-
3476
- // Next, merge those two ends
3477
- var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
3478
- if (first) {
3479
- // Fix up .to properties of first
3480
- for (var i = 0; i < first.length; ++i) {
3481
- var span = first[i];
3482
- if (span.to == null) {
3483
- var found = getMarkedSpanFor(last, span.marker);
3484
- if (!found) span.to = startCh;
3485
- else if (sameLine) span.to = found.to == null ? null : found.to + offset;
3486
- }
3487
- }
3488
- }
3489
- if (last) {
3490
- // Fix up .from in last (or move them into first in case of sameLine)
3491
- for (var i = 0; i < last.length; ++i) {
3492
- var span = last[i];
3493
- if (span.to != null) span.to += offset;
3494
- if (span.from == null) {
3495
- var found = getMarkedSpanFor(first, span.marker);
3496
- if (!found) {
3497
- span.from = offset;
3498
- if (sameLine) (first || (first = [])).push(span);
3499
- }
3500
- } else {
3501
- span.from += offset;
3502
- if (sameLine) (first || (first = [])).push(span);
3503
- }
3504
- }
3505
- }
3506
-
3507
- var newMarkers = [newHL(newText[0], first)];
3508
- if (!sameLine) {
3509
- // Fill gap with whole-line-spans
3510
- var gap = newText.length - 2, gapMarkers;
3511
- if (gap > 0 && first)
3512
- for (var i = 0; i < first.length; ++i)
3513
- if (first[i].to == null)
3514
- (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
3515
- for (var i = 0; i < gap; ++i)
3516
- newMarkers.push(newHL(newText[i+1], gapMarkers));
3517
- newMarkers.push(newHL(lst(newText), last));
3518
- }
3519
- return newMarkers;
3520
- }
3521
-
3522
- function removeReadOnlyRanges(doc, from, to) {
3523
- var markers = null;
3524
- doc.iter(from.line, to.line + 1, function(line) {
3525
- if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
3526
- var mark = line.markedSpans[i].marker;
3527
- if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
3528
- (markers || (markers = [])).push(mark);
3529
- }
3530
- });
3531
- if (!markers) return null;
3532
- var parts = [{from: from, to: to}];
3533
- for (var i = 0; i < markers.length; ++i) {
3534
- var m = markers[i].find();
3535
- for (var j = 0; j < parts.length; ++j) {
3536
- var p = parts[j];
3537
- if (!posLess(m.from, p.to) || posLess(m.to, p.from)) continue;
3538
- var newParts = [j, 1];
3539
- if (posLess(p.from, m.from)) newParts.push({from: p.from, to: m.from});
3540
- if (posLess(m.to, p.to)) newParts.push({from: m.to, to: p.to});
3541
- parts.splice.apply(parts, newParts);
3542
- j += newParts.length - 1;
3543
- }
3544
- }
3545
- return parts;
3546
- }
3547
-
3548
- function collapsedSpanAt(line, ch) {
3549
- var sps = sawCollapsedSpans && line.markedSpans, found;
3550
- if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3551
- sp = sps[i];
3552
- if (!sp.marker.collapsed) continue;
3553
- if ((sp.from == null || sp.from < ch) &&
3554
- (sp.to == null || sp.to > ch) &&
3555
- (!found || found.width < sp.marker.width))
3556
- found = sp.marker;
3557
- }
3558
- return found;
3559
- }
3560
- function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
3561
- function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
3562
-
3563
- function visualLine(doc, line) {
3564
- var merged;
3565
- while (merged = collapsedSpanAtStart(line))
3566
- line = getLine(doc, merged.find().from.line);
3567
- return line;
3568
- }
3569
-
3570
- function lineIsHidden(line) {
3571
- var sps = sawCollapsedSpans && line.markedSpans;
3572
- if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3573
- sp = sps[i];
3574
- if (!sp.marker.collapsed) continue;
3575
- if (sp.from == null) return true;
3576
- if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(line, sp))
3577
- return true;
3578
- }
3579
- }
3580
- function lineIsHiddenInner(line, span) {
3581
- if (span.to == null) {
3582
- var end = span.marker.find().to, endLine = getLine(lineDoc(line), end.line);
3583
- return lineIsHiddenInner(endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
3584
- }
3585
- if (span.marker.inclusiveRight && span.to == line.text.length)
3586
- return true;
3587
- for (var sp, i = 0; i < line.markedSpans.length; ++i) {
3588
- sp = line.markedSpans[i];
3589
- if (sp.marker.collapsed && sp.from == span.to &&
3590
- (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
3591
- lineIsHiddenInner(line, sp)) return true;
3592
- }
3593
- }
3594
-
3595
- // hl stands for history-line, a data structure that can be either a
3596
- // string (line without markers) or a {text, markedSpans} object.
3597
- function hlText(val) { return typeof val == "string" ? val : val.text; }
3598
- function hlSpans(val) {
3599
- if (typeof val == "string") return null;
3600
- var spans = val.markedSpans, out = null;
3601
- for (var i = 0; i < spans.length; ++i) {
3602
- if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
3603
- else if (out) out.push(spans[i]);
3604
- }
3605
- return !out ? spans : out.length ? out : null;
3606
- }
3607
- function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
3608
-
3609
- function detachMarkedSpans(line) {
3610
- var spans = line.markedSpans;
3611
- if (!spans) return;
3612
- for (var i = 0; i < spans.length; ++i) {
3613
- var lines = spans[i].marker.lines;
3614
- var ix = indexOf(lines, line);
3615
- lines.splice(ix, 1);
3616
- }
3617
- line.markedSpans = null;
3618
- }
3619
-
3620
- function attachMarkedSpans(line, spans) {
3621
- if (!spans) return;
3622
- for (var i = 0; i < spans.length; ++i)
3623
- spans[i].marker.lines.push(line);
3624
- line.markedSpans = spans;
3625
- }
3626
-
3627
- // LINE WIDGETS
3628
-
3629
- var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
3630
- for (var opt in options) if (options.hasOwnProperty(opt))
3631
- this[opt] = options[opt];
3632
- this.cm = cm;
3633
- this.node = node;
3634
- };
3635
- function widgetOperation(f) {
3636
- return function() {
3637
- startOperation(this.cm);
3638
- try {var result = f.apply(this, arguments);}
3639
- finally {endOperation(this.cm);}
3640
- return result;
3641
- };
3642
- }
3643
- LineWidget.prototype.clear = widgetOperation(function() {
3644
- var ws = this.line.widgets, no = lineNo(this.line);
3645
- if (no == null || !ws) return;
3646
- for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
3647
- updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
3648
- regChange(this.cm, no, no + 1);
3649
- });
3650
- LineWidget.prototype.changed = widgetOperation(function() {
3651
- var oldH = this.height;
3652
- this.height = null;
3653
- var diff = widgetHeight(this) - oldH;
3654
- if (!diff) return;
3655
- updateLineHeight(this.line, this.line.height + diff);
3656
- var no = lineNo(this.line);
3657
- regChange(this.cm, no, no + 1);
3658
- });
3659
-
3660
- function widgetHeight(widget) {
3661
- if (widget.height != null) return widget.height;
3662
- if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
3663
- removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
3664
- return widget.height = widget.node.offsetHeight;
3665
- }
3666
-
3667
- function addLineWidget(cm, handle, node, options) {
3668
- var widget = new LineWidget(cm, node, options);
3669
- if (widget.noHScroll) cm.display.alignWidgets = true;
3670
- changeLine(cm, handle, function(line) {
3671
- (line.widgets || (line.widgets = [])).push(widget);
3672
- widget.line = line;
3673
- if (!lineIsHidden(line) || widget.showIfHidden) {
3674
- var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
3675
- updateLineHeight(line, line.height + widgetHeight(widget));
3676
- if (aboveVisible)
3677
- setTimeout(function() {cm.display.scroller.scrollTop += widget.height;});
3678
- }
3679
- return true;
3680
- });
3681
- return widget;
3682
- }
3683
-
3684
- // LINE DATA STRUCTURE
3685
-
3686
- // Line objects. These hold state related to a line, including
3687
- // highlighting info (the styles array).
3688
- function makeLine(text, markedSpans, height) {
3689
- var line = {text: text, height: height};
3690
- attachMarkedSpans(line, markedSpans);
3691
- if (lineIsHidden(line)) line.height = 0;
3692
- return line;
3693
- }
3694
-
3695
- function updateLine(cm, line, text, markedSpans) {
3696
- line.text = text;
3697
- if (line.stateAfter) line.stateAfter = null;
3698
- if (line.styles) line.styles = null;
3699
- if (line.order != null) line.order = null;
3700
- detachMarkedSpans(line);
3701
- attachMarkedSpans(line, markedSpans);
3702
- if (lineIsHidden(line)) line.height = 0;
3703
- else if (!line.height) line.height = textHeight(cm.display);
3704
- signalLater(cm, line, "change");
3705
- }
3706
-
3707
- function cleanUpLine(line) {
3708
- line.parent = null;
3709
- detachMarkedSpans(line);
3710
- }
3711
-
3712
- // Run the given mode's parser over a line, update the styles
3713
- // array, which contains alternating fragments of text and CSS
3714
- // classes.
3715
- function runMode(cm, text, mode, state, f) {
3716
- var flattenSpans = cm.options.flattenSpans;
3717
- var curText = "", curStyle = null;
3718
- var stream = new StringStream(text, cm.options.tabSize);
3719
- if (text == "" && mode.blankLine) mode.blankLine(state);
3720
- while (!stream.eol()) {
3721
- var style = mode.token(stream, state);
3722
- if (stream.pos > 5000) {
3723
- flattenSpans = false;
3724
- // Webkit seems to refuse to render text nodes longer than 57444 characters
3725
- stream.pos = Math.min(text.length, stream.start + 50000);
3726
- style = null;
3727
- }
3728
- var substr = stream.current();
3729
- stream.start = stream.pos;
3730
- if (!flattenSpans || curStyle != style) {
3731
- if (curText) f(curText, curStyle);
3732
- curText = substr; curStyle = style;
3733
- } else curText = curText + substr;
3734
- }
3735
- if (curText) f(curText, curStyle);
3736
- }
3737
-
3738
- function highlightLine(cm, line, state) {
3739
- // A styles array always starts with a number identifying the
3740
- // mode/overlays that it is based on (for easy invalidation).
3741
- var st = [cm.view.modeGen];
3742
- // Compute the base array of styles
3743
- runMode(cm, line.text, cm.view.mode, state, function(txt, style) {st.push(txt, style);});
3744
-
3745
- // Run overlays, adjust style array.
3746
- for (var o = 0; o < cm.view.overlays.length; ++o) {
3747
- var overlay = cm.view.overlays[o], i = 1;
3748
- runMode(cm, line.text, overlay.mode, true, function(txt, style) {
3749
- var start = i, len = txt.length;
3750
- // Ensure there's a token end at the current position, and that i points at it
3751
- while (len) {
3752
- var cur = st[i], len_ = cur.length;
3753
- if (len_ <= len) {
3754
- len -= len_;
3755
- } else {
3756
- st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
3757
- len = 0;
3758
- }
3759
- i += 2;
3760
- }
3761
- if (!style) return;
3762
- if (overlay.opaque) {
3763
- st.splice(start, i - start, txt, style);
3764
- i = start + 2;
3765
- } else {
3766
- for (; start < i; start += 2) {
3767
- var cur = st[start+1];
3768
- st[start+1] = cur ? cur + " " + style : style;
3769
- }
3770
- }
3771
- });
3772
- }
3773
-
3774
- return st;
3775
- }
3776
-
3777
- function getLineStyles(cm, line) {
3778
- if (!line.styles || line.styles[0] != cm.view.modeGen)
3779
- line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
3780
- return line.styles;
3781
- }
3782
-
3783
- // Lightweight form of highlight -- proceed over this line and
3784
- // update state, but don't save a style array.
3785
- function processLine(cm, line, state) {
3786
- var mode = cm.view.mode;
3787
- var stream = new StringStream(line.text, cm.options.tabSize);
3788
- if (line.text == "" && mode.blankLine) mode.blankLine(state);
3789
- while (!stream.eol() && stream.pos <= 5000) {
3790
- mode.token(stream, state);
3791
- stream.start = stream.pos;
3792
- }
3793
- }
3794
-
3795
- var styleToClassCache = {};
3796
- function styleToClass(style) {
3797
- if (!style) return null;
3798
- return styleToClassCache[style] ||
3799
- (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
3800
- }
3801
-
3802
- function lineContent(cm, realLine, measure) {
3803
- var merged, line = realLine, lineBefore, sawBefore, simple = true;
3804
- while (merged = collapsedSpanAtStart(line)) {
3805
- simple = false;
3806
- line = getLine(cm.view.doc, merged.find().from.line);
3807
- if (!lineBefore) lineBefore = line;
3808
- }
3809
-
3810
- var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
3811
- measure: null, addedOne: false, cm: cm};
3812
- if (line.textClass) builder.pre.className = line.textClass;
3813
-
3814
- do {
3815
- builder.measure = line == realLine && measure;
3816
- builder.pos = 0;
3817
- builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
3818
- if (measure && sawBefore && line != realLine && !builder.addedOne) {
3819
- measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
3820
- builder.addedOne = true;
3821
- }
3822
- var next = insertLineContent(line, builder, getLineStyles(cm, line));
3823
- sawBefore = line == lineBefore;
3824
- if (next) {
3825
- line = getLine(cm.view.doc, next.to.line);
3826
- simple = false;
3827
- }
3828
- } while (next);
3829
-
3830
- if (measure && !builder.addedOne)
3831
- measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
3832
- if (!builder.pre.firstChild && !lineIsHidden(realLine))
3833
- builder.pre.appendChild(document.createTextNode("\u00a0"));
3834
-
3835
- return builder.pre;
3836
- }
3837
-
3838
- var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
3839
- function buildToken(builder, text, style, startStyle, endStyle) {
3840
- if (!text) return;
3841
- if (!tokenSpecialChars.test(text)) {
3842
- builder.col += text.length;
3843
- var content = document.createTextNode(text);
3844
- } else {
3845
- var content = document.createDocumentFragment(), pos = 0;
3846
- while (true) {
3847
- tokenSpecialChars.lastIndex = pos;
3848
- var m = tokenSpecialChars.exec(text);
3849
- var skipped = m ? m.index - pos : text.length - pos;
3850
- if (skipped) {
3851
- content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
3852
- builder.col += skipped;
3853
- }
3854
- if (!m) break;
3855
- pos += skipped + 1;
3856
- if (m[0] == "\t") {
3857
- var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
3858
- content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
3859
- builder.col += tabWidth;
3860
- } else {
3861
- var token = elt("span", "\u2022", "cm-invalidchar");
3862
- token.title = "\\u" + m[0].charCodeAt(0).toString(16);
3863
- content.appendChild(token);
3864
- builder.col += 1;
3865
- }
3866
- }
3867
- }
3868
- if (style || startStyle || endStyle || builder.measure) {
3869
- var fullStyle = style || "";
3870
- if (startStyle) fullStyle += startStyle;
3871
- if (endStyle) fullStyle += endStyle;
3872
- return builder.pre.appendChild(elt("span", [content], fullStyle));
3873
- }
3874
- builder.pre.appendChild(content);
3875
- }
3876
-
3877
- function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
3878
- for (var i = 0; i < text.length; ++i) {
3879
- if (i && i < text.length &&
3880
- builder.cm.options.lineWrapping &&
3881
- spanAffectsWrapping.test(text.slice(i - 1, i + 1)))
3882
- builder.pre.appendChild(elt("wbr"));
3883
- builder.measure[builder.pos++] =
3884
- buildToken(builder, text.charAt(i), style,
3885
- i == 0 && startStyle, i == text.length - 1 && endStyle);
3886
- }
3887
- if (text.length) builder.addedOne = true;
3888
- }
3889
-
3890
- function buildCollapsedSpan(builder, size, widget) {
3891
- if (widget) {
3892
- if (!builder.display) widget = widget.cloneNode(true);
3893
- builder.pre.appendChild(widget);
3894
- if (builder.measure && size) {
3895
- builder.measure[builder.pos] = widget;
3896
- builder.addedOne = true;
3897
- }
3898
- }
3899
- builder.pos += size;
3900
- }
3901
-
3902
- // Outputs a number of spans to make up a line, taking highlighting
3903
- // and marked text into account.
3904
- function insertLineContent(line, builder, styles) {
3905
- var spans = line.markedSpans;
3906
- if (!spans) {
3907
- for (var i = 1; i < styles.length; i+=2)
3908
- builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
3909
- return;
3910
- }
3911
-
3912
- var allText = line.text, len = allText.length;
3913
- var pos = 0, i = 1, text = "", style;
3914
- var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
3915
- for (;;) {
3916
- if (nextChange == pos) { // Update current marker set
3917
- spanStyle = spanEndStyle = spanStartStyle = "";
3918
- collapsed = null; nextChange = Infinity;
3919
- var foundBookmark = null;
3920
- for (var j = 0; j < spans.length; ++j) {
3921
- var sp = spans[j], m = sp.marker;
3922
- if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
3923
- if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
3924
- if (m.className) spanStyle += " " + m.className;
3925
- if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
3926
- if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
3927
- if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
3928
- collapsed = sp;
3929
- } else if (sp.from > pos && nextChange > sp.from) {
3930
- nextChange = sp.from;
3931
- }
3932
- if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
3933
- foundBookmark = m.replacedWith;
3934
- }
3935
- if (collapsed && (collapsed.from || 0) == pos) {
3936
- buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
3937
- collapsed.from != null && collapsed.marker.replacedWith);
3938
- if (collapsed.to == null) return collapsed.marker.find();
3939
- }
3940
- if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
3941
- }
3942
- if (pos >= len) break;
3943
-
3944
- var upto = Math.min(len, nextChange);
3945
- while (true) {
3946
- if (text) {
3947
- var end = pos + text.length;
3948
- if (!collapsed) {
3949
- var tokenText = end > upto ? text.slice(0, upto - pos) : text;
3950
- builder.addToken(builder, tokenText, style + spanStyle,
3951
- spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
3952
- }
3953
- if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
3954
- pos = end;
3955
- spanStartStyle = "";
3956
- }
3957
- text = styles[i++]; style = styleToClass(styles[i++]);
3958
- }
3959
- }
3960
- }
3961
-
3962
- // DOCUMENT DATA STRUCTURE
3963
-
3964
- function LeafChunk(lines) {
3965
- this.lines = lines;
3966
- this.parent = null;
3967
- for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
3968
- lines[i].parent = this;
3969
- height += lines[i].height;
3970
- }
3971
- this.height = height;
3972
- }
3973
-
3974
- LeafChunk.prototype = {
3975
- chunkSize: function() { return this.lines.length; },
3976
- remove: function(at, n, cm) {
3977
- for (var i = at, e = at + n; i < e; ++i) {
3978
- var line = this.lines[i];
3979
- this.height -= line.height;
3980
- cleanUpLine(line);
3981
- signalLater(cm, line, "delete");
3982
- }
3983
- this.lines.splice(at, n);
3984
- },
3985
- collapse: function(lines) {
3986
- lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
3987
- },
3988
- insertHeight: function(at, lines, height) {
3989
- this.height += height;
3990
- this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
3991
- for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
3992
- },
3993
- iterN: function(at, n, op) {
3994
- for (var e = at + n; at < e; ++at)
3995
- if (op(this.lines[at])) return true;
3996
- }
3997
- };
3998
-
3999
- function BranchChunk(children) {
4000
- this.children = children;
4001
- var size = 0, height = 0;
4002
- for (var i = 0, e = children.length; i < e; ++i) {
4003
- var ch = children[i];
4004
- size += ch.chunkSize(); height += ch.height;
4005
- ch.parent = this;
4006
- }
4007
- this.size = size;
4008
- this.height = height;
4009
- this.parent = null;
4010
- }
4011
-
4012
- BranchChunk.prototype = {
4013
- chunkSize: function() { return this.size; },
4014
- remove: function(at, n, callbacks) {
4015
- this.size -= n;
4016
- for (var i = 0; i < this.children.length; ++i) {
4017
- var child = this.children[i], sz = child.chunkSize();
4018
- if (at < sz) {
4019
- var rm = Math.min(n, sz - at), oldHeight = child.height;
4020
- child.remove(at, rm, callbacks);
4021
- this.height -= oldHeight - child.height;
4022
- if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
4023
- if ((n -= rm) == 0) break;
4024
- at = 0;
4025
- } else at -= sz;
4026
- }
4027
- if (this.size - n < 25) {
4028
- var lines = [];
4029
- this.collapse(lines);
4030
- this.children = [new LeafChunk(lines)];
4031
- this.children[0].parent = this;
4032
- }
4033
- },
4034
- collapse: function(lines) {
4035
- for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
4036
- },
4037
- insert: function(at, lines) {
4038
- var height = 0;
4039
- for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
4040
- this.insertHeight(at, lines, height);
4041
- },
4042
- insertHeight: function(at, lines, height) {
4043
- this.size += lines.length;
4044
- this.height += height;
4045
- for (var i = 0, e = this.children.length; i < e; ++i) {
4046
- var child = this.children[i], sz = child.chunkSize();
4047
- if (at <= sz) {
4048
- child.insertHeight(at, lines, height);
4049
- if (child.lines && child.lines.length > 50) {
4050
- while (child.lines.length > 50) {
4051
- var spilled = child.lines.splice(child.lines.length - 25, 25);
4052
- var newleaf = new LeafChunk(spilled);
4053
- child.height -= newleaf.height;
4054
- this.children.splice(i + 1, 0, newleaf);
4055
- newleaf.parent = this;
4056
- }
4057
- this.maybeSpill();
4058
- }
4059
- break;
4060
- }
4061
- at -= sz;
4062
- }
4063
- },
4064
- maybeSpill: function() {
4065
- if (this.children.length <= 10) return;
4066
- var me = this;
4067
- do {
4068
- var spilled = me.children.splice(me.children.length - 5, 5);
4069
- var sibling = new BranchChunk(spilled);
4070
- if (!me.parent) { // Become the parent node
4071
- var copy = new BranchChunk(me.children);
4072
- copy.parent = me;
4073
- me.children = [copy, sibling];
4074
- me = copy;
4075
- } else {
4076
- me.size -= sibling.size;
4077
- me.height -= sibling.height;
4078
- var myIndex = indexOf(me.parent.children, me);
4079
- me.parent.children.splice(myIndex + 1, 0, sibling);
4080
- }
4081
- sibling.parent = me.parent;
4082
- } while (me.children.length > 10);
4083
- me.parent.maybeSpill();
4084
- },
4085
- iter: function(from, to, op) { this.iterN(from, to - from, op); },
4086
- iterN: function(at, n, op) {
4087
- for (var i = 0, e = this.children.length; i < e; ++i) {
4088
- var child = this.children[i], sz = child.chunkSize();
4089
- if (at < sz) {
4090
- var used = Math.min(n, sz - at);
4091
- if (child.iterN(at, used, op)) return true;
4092
- if ((n -= used) == 0) break;
4093
- at = 0;
4094
- } else at -= sz;
4095
- }
4096
- }
4097
- };
4098
-
4099
- // LINE UTILITIES
4100
-
4101
- function getLine(chunk, n) {
4102
- while (!chunk.lines) {
4103
- for (var i = 0;; ++i) {
4104
- var child = chunk.children[i], sz = child.chunkSize();
4105
- if (n < sz) { chunk = child; break; }
4106
- n -= sz;
4107
- }
4108
- }
4109
- return chunk.lines[n];
4110
- }
4111
-
4112
- function updateLineHeight(line, height) {
4113
- var diff = height - line.height;
4114
- for (var n = line; n; n = n.parent) n.height += diff;
4115
- }
4116
-
4117
- function lineNo(line) {
4118
- if (line.parent == null) return null;
4119
- var cur = line.parent, no = indexOf(cur.lines, line);
4120
- for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
4121
- for (var i = 0;; ++i) {
4122
- if (chunk.children[i] == cur) break;
4123
- no += chunk.children[i].chunkSize();
4124
- }
4125
- }
4126
- return no;
4127
- }
4128
-
4129
- function lineDoc(line) {
4130
- for (var d = line.parent; d.parent; d = d.parent) {}
4131
- return d;
4132
- }
4133
-
4134
- function lineAtHeight(chunk, h) {
4135
- var n = 0;
4136
- outer: do {
4137
- for (var i = 0, e = chunk.children.length; i < e; ++i) {
4138
- var child = chunk.children[i], ch = child.height;
4139
- if (h < ch) { chunk = child; continue outer; }
4140
- h -= ch;
4141
- n += child.chunkSize();
4142
- }
4143
- return n;
4144
- } while (!chunk.lines);
4145
- for (var i = 0, e = chunk.lines.length; i < e; ++i) {
4146
- var line = chunk.lines[i], lh = line.height;
4147
- if (h < lh) break;
4148
- h -= lh;
4149
- }
4150
- return n + i;
4151
- }
4152
-
4153
- function heightAtLine(cm, lineObj) {
4154
- lineObj = visualLine(cm.view.doc, lineObj);
4155
-
4156
- var h = 0, chunk = lineObj.parent;
4157
- for (var i = 0; i < chunk.lines.length; ++i) {
4158
- var line = chunk.lines[i];
4159
- if (line == lineObj) break;
4160
- else h += line.height;
4161
- }
4162
- for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
4163
- for (var i = 0; i < p.children.length; ++i) {
4164
- var cur = p.children[i];
4165
- if (cur == chunk) break;
4166
- else h += cur.height;
4167
- }
4168
- }
4169
- return h;
4170
- }
4171
-
4172
- function getOrder(line) {
4173
- var order = line.order;
4174
- if (order == null) order = line.order = bidiOrdering(line.text);
4175
- return order;
4176
- }
4177
-
4178
- // HISTORY
4179
-
4180
- function makeHistory() {
4181
- return {
4182
- // Arrays of history events. Doing something adds an event to
4183
- // done and clears undo. Undoing moves events from done to
4184
- // undone, redoing moves them in the other direction.
4185
- done: [], undone: [],
4186
- // Used to track when changes can be merged into a single undo
4187
- // event
4188
- lastTime: 0, lastOp: null, lastOrigin: null,
4189
- // Used by the isClean() method
4190
- dirtyCounter: 0
4191
- };
4192
- }
4193
-
4194
- function addChange(cm, start, added, old, origin, fromBefore, toBefore, fromAfter, toAfter) {
4195
- var history = cm.view.history;
4196
- history.undone.length = 0;
4197
- var time = +new Date, cur = lst(history.done);
4198
-
4199
- if (cur &&
4200
- (history.lastOp == cm.curOp.id ||
4201
- history.lastOrigin == origin && (origin == "input" || origin == "delete") &&
4202
- history.lastTime > time - 600)) {
4203
- // Merge this change into the last event
4204
- var last = lst(cur.events);
4205
- if (last.start > start + old.length || last.start + last.added < start) {
4206
- // Doesn't intersect with last sub-event, add new sub-event
4207
- cur.events.push({start: start, added: added, old: old});
4208
- } else {
4209
- // Patch up the last sub-event
4210
- var startBefore = Math.max(0, last.start - start),
4211
- endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
4212
- for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
4213
- for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
4214
- if (startBefore) last.start = start;
4215
- last.added += added - (old.length - startBefore - endAfter);
4216
- }
4217
- cur.fromAfter = fromAfter; cur.toAfter = toAfter;
4218
- } else {
4219
- // Can not be merged, start a new event.
4220
- cur = {events: [{start: start, added: added, old: old}],
4221
- fromBefore: fromBefore, toBefore: toBefore, fromAfter: fromAfter, toAfter: toAfter};
4222
- history.done.push(cur);
4223
- while (history.done.length > cm.options.undoDepth)
4224
- history.done.shift();
4225
- if (history.dirtyCounter < 0)
4226
- // The user has made a change after undoing past the last clean state.
4227
- // We can never get back to a clean state now until markClean() is called.
4228
- history.dirtyCounter = NaN;
4229
- else
4230
- history.dirtyCounter++;
4231
- }
4232
- history.lastTime = time;
4233
- history.lastOp = cm.curOp.id;
4234
- history.lastOrigin = origin;
4235
- }
4236
-
4237
- // EVENT OPERATORS
4238
-
4239
- function stopMethod() {e_stop(this);}
4240
- // Ensure an event has a stop method.
4241
- function addStop(event) {
4242
- if (!event.stop) event.stop = stopMethod;
4243
- return event;
4244
- }
4245
-
4246
- function e_preventDefault(e) {
4247
- if (e.preventDefault) e.preventDefault();
4248
- else e.returnValue = false;
4249
- }
4250
- function e_stopPropagation(e) {
4251
- if (e.stopPropagation) e.stopPropagation();
4252
- else e.cancelBubble = true;
4253
- }
4254
- function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
4255
- CodeMirror.e_stop = e_stop;
4256
- CodeMirror.e_preventDefault = e_preventDefault;
4257
- CodeMirror.e_stopPropagation = e_stopPropagation;
4258
-
4259
- function e_target(e) {return e.target || e.srcElement;}
4260
- function e_button(e) {
4261
- var b = e.which;
4262
- if (b == null) {
4263
- if (e.button & 1) b = 1;
4264
- else if (e.button & 2) b = 3;
4265
- else if (e.button & 4) b = 2;
4266
- }
4267
- if (mac && e.ctrlKey && b == 1) b = 3;
4268
- return b;
4269
- }
4270
-
4271
- // Allow 3rd-party code to override event properties by adding an override
4272
- // object to an event object.
4273
- function e_prop(e, prop) {
4274
- var overridden = e.override && e.override.hasOwnProperty(prop);
4275
- return overridden ? e.override[prop] : e[prop];
4276
- }
4277
-
4278
- // EVENT HANDLING
4279
-
4280
- function on(emitter, type, f) {
4281
- if (emitter.addEventListener)
4282
- emitter.addEventListener(type, f, false);
4283
- else if (emitter.attachEvent)
4284
- emitter.attachEvent("on" + type, f);
4285
- else {
4286
- var map = emitter._handlers || (emitter._handlers = {});
4287
- var arr = map[type] || (map[type] = []);
4288
- arr.push(f);
4289
- }
4290
- }
4291
-
4292
- function off(emitter, type, f) {
4293
- if (emitter.removeEventListener)
4294
- emitter.removeEventListener(type, f, false);
4295
- else if (emitter.detachEvent)
4296
- emitter.detachEvent("on" + type, f);
4297
- else {
4298
- var arr = emitter._handlers && emitter._handlers[type];
4299
- if (!arr) return;
4300
- for (var i = 0; i < arr.length; ++i)
4301
- if (arr[i] == f) { arr.splice(i, 1); break; }
4302
- }
4303
- }
4304
-
4305
- function signal(emitter, type /*, values...*/) {
4306
- var arr = emitter._handlers && emitter._handlers[type];
4307
- if (!arr) return;
4308
- var args = Array.prototype.slice.call(arguments, 2);
4309
- for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
4310
- }
4311
-
4312
- function signalLater(cm, emitter, type /*, values...*/) {
4313
- var arr = emitter._handlers && emitter._handlers[type];
4314
- if (!arr) return;
4315
- var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks;
4316
- function bnd(f) {return function(){f.apply(null, args);};};
4317
- for (var i = 0; i < arr.length; ++i)
4318
- if (flist) flist.push(bnd(arr[i]));
4319
- else arr[i].apply(null, args);
4320
- }
4321
-
4322
- function hasHandler(emitter, type) {
4323
- var arr = emitter._handlers && emitter._handlers[type];
4324
- return arr && arr.length > 0;
4325
- }
4326
-
4327
- CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
4328
-
4329
- // MISC UTILITIES
4330
-
4331
- // Number of pixels added to scroller and sizer to hide scrollbar
4332
- var scrollerCutOff = 30;
4333
-
4334
- // Returned or thrown by various protocols to signal 'I'm not
4335
- // handling this'.
4336
- var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
4337
-
4338
- function Delayed() {this.id = null;}
4339
- Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
4340
-
4341
- // Counts the column offset in a string, taking tabs into account.
4342
- // Used mostly to find indentation.
4343
- function countColumn(string, end, tabSize) {
4344
- if (end == null) {
4345
- end = string.search(/[^\s\u00a0]/);
4346
- if (end == -1) end = string.length;
4347
- }
4348
- for (var i = 0, n = 0; i < end; ++i) {
4349
- if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
4350
- else ++n;
4351
- }
4352
- return n;
4353
- }
4354
- CodeMirror.countColumn = countColumn;
4355
-
4356
- var spaceStrs = [""];
4357
- function spaceStr(n) {
4358
- while (spaceStrs.length <= n)
4359
- spaceStrs.push(lst(spaceStrs) + " ");
4360
- return spaceStrs[n];
4361
- }
4362
-
4363
- function lst(arr) { return arr[arr.length-1]; }
4364
-
4365
- function selectInput(node) {
4366
- if (ios) { // Mobile Safari apparently has a bug where select() is broken.
4367
- node.selectionStart = 0;
4368
- node.selectionEnd = node.value.length;
4369
- } else node.select();
4370
- }
4371
-
4372
- function indexOf(collection, elt) {
4373
- if (collection.indexOf) return collection.indexOf(elt);
4374
- for (var i = 0, e = collection.length; i < e; ++i)
4375
- if (collection[i] == elt) return i;
4376
- return -1;
4377
- }
4378
-
4379
- function emptyArray(size) {
4380
- for (var a = [], i = 0; i < size; ++i) a.push(undefined);
4381
- return a;
4382
- }
4383
-
4384
- function bind(f) {
4385
- var args = Array.prototype.slice.call(arguments, 1);
4386
- return function(){return f.apply(null, args);};
4387
- }
4388
-
4389
- var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
4390
- function isWordChar(ch) {
4391
- return /\w/.test(ch) || ch > "\x80" &&
4392
- (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
4393
- }
4394
-
4395
- function isEmpty(obj) {
4396
- var c = 0;
4397
- for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c;
4398
- return !c;
4399
- }
4400
-
4401
- var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/;
4402
-
4403
- // DOM UTILITIES
4404
-
4405
- function elt(tag, content, className, style) {
4406
- var e = document.createElement(tag);
4407
- if (className) e.className = className;
4408
- if (style) e.style.cssText = style;
4409
- if (typeof content == "string") setTextContent(e, content);
4410
- else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
4411
- return e;
4412
- }
4413
-
4414
- function removeChildren(e) {
4415
- // IE will break all parent-child relations in subnodes when setting innerHTML
4416
- if (!ie) e.innerHTML = "";
4417
- else while (e.firstChild) e.removeChild(e.firstChild);
4418
- return e;
4419
- }
4420
-
4421
- function removeChildrenAndAdd(parent, e) {
4422
- return removeChildren(parent).appendChild(e);
4423
- }
4424
-
4425
- function setTextContent(e, str) {
4426
- if (ie_lt9) {
4427
- e.innerHTML = "";
4428
- e.appendChild(document.createTextNode(str));
4429
- } else e.textContent = str;
4430
- }
4431
-
4432
- // FEATURE DETECTION
4433
-
4434
- // Detect drag-and-drop
4435
- var dragAndDrop = function() {
4436
- // There is *some* kind of drag-and-drop support in IE6-8, but I
4437
- // couldn't get it to work yet.
4438
- if (ie_lt9) return false;
4439
- var div = elt('div');
4440
- return "draggable" in div || "dragDrop" in div;
4441
- }();
4442
-
4443
- // For a reason I have yet to figure out, some browsers disallow
4444
- // word wrapping between certain characters *only* if a new inline
4445
- // element is started between them. This makes it hard to reliably
4446
- // measure the position of things, since that requires inserting an
4447
- // extra span. This terribly fragile set of regexps matches the
4448
- // character combinations that suffer from this phenomenon on the
4449
- // various browsers.
4450
- var spanAffectsWrapping = /^$/; // Won't match any two-character string
4451
- if (gecko) spanAffectsWrapping = /$'/;
4452
- else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
4453
- else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
4454
-
4455
- var knownScrollbarWidth;
4456
- function scrollbarWidth(measure) {
4457
- if (knownScrollbarWidth != null) return knownScrollbarWidth;
4458
- var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
4459
- removeChildrenAndAdd(measure, test);
4460
- if (test.offsetWidth)
4461
- knownScrollbarWidth = test.offsetHeight - test.clientHeight;
4462
- return knownScrollbarWidth || 0;
4463
- }
4464
-
4465
- var zwspSupported;
4466
- function zeroWidthElement(measure) {
4467
- if (zwspSupported == null) {
4468
- var test = elt("span", "\u200b");
4469
- removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
4470
- if (measure.firstChild.offsetHeight != 0)
4471
- zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
4472
- }
4473
- if (zwspSupported) return elt("span", "\u200b");
4474
- else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
4475
- }
4476
-
4477
- // See if "".split is the broken IE version, if so, provide an
4478
- // alternative way to split lines.
4479
- var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
4480
- var pos = 0, result = [], l = string.length;
4481
- while (pos <= l) {
4482
- var nl = string.indexOf("\n", pos);
4483
- if (nl == -1) nl = string.length;
4484
- var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
4485
- var rt = line.indexOf("\r");
4486
- if (rt != -1) {
4487
- result.push(line.slice(0, rt));
4488
- pos += rt + 1;
4489
- } else {
4490
- result.push(line);
4491
- pos = nl + 1;
4492
- }
4493
- }
4494
- return result;
4495
- } : function(string){return string.split(/\r\n?|\n/);};
4496
- CodeMirror.splitLines = splitLines;
4497
-
4498
- var hasSelection = window.getSelection ? function(te) {
4499
- try { return te.selectionStart != te.selectionEnd; }
4500
- catch(e) { return false; }
4501
- } : function(te) {
4502
- try {var range = te.ownerDocument.selection.createRange();}
4503
- catch(e) {}
4504
- if (!range || range.parentElement() != te) return false;
4505
- return range.compareEndPoints("StartToEnd", range) != 0;
4506
- };
4507
-
4508
- var hasCopyEvent = (function() {
4509
- var e = elt("div");
4510
- if ("oncopy" in e) return true;
4511
- e.setAttribute("oncopy", "return;");
4512
- return typeof e.oncopy == 'function';
4513
- })();
4514
-
4515
- // KEY NAMING
4516
-
4517
- var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
4518
- 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
4519
- 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
4520
- 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
4521
- 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
4522
- 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
4523
- 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
4524
- CodeMirror.keyNames = keyNames;
4525
- (function() {
4526
- // Number keys
4527
- for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
4528
- // Alphabetic keys
4529
- for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
4530
- // Function keys
4531
- for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
4532
- })();
4533
-
4534
- // BIDI HELPERS
4535
-
4536
- function iterateBidiSections(order, from, to, f) {
4537
- if (!order) return f(from, to, "ltr");
4538
- for (var i = 0; i < order.length; ++i) {
4539
- var part = order[i];
4540
- if (part.from < to && part.to > from || from == to && part.to == from)
4541
- f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
4542
- }
4543
- }
4544
-
4545
- function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
4546
- function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
4547
-
4548
- function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
4549
- function lineRight(line) {
4550
- var order = getOrder(line);
4551
- if (!order) return line.text.length;
4552
- return bidiRight(lst(order));
4553
- }
4554
-
4555
- function lineStart(cm, lineN) {
4556
- var line = getLine(cm.view.doc, lineN);
4557
- var visual = visualLine(cm.view.doc, line);
4558
- if (visual != line) lineN = lineNo(visual);
4559
- var order = getOrder(visual);
4560
- var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
4561
- return {line: lineN, ch: ch};
4562
- }
4563
- function lineEnd(cm, lineNo) {
4564
- var merged, line;
4565
- while (merged = collapsedSpanAtEnd(line = getLine(cm.view.doc, lineNo)))
4566
- lineNo = merged.find().to.line;
4567
- var order = getOrder(line);
4568
- var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
4569
- return {line: lineNo, ch: ch};
4570
- }
4571
-
4572
- // This is somewhat involved. It is needed in order to move
4573
- // 'visually' through bi-directional text -- i.e., pressing left
4574
- // should make the cursor go left, even when in RTL text. The
4575
- // tricky part is the 'jumps', where RTL and LTR text touch each
4576
- // other. This often requires the cursor offset to move more than
4577
- // one unit, in order to visually move one unit.
4578
- function moveVisually(line, start, dir, byUnit) {
4579
- var bidi = getOrder(line);
4580
- if (!bidi) return moveLogically(line, start, dir, byUnit);
4581
- var moveOneUnit = byUnit ? function(pos, dir) {
4582
- do pos += dir;
4583
- while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
4584
- return pos;
4585
- } : function(pos, dir) { return pos + dir; };
4586
- var linedir = bidi[0].level;
4587
- for (var i = 0; i < bidi.length; ++i) {
4588
- var part = bidi[i], sticky = part.level % 2 == linedir;
4589
- if ((part.from < start && part.to > start) ||
4590
- (sticky && (part.from == start || part.to == start))) break;
4591
- }
4592
- var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
4593
-
4594
- while (target != null) {
4595
- if (part.level % 2 == linedir) {
4596
- if (target < part.from || target > part.to) {
4597
- part = bidi[i += dir];
4598
- target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
4599
- } else break;
4600
- } else {
4601
- if (target == bidiLeft(part)) {
4602
- part = bidi[--i];
4603
- target = part && bidiRight(part);
4604
- } else if (target == bidiRight(part)) {
4605
- part = bidi[++i];
4606
- target = part && bidiLeft(part);
4607
- } else break;
4608
- }
4609
- }
4610
-
4611
- return target < 0 || target > line.text.length ? null : target;
4612
- }
4613
-
4614
- function moveLogically(line, start, dir, byUnit) {
4615
- var target = start + dir;
4616
- if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
4617
- return target < 0 || target > line.text.length ? null : target;
4618
- }
4619
-
4620
- // Bidirectional ordering algorithm
4621
- // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
4622
- // that this (partially) implements.
4623
-
4624
- // One-char codes used for character types:
4625
- // L (L): Left-to-Right
4626
- // R (R): Right-to-Left
4627
- // r (AL): Right-to-Left Arabic
4628
- // 1 (EN): European Number
4629
- // + (ES): European Number Separator
4630
- // % (ET): European Number Terminator
4631
- // n (AN): Arabic Number
4632
- // , (CS): Common Number Separator
4633
- // m (NSM): Non-Spacing Mark
4634
- // b (BN): Boundary Neutral
4635
- // s (B): Paragraph Separator
4636
- // t (S): Segment Separator
4637
- // w (WS): Whitespace
4638
- // N (ON): Other Neutrals
4639
-
4640
- // Returns null if characters are ordered as they appear
4641
- // (left-to-right), or an array of sections ({from, to, level}
4642
- // objects) in the order in which they occur visually.
4643
- var bidiOrdering = (function() {
4644
- // Character types for codepoints 0 to 0xff
4645
- var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
4646
- // Character types for codepoints 0x600 to 0x6ff
4647
- var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
4648
- function charType(code) {
4649
- if (code <= 0xff) return lowTypes.charAt(code);
4650
- else if (0x590 <= code && code <= 0x5f4) return "R";
4651
- else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
4652
- else if (0x700 <= code && code <= 0x8ac) return "r";
4653
- else return "L";
4654
- }
4655
-
4656
- var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
4657
- var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
4658
- // Browsers seem to always treat the boundaries of block elements as being L.
4659
- var outerType = "L";
4660
-
4661
- return function charOrdering(str) {
4662
- if (!bidiRE.test(str)) return false;
4663
- var len = str.length, types = [];
4664
- for (var i = 0, type; i < len; ++i)
4665
- types.push(type = charType(str.charCodeAt(i)));
4666
-
4667
- // W1. Examine each non-spacing mark (NSM) in the level run, and
4668
- // change the type of the NSM to the type of the previous
4669
- // character. If the NSM is at the start of the level run, it will
4670
- // get the type of sor.
4671
- for (var i = 0, prev = outerType; i < len; ++i) {
4672
- var type = types[i];
4673
- if (type == "m") types[i] = prev;
4674
- else prev = type;
4675
- }
4676
-
4677
- // W2. Search backwards from each instance of a European number
4678
- // until the first strong type (R, L, AL, or sor) is found. If an
4679
- // AL is found, change the type of the European number to Arabic
4680
- // number.
4681
- // W3. Change all ALs to R.
4682
- for (var i = 0, cur = outerType; i < len; ++i) {
4683
- var type = types[i];
4684
- if (type == "1" && cur == "r") types[i] = "n";
4685
- else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
4686
- }
4687
-
4688
- // W4. A single European separator between two European numbers
4689
- // changes to a European number. A single common separator between
4690
- // two numbers of the same type changes to that type.
4691
- for (var i = 1, prev = types[0]; i < len - 1; ++i) {
4692
- var type = types[i];
4693
- if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
4694
- else if (type == "," && prev == types[i+1] &&
4695
- (prev == "1" || prev == "n")) types[i] = prev;
4696
- prev = type;
4697
- }
4698
-
4699
- // W5. A sequence of European terminators adjacent to European
4700
- // numbers changes to all European numbers.
4701
- // W6. Otherwise, separators and terminators change to Other
4702
- // Neutral.
4703
- for (var i = 0; i < len; ++i) {
4704
- var type = types[i];
4705
- if (type == ",") types[i] = "N";
4706
- else if (type == "%") {
4707
- for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
4708
- var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
4709
- for (var j = i; j < end; ++j) types[j] = replace;
4710
- i = end - 1;
4711
- }
4712
- }
4713
-
4714
- // W7. Search backwards from each instance of a European number
4715
- // until the first strong type (R, L, or sor) is found. If an L is
4716
- // found, then change the type of the European number to L.
4717
- for (var i = 0, cur = outerType; i < len; ++i) {
4718
- var type = types[i];
4719
- if (cur == "L" && type == "1") types[i] = "L";
4720
- else if (isStrong.test(type)) cur = type;
4721
- }
4722
-
4723
- // N1. A sequence of neutrals takes the direction of the
4724
- // surrounding strong text if the text on both sides has the same
4725
- // direction. European and Arabic numbers act as if they were R in
4726
- // terms of their influence on neutrals. Start-of-level-run (sor)
4727
- // and end-of-level-run (eor) are used at level run boundaries.
4728
- // N2. Any remaining neutrals take the embedding direction.
4729
- for (var i = 0; i < len; ++i) {
4730
- if (isNeutral.test(types[i])) {
4731
- for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
4732
- var before = (i ? types[i-1] : outerType) == "L";
4733
- var after = (end < len - 1 ? types[end] : outerType) == "L";
4734
- var replace = before || after ? "L" : "R";
4735
- for (var j = i; j < end; ++j) types[j] = replace;
4736
- i = end - 1;
4737
- }
4738
- }
4739
-
4740
- // Here we depart from the documented algorithm, in order to avoid
4741
- // building up an actual levels array. Since there are only three
4742
- // levels (0, 1, 2) in an implementation that doesn't take
4743
- // explicit embedding into account, we can build up the order on
4744
- // the fly, without following the level-based algorithm.
4745
- var order = [], m;
4746
- for (var i = 0; i < len;) {
4747
- if (countsAsLeft.test(types[i])) {
4748
- var start = i;
4749
- for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
4750
- order.push({from: start, to: i, level: 0});
4751
- } else {
4752
- var pos = i, at = order.length;
4753
- for (++i; i < len && types[i] != "L"; ++i) {}
4754
- for (var j = pos; j < i;) {
4755
- if (countsAsNum.test(types[j])) {
4756
- if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
4757
- var nstart = j;
4758
- for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
4759
- order.splice(at, 0, {from: nstart, to: j, level: 2});
4760
- pos = j;
4761
- } else ++j;
4762
- }
4763
- if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
4764
- }
4765
- }
4766
- if (order[0].level == 1 && (m = str.match(/^\s+/))) {
4767
- order[0].from = m[0].length;
4768
- order.unshift({from: 0, to: m[0].length, level: 0});
4769
- }
4770
- if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
4771
- lst(order).to -= m[0].length;
4772
- order.push({from: len - m[0].length, to: len, level: 0});
4773
- }
4774
- if (order[0].level != lst(order).level)
4775
- order.push({from: len, to: len, level: order[0].level});
4776
-
4777
- return order;
4778
- };
4779
- })();
4780
-
4781
- // THE END
4782
-
4783
- CodeMirror.version = "3.02";
4784
-
4785
- return CodeMirror;
4786
- })();