Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Refactor CodeMirrorAdapter

Use a new approach to converting CodeMirror change objects to
TextOperations, using the new `removed` property on change objects that
contains the deleted text (works only with the current trunk of
CodeMirror). Thanks to @mikelehen for pointing this out to me!
  • Loading branch information...
commit e7781630af888d67ae6eb75a7b7119b7cd7c2747 1 parent bbcc393
@timjb timjb authored
Showing with 4,646 additions and 1,534 deletions.
  1. +1 −1  dist/ot-min.js
  2. +92 −75 dist/ot.js
  3. +91 −74 lib/codemirror-adapter.js
  4. +1 −1  lib/text-operation.js
  5. +8 −4 test/helpers.js
  6. 0  test/phantomjs/codemirror/.gitattributes
  7. +1 −0  test/phantomjs/codemirror/.gitignore
  8. 0  test/phantomjs/codemirror/.travis.yml
  9. 0  test/phantomjs/codemirror/CONTRIBUTING.md
  10. 0  test/phantomjs/codemirror/LICENSE
  11. 0  test/phantomjs/codemirror/README.md
  12. 0  test/phantomjs/codemirror/addon/dialog/dialog.css
  13. +4 −0 test/phantomjs/codemirror/addon/dialog/dialog.js
  14. +54 −0 test/phantomjs/codemirror/addon/display/placeholder.js
  15. +27 −4 test/phantomjs/codemirror/addon/edit/closebrackets.js
  16. +1 −2  test/phantomjs/codemirror/addon/edit/closetag.js
  17. 0  test/phantomjs/codemirror/addon/edit/continuecomment.js
  18. +20 −23 test/phantomjs/codemirror/addon/edit/continuelist.js
  19. 0  test/phantomjs/codemirror/addon/edit/matchbrackets.js
  20. 0  test/phantomjs/codemirror/addon/fold/collapserange.js
  21. +50 −92 test/phantomjs/codemirror/addon/fold/foldcode.js
  22. 0  test/phantomjs/codemirror/addon/format/formatting.js
  23. 0  test/phantomjs/codemirror/addon/hint/javascript-hint.js
  24. 0  test/phantomjs/codemirror/addon/hint/pig-hint.js
  25. 0  test/phantomjs/codemirror/addon/hint/python-hint.js
  26. 0  test/phantomjs/codemirror/addon/hint/show-hint.css
  27. +29 −12 test/phantomjs/codemirror/addon/hint/show-hint.js
  28. 0  test/phantomjs/codemirror/addon/hint/simple-hint.css
  29. 0  test/phantomjs/codemirror/addon/hint/simple-hint.js
  30. +1 −1  test/phantomjs/codemirror/addon/hint/xml-hint.js
  31. +8 −2 test/phantomjs/codemirror/addon/lint/javascript-lint.js
  32. 0  test/phantomjs/codemirror/addon/lint/json-lint.js
  33. 0  test/phantomjs/codemirror/addon/lint/lint.css
  34. +1 −0  test/phantomjs/codemirror/addon/lint/lint.js
  35. 0  test/phantomjs/codemirror/addon/mode/loadmode.js
  36. 0  test/phantomjs/codemirror/addon/mode/multiplex.js
  37. 0  test/phantomjs/codemirror/addon/mode/overlay.js
  38. 0  test/phantomjs/codemirror/addon/runmode/colorize.js
  39. 0  test/phantomjs/codemirror/addon/runmode/runmode-standalone.js
  40. 0  test/phantomjs/codemirror/addon/runmode/runmode.js
  41. 0  test/phantomjs/codemirror/addon/runmode/runmode.node.js
  42. 0  test/phantomjs/codemirror/addon/search/match-highlighter.js
  43. 0  test/phantomjs/codemirror/addon/search/search.js
  44. +8 −9 test/phantomjs/codemirror/addon/search/searchcursor.js
  45. 0  test/phantomjs/codemirror/addon/selection/active-line.js
  46. 0  test/phantomjs/codemirror/addon/selection/mark-selection.js
  47. 0  test/phantomjs/codemirror/demo/activeline.html
  48. 0  test/phantomjs/codemirror/demo/bidi.html
  49. 0  test/phantomjs/codemirror/demo/btree.html
  50. 0  test/phantomjs/codemirror/demo/buffers.html
  51. 0  test/phantomjs/codemirror/demo/changemode.html
  52. +6 −2 test/phantomjs/codemirror/demo/closebrackets.html
  53. 0  test/phantomjs/codemirror/demo/closetag.html
  54. 0  test/phantomjs/codemirror/demo/collapserange.html
  55. +1 −1  test/phantomjs/codemirror/demo/complete.html
  56. 0  test/phantomjs/codemirror/demo/emacs.html
  57. 0  test/phantomjs/codemirror/demo/folding.html
  58. 0  test/phantomjs/codemirror/demo/formatting.html
  59. 0  test/phantomjs/codemirror/demo/fullscreen.html
  60. +49 −0 test/phantomjs/codemirror/demo/indentwrap.html
  61. 0  test/phantomjs/codemirror/demo/lint.html
  62. 0  test/phantomjs/codemirror/demo/loadmode.html
  63. 0  test/phantomjs/codemirror/demo/marker.html
  64. 0  test/phantomjs/codemirror/demo/markselection.html
  65. 0  test/phantomjs/codemirror/demo/matchhighlighter.html
  66. 0  test/phantomjs/codemirror/demo/multiplex.html
  67. 0  test/phantomjs/codemirror/demo/mustache.html
  68. +36 −0 test/phantomjs/codemirror/demo/placeholder.html
  69. 0  test/phantomjs/codemirror/demo/preview.html
  70. +5 −2 test/phantomjs/codemirror/demo/resize.html
  71. 0  test/phantomjs/codemirror/demo/runmode.html
  72. 0  test/phantomjs/codemirror/demo/search.html
  73. +73 −0 test/phantomjs/codemirror/demo/spanaffectswrapping_shim.html
  74. +2 −0  test/phantomjs/codemirror/demo/theme.html
  75. 0  test/phantomjs/codemirror/demo/variableheight.html
  76. 0  test/phantomjs/codemirror/demo/vim.html
  77. +1 −1  test/phantomjs/codemirror/demo/visibletabs.html
  78. 0  test/phantomjs/codemirror/demo/widget.html
  79. 0  test/phantomjs/codemirror/demo/xmlcomplete.html
  80. 0  test/phantomjs/codemirror/doc/baboon.png
  81. 0  test/phantomjs/codemirror/doc/baboon_vector.svg
  82. +3 −0  test/phantomjs/codemirror/doc/compress.html
  83. 0  test/phantomjs/codemirror/doc/docs.css
  84. 0  test/phantomjs/codemirror/doc/internals.html
  85. +43 −13 test/phantomjs/codemirror/doc/manual.html
  86. +4 −0 test/phantomjs/codemirror/doc/modes.html
  87. 0  test/phantomjs/codemirror/doc/oldrelease.html
  88. +2 −1  test/phantomjs/codemirror/doc/realworld.html
  89. 0  test/phantomjs/codemirror/doc/reporting.html
  90. 0  test/phantomjs/codemirror/doc/upgrade_v2.2.html
  91. 0  test/phantomjs/codemirror/doc/upgrade_v3.html
  92. +2 −1  test/phantomjs/codemirror/index.html
  93. 0  test/phantomjs/codemirror/keymap/emacs.js
  94. +287 −140 test/phantomjs/codemirror/keymap/vim.js
  95. +5 −8 test/phantomjs/codemirror/lib/codemirror.css
  96. +244 −119 test/phantomjs/codemirror/lib/codemirror.js
  97. 0  test/phantomjs/codemirror/mode/apl/apl.js
  98. 0  test/phantomjs/codemirror/mode/apl/index.html
  99. 0  test/phantomjs/codemirror/mode/asterisk/asterisk.js
  100. 0  test/phantomjs/codemirror/mode/asterisk/index.html
  101. 0  test/phantomjs/codemirror/mode/clike/clike.js
  102. 0  test/phantomjs/codemirror/mode/clike/index.html
  103. 0  test/phantomjs/codemirror/mode/clike/scala.html
  104. +1 −1  test/phantomjs/codemirror/mode/clojure/clojure.js
  105. 0  test/phantomjs/codemirror/mode/clojure/index.html
  106. 0  test/phantomjs/codemirror/mode/coffeescript/LICENSE
  107. 0  test/phantomjs/codemirror/mode/coffeescript/coffeescript.js
  108. 0  test/phantomjs/codemirror/mode/coffeescript/index.html
  109. 0  test/phantomjs/codemirror/mode/commonlisp/commonlisp.js
  110. 0  test/phantomjs/codemirror/mode/commonlisp/index.html
  111. +336 −234 test/phantomjs/codemirror/mode/css/css.js
  112. 0  test/phantomjs/codemirror/mode/css/index.html
  113. +142 −0 test/phantomjs/codemirror/mode/css/scss.html
  114. +77 −0 test/phantomjs/codemirror/mode/css/scss_test.js
  115. +4 −1 test/phantomjs/codemirror/mode/css/test.js
  116. 0  test/phantomjs/codemirror/mode/diff/diff.js
  117. 0  test/phantomjs/codemirror/mode/diff/index.html
  118. 0  test/phantomjs/codemirror/mode/ecl/ecl.js
  119. 0  test/phantomjs/codemirror/mode/ecl/index.html
  120. 0  test/phantomjs/codemirror/mode/erlang/erlang.js
  121. 0  test/phantomjs/codemirror/mode/erlang/index.html
  122. +1 −0  test/phantomjs/codemirror/mode/gfm/gfm.js
  123. +5 −0 test/phantomjs/codemirror/mode/gfm/index.html
  124. +29 −1 test/phantomjs/codemirror/mode/gfm/test.js
  125. 0  test/phantomjs/codemirror/mode/go/go.js
  126. 0  test/phantomjs/codemirror/mode/go/index.html
  127. 0  test/phantomjs/codemirror/mode/groovy/groovy.js
  128. 0  test/phantomjs/codemirror/mode/groovy/index.html
  129. 0  test/phantomjs/codemirror/mode/haskell/haskell.js
  130. 0  test/phantomjs/codemirror/mode/haskell/index.html
  131. 0  test/phantomjs/codemirror/mode/haxe/haxe.js
  132. 0  test/phantomjs/codemirror/mode/haxe/index.html
  133. 0  test/phantomjs/codemirror/mode/htmlembedded/htmlembedded.js
  134. 0  test/phantomjs/codemirror/mode/htmlembedded/index.html
  135. 0  test/phantomjs/codemirror/mode/htmlmixed/htmlmixed.js
  136. 0  test/phantomjs/codemirror/mode/htmlmixed/index.html
  137. 0  test/phantomjs/codemirror/mode/http/http.js
  138. 0  test/phantomjs/codemirror/mode/http/index.html
  139. 0  test/phantomjs/codemirror/mode/javascript/index.html
  140. +17 −6 test/phantomjs/codemirror/mode/javascript/javascript.js
  141. 0  test/phantomjs/codemirror/mode/javascript/typescript.html
  142. 0  test/phantomjs/codemirror/mode/jinja2/index.html
  143. 0  test/phantomjs/codemirror/mode/jinja2/jinja2.js
  144. 0  test/phantomjs/codemirror/mode/less/index.html
  145. 0  test/phantomjs/codemirror/mode/less/less.js
  146. +23 −0 test/phantomjs/codemirror/mode/livescript/LICENSE
  147. +446 −0 test/phantomjs/codemirror/mode/livescript/index.html
  148. +267 −0 test/phantomjs/codemirror/mode/livescript/livescript.js
  149. +266 −0 test/phantomjs/codemirror/mode/livescript/livescript.ls
  150. 0  test/phantomjs/codemirror/mode/lua/index.html
  151. 0  test/phantomjs/codemirror/mode/lua/lua.js
  152. 0  test/phantomjs/codemirror/mode/markdown/index.html
  153. +76 −25 test/phantomjs/codemirror/mode/markdown/markdown.js
  154. +144 −87 test/phantomjs/codemirror/mode/markdown/test.js
  155. +4 −0 test/phantomjs/codemirror/mode/meta.js
  156. +149 −0 test/phantomjs/codemirror/mode/mirc/index.html
  157. +177 −0 test/phantomjs/codemirror/mode/mirc/mirc.js
  158. 0  test/phantomjs/codemirror/mode/mysql/index.html
  159. 0  test/phantomjs/codemirror/mode/mysql/mysql.js
  160. 0  test/phantomjs/codemirror/mode/ntriples/index.html
  161. 0  test/phantomjs/codemirror/mode/ntriples/ntriples.js
  162. 0  test/phantomjs/codemirror/mode/ocaml/index.html
  163. 0  test/phantomjs/codemirror/mode/ocaml/ocaml.js
  164. 0  test/phantomjs/codemirror/mode/pascal/LICENSE
  165. 0  test/phantomjs/codemirror/mode/pascal/index.html
  166. 0  test/phantomjs/codemirror/mode/pascal/pascal.js
  167. 0  test/phantomjs/codemirror/mode/perl/LICENSE
  168. 0  test/phantomjs/codemirror/mode/perl/index.html
  169. 0  test/phantomjs/codemirror/mode/perl/perl.js
  170. 0  test/phantomjs/codemirror/mode/php/index.html
  171. 0  test/phantomjs/codemirror/mode/php/php.js
  172. 0  test/phantomjs/codemirror/mode/pig/index.html
  173. 0  test/phantomjs/codemirror/mode/pig/pig.js
  174. 0  test/phantomjs/codemirror/mode/plsql/index.html
  175. 0  test/phantomjs/codemirror/mode/plsql/plsql.js
  176. 0  test/phantomjs/codemirror/mode/properties/index.html
  177. 0  test/phantomjs/codemirror/mode/properties/properties.js
  178. 0  test/phantomjs/codemirror/mode/python/LICENSE.txt
  179. 0  test/phantomjs/codemirror/mode/python/index.html
  180. 0  test/phantomjs/codemirror/mode/python/python.js
  181. 0  test/phantomjs/codemirror/mode/q/index.html
  182. 0  test/phantomjs/codemirror/mode/q/q.js
  183. 0  test/phantomjs/codemirror/mode/r/LICENSE
  184. 0  test/phantomjs/codemirror/mode/r/index.html
  185. 0  test/phantomjs/codemirror/mode/r/r.js
  186. 0  test/phantomjs/codemirror/mode/rpm/changes/changes.js
  187. 0  test/phantomjs/codemirror/mode/rpm/changes/index.html
  188. 0  test/phantomjs/codemirror/mode/rpm/spec/index.html
  189. 0  test/phantomjs/codemirror/mode/rpm/spec/spec.css
  190. 0  test/phantomjs/codemirror/mode/rpm/spec/spec.js
  191. +21 −0 test/phantomjs/codemirror/mode/rst/LICENSE.txt
  192. +9 −11 test/phantomjs/codemirror/mode/rst/index.html
  193. +486 −250 test/phantomjs/codemirror/mode/rst/rst.js
  194. 0  test/phantomjs/codemirror/mode/ruby/LICENSE
  195. 0  test/phantomjs/codemirror/mode/ruby/index.html
  196. 0  test/phantomjs/codemirror/mode/ruby/ruby.js
  197. 0  test/phantomjs/codemirror/mode/rust/index.html
  198. 0  test/phantomjs/codemirror/mode/rust/rust.js
  199. 0  test/phantomjs/codemirror/mode/sass/index.html
  200. 0  test/phantomjs/codemirror/mode/sass/sass.js
  201. 0  test/phantomjs/codemirror/mode/scheme/index.html
  202. 0  test/phantomjs/codemirror/mode/scheme/scheme.js
  203. 0  test/phantomjs/codemirror/mode/shell/index.html
  204. 0  test/phantomjs/codemirror/mode/shell/shell.js
  205. 0  test/phantomjs/codemirror/mode/sieve/LICENSE
  206. 0  test/phantomjs/codemirror/mode/sieve/index.html
  207. 0  test/phantomjs/codemirror/mode/sieve/sieve.js
  208. 0  test/phantomjs/codemirror/mode/smalltalk/index.html
  209. 0  test/phantomjs/codemirror/mode/smalltalk/smalltalk.js
  210. 0  test/phantomjs/codemirror/mode/smarty/index.html
  211. 0  test/phantomjs/codemirror/mode/smarty/smarty.js
  212. 0  test/phantomjs/codemirror/mode/sparql/index.html
  213. 0  test/phantomjs/codemirror/mode/sparql/sparql.js
  214. 0  test/phantomjs/codemirror/mode/sql/index.html
  215. 0  test/phantomjs/codemirror/mode/sql/sql.js
  216. 0  test/phantomjs/codemirror/mode/stex/index.html
  217. +192 −121 test/phantomjs/codemirror/mode/stex/stex.js
  218. +13 −0 test/phantomjs/codemirror/mode/stex/test.js
  219. +129 −0 test/phantomjs/codemirror/mode/tcl/index.html
  220. +131 −0 test/phantomjs/codemirror/mode/tcl/tcl.js
  221. 0  test/phantomjs/codemirror/mode/tiddlywiki/index.html
  222. 0  test/phantomjs/codemirror/mode/tiddlywiki/tiddlywiki.css
  223. 0  test/phantomjs/codemirror/mode/tiddlywiki/tiddlywiki.js
  224. 0  test/phantomjs/codemirror/mode/tiki/index.html
  225. 0  test/phantomjs/codemirror/mode/tiki/tiki.css
  226. 0  test/phantomjs/codemirror/mode/tiki/tiki.js
  227. 0  test/phantomjs/codemirror/mode/turtle/index.html
  228. 0  test/phantomjs/codemirror/mode/turtle/turtle.js
  229. 0  test/phantomjs/codemirror/mode/vb/LICENSE.txt
  230. 0  test/phantomjs/codemirror/mode/vb/index.html
  231. 0  test/phantomjs/codemirror/mode/vb/vb.js
  232. 0  test/phantomjs/codemirror/mode/vbscript/index.html
  233. 0  test/phantomjs/codemirror/mode/vbscript/vbscript.js
  234. 0  test/phantomjs/codemirror/mode/velocity/index.html
  235. 0  test/phantomjs/codemirror/mode/velocity/velocity.js
  236. +82 −171 test/phantomjs/codemirror/mode/verilog/index.html
  237. 0  test/phantomjs/codemirror/mode/verilog/verilog.js
  238. 0  test/phantomjs/codemirror/mode/xml/index.html
  239. +7 −5 test/phantomjs/codemirror/mode/xml/xml.js
  240. 0  test/phantomjs/codemirror/mode/xquery/LICENSE
  241. 0  test/phantomjs/codemirror/mode/xquery/index.html
  242. 0  test/phantomjs/codemirror/mode/xquery/test.js
  243. 0  test/phantomjs/codemirror/mode/xquery/xquery.js
  244. 0  test/phantomjs/codemirror/mode/yaml/index.html
  245. 0  test/phantomjs/codemirror/mode/yaml/yaml.js
  246. 0  test/phantomjs/codemirror/mode/z80/index.html
  247. 0  test/phantomjs/codemirror/mode/z80/z80.js
  248. +2 −2 test/phantomjs/codemirror/package.json
  249. 0  test/phantomjs/codemirror/test/doc_test.js
  250. 0  test/phantomjs/codemirror/test/driver.js
  251. +1 −0  test/phantomjs/codemirror/test/index.html
  252. 0  test/phantomjs/codemirror/test/lint/acorn.js
  253. 0  test/phantomjs/codemirror/test/lint/lint.js
  254. 0  test/phantomjs/codemirror/test/lint/parse-js.js
  255. 0  test/phantomjs/codemirror/test/lint/walk.js
  256. 0  test/phantomjs/codemirror/test/mode_test.css
  257. +2 −2 test/phantomjs/codemirror/test/mode_test.js
  258. 0  test/phantomjs/codemirror/test/phantom_driver.js
  259. +58 −3 test/phantomjs/codemirror/test/test.js
  260. +137 −12 test/phantomjs/codemirror/test/vim_test.js
  261. 0  test/phantomjs/codemirror/theme/ambiance-mobile.css
  262. 0  test/phantomjs/codemirror/theme/ambiance.css
  263. 0  test/phantomjs/codemirror/theme/blackboard.css
  264. 0  test/phantomjs/codemirror/theme/cobalt.css
  265. +1 −1  test/phantomjs/codemirror/theme/eclipse.css
  266. 0  test/phantomjs/codemirror/theme/elegant.css
  267. 0  test/phantomjs/codemirror/theme/erlang-dark.css
  268. 0  test/phantomjs/codemirror/theme/lesser-dark.css
  269. 0  test/phantomjs/codemirror/theme/monokai.css
  270. 0  test/phantomjs/codemirror/theme/neat.css
  271. 0  test/phantomjs/codemirror/theme/night.css
  272. 0  test/phantomjs/codemirror/theme/rubyblue.css
  273. 0  test/phantomjs/codemirror/theme/solarized.css
  274. 0  test/phantomjs/codemirror/theme/twilight.css
  275. 0  test/phantomjs/codemirror/theme/vibrant-ink.css
  276. 0  test/phantomjs/codemirror/theme/xq-dark.css
  277. +43 −0 test/phantomjs/codemirror/theme/xq-light.css
  278. +8 −13 test/phantomjs/test-codemirror-adapter.js
View
2  dist/ot-min.js
@@ -7,4 +7,4 @@
* \/ ot may be freely distributed under the MIT license.
*/
-if(ot===void 0)var ot={};if(ot.TextOperation=function(){"use strict";function t(){return this&&this.constructor===t?(this.ops=[],this.baseLength=0,this.targetLength=0,void 0):new t}t.prototype.equals=function(t){if(this.baseLength!==t.baseLength)return!1;if(this.targetLength!==t.targetLength)return!1;if(this.ops.length!==t.ops.length)return!1;for(var e=0;this.ops.length>e;e++)if(this.ops[e]!==t.ops[e])return!1;return!0};var e=t.isRetain=function(t){return"number"==typeof t&&t>0},o=t.isInsert=function(t){return"string"==typeof t},r=t.isDelete=function(t){return"number"==typeof t&&0>t};return t.prototype.retain=function(t){if("number"!=typeof t)throw Error("retain expects an integer");return 0===t?this:(this.baseLength+=t,this.targetLength+=t,e(this.ops[this.ops.length-1])?this.ops[this.ops.length-1]+=t:this.ops.push(t),this)},t.prototype.insert=function(t){if("string"!=typeof t)throw Error("insert expects a string");if(""===t)return this;this.targetLength+=t.length;var e=this.ops;return o(e[e.length-1])?e[e.length-1]+=t:r(e[e.length-1])?o(e[e.length-2])?e[e.length-2]+=t:(e[e.length]=e[e.length-1],e[e.length-2]=t):e.push(t),this},t.prototype["delete"]=function(t){if("string"==typeof t&&(t=t.length),"number"!=typeof t)throw Error("delete expects an integer or a string");return 0===t?this:(t>0&&(t=-t),this.baseLength-=t,r(this.ops[this.ops.length-1])?this.ops[this.ops.length-1]+=t:this.ops.push(t),this)},t.prototype.isNoop=function(){return 0===this.ops.length||1===this.ops.length&&e(this.ops[0])},t.prototype.toString=function(){var t=Array.prototype.map||function(t){for(var e=this,o=[],r=0,n=e.length;n>r;r++)o[r]=t(e[r]);return o};return t.call(this.ops,function(t){return e(t)?"retain "+t:o(t)?"insert '"+t+"'":"delete "+-t}).join(", ")},t.prototype.toJSON=function(){return this.ops},t.fromJSON=function(n){for(var i=new t,s=0,a=n.length;a>s;s++){var p=n[s];if(e(p))i.retain(p);else if(o(p))i.insert(p);else{if(!r(p))throw Error("unknown operation: "+JSON.stringify(p));i["delete"](p)}}return i},t.prototype.apply=function(t){var r=this;if(t.length!==r.baseLength)throw Error("The operation's base length must be equal to the string's length.");for(var n=[],i=0,s=0,a=this.ops,p=0,h=a.length;h>p;p++){var c=a[p];if(e(c)){if(s+c>t.length)throw Error("Operation can't retain more characters than are left in the string.");n[i++]=t.slice(s,s+c),s+=c}else o(c)?n[i++]=c:s-=c}if(s!==t.length)throw Error("The operation didn't operate on the whole string.");return n.join("")},t.prototype.invert=function(r){for(var n=0,i=new t,s=this.ops,a=0,p=s.length;p>a;a++){var h=s[a];e(h)?(i.retain(h),n+=h):o(h)?i["delete"](h.length):(i.insert(r.slice(n,n-h)),n-=h)}return i},t.prototype.compose=function(n){var i=this;if(i.targetLength!==n.baseLength)throw Error("The base length of the second operation has to be the target length of the first operation");for(var s=new t,a=i.ops,p=n.ops,h=0,c=0,u=a[h++],l=p[c++];;){if(u===void 0&&l===void 0)break;if(r(u))s["delete"](u),u=a[h++];else if(o(l))s.insert(l),l=p[c++];else{if(u===void 0)throw Error("Cannot compose operations: first operation is too short.");if(l===void 0)throw Error("Cannot compose operations: fist operation is too long.");if(e(u)&&e(l))u>l?(s.retain(l),u-=l,l=p[c++]):u===l?(s.retain(u),u=a[h++],l=p[c++]):(s.retain(u),l-=u,u=a[h++]);else if(o(u)&&r(l))u.length>-l?(u=u.slice(-l),l=p[c++]):u.length===-l?(u=a[h++],l=p[c++]):(l+=u.length,u=a[h++]);else if(o(u)&&e(l))u.length>l?(s.insert(u.slice(0,l)),u=u.slice(l),l=p[c++]):u.length===l?(s.insert(u),u=a[h++],l=p[c++]):(s.insert(u),l-=u.length,u=a[h++]);else{if(!e(u)||!r(l))throw Error("This shouldn't happen: op1: "+JSON.stringify(u)+", op2: "+JSON.stringify(l));u>-l?(s["delete"](l),u+=l,l=p[c++]):u===-l?(s["delete"](l),u=a[h++],l=p[c++]):(s["delete"](u),l+=u,u=a[h++])}}}return s},t.prototype.shouldBeComposedWith=function(n){function i(e){var o=e.ops,r=t.isRetain;switch(o.length){case 1:return o[0];case 2:return r(o[0])?o[1]:r(o[1])?o[0]:null;case 3:if(r(o[0])&&r(o[2]))return o[1]}return null}function s(t){return e(t.ops[0])?t.ops[0]:0}if(this.isNoop()||n.isNoop())return!0;var a=s(this),p=s(n),h=i(this),c=i(n);return h&&c?o(h)&&o(c)?a+h.length===p:r(h)&&r(c)?p-c===a||a===p:!1:!1},t.transform=function(n,i){if(n.baseLength!==i.baseLength)throw Error("Both operations have to have the same base length");for(var s=new t,a=new t,p=n.ops,h=i.ops,c=0,u=0,l=p[c++],f=h[u++];;){if(l===void 0&&f===void 0)break;if(o(l))s.insert(l),a.retain(l.length),l=p[c++];else if(o(f))s.retain(f.length),a.insert(f),f=h[u++];else{if(l===void 0)throw Error("Cannot compose operations: first operation is too short.");if(f===void 0)throw Error("Cannot compose operations: first operation is too long.");var d;if(e(l)&&e(f))l>f?(d=f,l-=f,f=h[u++]):l===f?(d=f,l=p[c++],f=h[u++]):(d=l,f-=l,l=p[c++]),s.retain(d),a.retain(d);else if(r(l)&&r(f))-l>-f?(l-=f,f=h[u++]):l===f?(l=p[c++],f=h[u++]):(f-=l,l=p[c++]);else if(r(l)&&e(f))-l>f?(d=f,l+=f,f=h[u++]):-l===f?(d=f,l=p[c++],f=h[u++]):(d=-l,f+=l,l=p[c++]),s["delete"](d);else{if(!e(l)||!r(f))throw Error("The two operations aren't compatible");l>-f?(d=-f,l+=f,f=h[u++]):l===-f?(d=l,l=p[c++],f=h[u++]):(d=l,f+=l,l=p[c++]),a["delete"](d)}}}return[s,a]},t}(),"object"==typeof module&&(module.exports=ot.TextOperation),ot===void 0)var ot={};if(ot.Cursor=function(t){"use strict";function e(t,e){this.position=t,this.selectionEnd=e}var o=t.ot?t.ot.TextOperation:require("./text-operation");return e.fromJSON=function(t){return new e(t.position,t.selectionEnd)},e.prototype.equals=function(t){return this.position===t.position&&this.selectionEnd===t.selectionEnd},e.prototype.compose=function(t){return t},e.prototype.transform=function(t){function r(e){for(var r=e,n=t.ops,i=0,s=t.ops.length;s>i&&(o.isRetain(n[i])?e-=n[i]:o.isInsert(n[i])?r+=n[i].length:(r-=Math.min(e,-n[i]),e+=n[i]),!(0>e));i++);return r}var n=r(this.position);return this.position===this.selectionEnd?new e(n,n):new e(n,r(this.selectionEnd))},e}(this),"object"==typeof module&&(module.exports=ot.Cursor),ot===void 0)var ot={};if(ot.WrappedOperation=function(){"use strict";function t(t,e){this.wrapped=t,this.meta=e}function e(t,e){for(var o in t)t.hasOwnProperty(o)&&(e[o]=t[o])}function o(t,o){if(t&&"object"==typeof t){if("function"==typeof t.compose)return t.compose(o);var r={};return e(t,r),e(o,r),r}return o}function r(t,e){return t&&"object"==typeof t&&"function"==typeof t.transform?t.transform(e):t}return t.prototype.apply=function(){return this.wrapped.apply.apply(this.wrapped,arguments)},t.prototype.invert=function(){var e=this.meta;return new t(this.wrapped.invert.apply(this.wrapped,arguments),e&&"object"==typeof e&&"function"==typeof e.invert?e.invert.apply(e,arguments):e)},t.prototype.compose=function(e){return new t(this.wrapped.compose(e.wrapped),o(this.meta,e.meta))},t.transform=function(e,o){var n=e.wrapped.constructor.transform,i=n(e.wrapped,o.wrapped);return[new t(i[0],r(e.meta,o.wrapped)),new t(i[1],r(o.meta,e.wrapped))]},t}(this),"object"==typeof module&&(module.exports=ot.WrappedOperation),ot===void 0)var ot={};if(ot.UndoManager=function(){"use strict";function t(t){this.maxItems=t||50,this.state=o,this.dontCompose=!1,this.undoStack=[],this.redoStack=[]}function e(t,e){for(var o=[],r=e.constructor,n=t.length-1;n>=0;n--){var i=r.transform(t[n],e);"function"==typeof i[0].isNoop&&i[0].isNoop()||o.push(i[0]),e=i[1]}return o.reverse()}var o="normal",r="undoing",n="redoing";return t.prototype.add=function(t,e){if(this.state===r)this.redoStack.push(t),this.dontCompose=!0;else if(this.state===n)this.undoStack.push(t),this.dontCompose=!0;else{var o=this.undoStack;!this.dontCompose&&e&&o.length>0?o.push(t.compose(o.pop())):(o.push(t),o.length>this.maxItems&&o.shift()),this.dontCompose=!1,this.redoStack=[]}},t.prototype.transform=function(t){this.undoStack=e(this.undoStack,t),this.redoStack=e(this.redoStack,t)},t.prototype.performUndo=function(t){if(this.state=r,0===this.undoStack.length)throw Error("undo not possible");t(this.undoStack.pop()),this.state=o},t.prototype.performRedo=function(t){if(this.state=n,0===this.redoStack.length)throw Error("redo not possible");t(this.redoStack.pop()),this.state=o},t.prototype.canUndo=function(){return 0!==this.undoStack.length},t.prototype.canRedo=function(){return 0!==this.redoStack.length},t.prototype.isUndoing=function(){return this.state===r},t.prototype.isRedoing=function(){return this.state===n},t}(),"object"==typeof module&&(module.exports=ot.UndoManager),ot===void 0)var ot={};ot.Client=function(){"use strict";function t(t){this.revision=t,this.state=n}function e(){}function o(t){this.outstanding=t}function r(t,e){this.outstanding=t,this.buffer=e}t.prototype.setState=function(t){this.state=t},t.prototype.applyClient=function(t){this.setState(this.state.applyClient(this,t))},t.prototype.applyServer=function(t){this.revision++,this.setState(this.state.applyServer(this,t))},t.prototype.serverAck=function(){this.revision++,this.setState(this.state.serverAck(this))},t.prototype.serverReconnect=function(){"function"==typeof this.state.resend&&this.state.resend(this)},t.prototype.transformCursor=function(t){return this.state.transformCursor(t)},t.prototype.sendOperation=function(){throw Error("sendOperation must be defined in child class")},t.prototype.applyOperation=function(){throw Error("applyOperation must be defined in child class")},t.Synchronized=e,e.prototype.applyClient=function(t,e){return t.sendOperation(t.revision,e),new o(e)},e.prototype.applyServer=function(t,e){return t.applyOperation(e),this},e.prototype.serverAck=function(){throw Error("There is no pending operation.")},e.prototype.transformCursor=function(t){return t};var n=new e;return t.AwaitingConfirm=o,o.prototype.applyClient=function(t,e){return new r(this.outstanding,e)},o.prototype.applyServer=function(t,e){var r=e.constructor.transform(this.outstanding,e);return t.applyOperation(r[1]),new o(r[0])},o.prototype.serverAck=function(){return n},o.prototype.transformCursor=function(t){return t.transform(this.outstanding)},o.prototype.resend=function(t){t.sendOperation(t.revision,this.outstanding)},t.AwaitingWithBuffer=r,r.prototype.applyClient=function(t,e){var o=this.buffer.compose(e);return new r(this.outstanding,o)},r.prototype.applyServer=function(t,e){var o=e.constructor.transform,n=o(this.outstanding,e),i=o(this.buffer,n[1]);return t.applyOperation(i[1]),new r(n[0],i[0])},r.prototype.serverAck=function(t){return t.sendOperation(t.revision,this.buffer),new o(this.buffer)},r.prototype.transformCursor=function(t){return t.transform(this.outstanding).transform(this.buffer)},r.prototype.resend=function(t){t.sendOperation(t.revision,this.outstanding)},t}(this),"object"==typeof module&&(module.exports=ot.Client),ot.CodeMirrorAdapter=function(){"use strict";function t(t){this.cm=t,this.ignoreNextChange=!1,this.changeRanges=[],this.docLength=this.cm.indexFromPos({line:this.cm.lastLine(),ch:0})+this.cm.getLine(this.cm.lastLine()).length,this.oldValue=this.cm.getValue(),o(this,"onBeforeChange"),o(this,"onChange"),o(this,"onCursorActivity"),o(this,"onFocus"),o(this,"onBlur"),t.on("beforeChange",this.onBeforeChange),t.on("change",this.onChange),t.on("cursorActivity",this.onCursorActivity),t.on("focus",this.onFocus),t.on("blur",this.onBlur)}function e(t,e){return t.line===e.line&&t.ch===e.ch}function o(t,e){var o=t[e];t[e]=function(){o.apply(t,arguments)}}var r=ot.TextOperation,n=ot.Cursor;t.prototype.detach=function(){this.cm.off("beforeChange",this.onBeforeChange),this.cm.off("change",this.onChange),this.cm.off("cursorActivity",this.onCursorActivity),this.cm.off("focus",this.onFocus),this.cm.off("blur",this.onBlur)},t.operationFromCodeMirrorChange=function(t,o,n,i){for(var s=n,a=(new r).retain(s),p=(new r).retain(s),h=0;t;){var c=i[h];if(!c||!e(c.from,t.from)||!e(c.to,t.to))throw Error("Not enough information in 'changeRanges' array.");var u=t.text.join("\n"),l=c.replacedText,f=s-c.fromIndex-l.length;a=a.compose((new r).retain(c.fromIndex)["delete"](l.length).insert(u).retain(f)),p=(new r).retain(c.fromIndex)["delete"](u.length).insert(l).retain(f).compose(p),s+=-l.length+u.length,t=t.next,h++}return[a,p]},t.applyOperationToCodeMirror=function(t,e){e.operation(function(){for(var o=t.ops,n=0,i=0,s=o.length;s>i;i++){var a=o[i];if(r.isRetain(a))n+=a;else if(r.isInsert(a))e.replaceRange(a,e.posFromIndex(n)),n+=a.length;else if(r.isDelete(a)){var p=e.posFromIndex(n),h=e.posFromIndex(n-a);e.replaceRange("",p,h)}}})},t.prototype.registerCallbacks=function(t){this.callbacks=t},t.getChangeRange=function(t,e){return{from:e.from,fromIndex:t.indexFromPos(e.from),to:e.to,replacedText:t.getRange(e.from,e.to)}},t.prototype.onBeforeChange=function(e,o){this.changeRanges.push(t.getChangeRange(this.cm,o))},t.prototype.onChange=function(e,o){if(!this.ignoreNextChange){var r=t.operationFromCodeMirrorChange(o,this.cm,this.docLength,this.changeRanges);this.trigger("change",r[0],r[1])}this.ignoreNextChange=!1,this.changeRanges=[],this.docLength=this.cm.indexFromPos({line:this.cm.lastLine(),ch:0})+this.cm.getLine(this.cm.lastLine()).length},t.prototype.onCursorActivity=t.prototype.onFocus=function(){this.trigger("cursorActivity")},t.prototype.onBlur=function(){this.cm.somethingSelected()||this.trigger("blur")},t.prototype.getValue=function(){return this.cm.getValue()},t.prototype.getCursor=function(){var t,o=this.cm,r=o.getCursor(),i=o.indexFromPos(r);if(o.somethingSelected()){var s=o.getCursor(!0),a=e(r,s)?o.getCursor(!1):s;t=o.indexFromPos(a)}else t=i;return new n(i,t)},t.prototype.setCursor=function(t){this.cm.setSelection(this.cm.posFromIndex(t.position),this.cm.posFromIndex(t.selectionEnd))};var i=function(){var t={},e=document.createElement("style");document.documentElement.getElementsByTagName("head")[0].appendChild(e);var o=e.sheet;return function(e){t[e]||(t[e]=!0,o.insertRule(e,(o.cssRules||o.rules).length))}}();return t.prototype.setOtherCursor=function(t,e,o){var r=this.cm.posFromIndex(t.position);if(t.position===t.selectionEnd){var n=this.cm.cursorCoords(r),s=document.createElement("pre");return s.className="other-client",s.style.borderLeftWidth="2px",s.style.borderLeftStyle="solid",s.innerHTML=" ",s.style.borderLeftColor=e,s.style.height=.9*(n.bottom-n.top)+"px",s.style.marginTop=n.top-n.bottom+"px",s.setAttribute("data-clientid",o),this.cm.addWidget(r,s,!1),{clear:function(){var t=s.parentNode;t&&t.removeChild(s)}}}var a=/^#([0-9a-fA-F]{6})$/.exec(e);if(!a)throw Error("only six-digit hex colors are allowed.");var p="selection-"+a[1],h="."+p+" { background: "+e+"; }";i(h);var c,u;return t.selectionEnd>t.position?(c=r,u=this.cm.posFromIndex(t.selectionEnd)):(c=this.cm.posFromIndex(t.selectionEnd),u=r),this.cm.markText(c,u,{className:p})},t.prototype.trigger=function(t){var e=Array.prototype.slice.call(arguments,1),o=this.callbacks&&this.callbacks[t];o&&o.apply(this,e)},t.prototype.applyOperation=function(e){this.ignoreNextChange=!0,t.applyOperationToCodeMirror(e,this.cm)},t.prototype.registerUndo=function(t){this.cm.undo=t},t.prototype.registerRedo=function(t){this.cm.redo=t},t}(),ot.SocketIOAdapter=function(){"use strict";function t(t){this.socket=t;var e=this;t.on("client_left",function(t){e.trigger("client_left",t)}).on("set_name",function(t,o){e.trigger("set_name",t,o)}).on("ack",function(){e.trigger("ack")}).on("operation",function(t,o,r){e.trigger("operation",o),e.trigger("cursor",t,r)}).on("cursor",function(t,o){e.trigger("cursor",t,o)}).on("reconnect",function(){e.trigger("reconnect")})}return t.prototype.sendOperation=function(t,e,o){this.socket.emit("operation",t,e,o)},t.prototype.sendCursor=function(t){this.socket.emit("cursor",t)},t.prototype.registerCallbacks=function(t){this.callbacks=t},t.prototype.trigger=function(t){var e=Array.prototype.slice.call(arguments,1),o=this.callbacks&&this.callbacks[t];o&&o.apply(this,e)},t}(),ot.AjaxAdapter=function(){"use strict";function t(t,e,o){"/"!==t[t.length-1]&&(t+="/"),this.path=t,this.ownUserName=e,this.majorRevision=o.major||0,this.minorRevision=o.minor||0,this.poll()}return t.prototype.renderRevisionPath=function(){return"revision/"+this.majorRevision+"-"+this.minorRevision},t.prototype.handleResponse=function(t){var e,o=t.operations;for(e=0;o.length>e;e++)o[e].user===this.ownUserName?this.trigger("ack"):this.trigger("operation",o[e].operation);o.length>0&&(this.majorRevision+=o.length,this.minorRevision=0);var r=t.events;if(r){for(e=0;r.length>e;e++){var n=r[e].user;if(n!==this.ownUserName)switch(r[e].event){case"joined":this.trigger("set_name",n,n);break;case"left":this.trigger("client_left",n);break;case"cursor":this.trigger("cursor",n,r[e].cursor)}}this.minorRevision+=r.length}var i=t.users;i&&(delete i[this.ownUserName],this.trigger("clients",i)),t.revision&&(this.majorRevision=t.revision.major,this.minorRevision=t.revision.minor)},t.prototype.poll=function(){var t=this;$.ajax({url:this.path+this.renderRevisionPath(),type:"GET",dataType:"json",timeout:5e3,success:function(e){t.handleResponse(e),t.poll()},error:function(){setTimeout(function(){t.poll()},500)}})},t.prototype.sendOperation=function(t,e,o){if(t!==this.majorRevision)throw Error("Revision numbers out of sync");var r=this;$.ajax({url:this.path+this.renderRevisionPath(),type:"POST",data:JSON.stringify({operation:e,cursor:o}),contentType:"application/json",processData:!1,success:function(){},error:function(){setTimeout(function(){r.sendOperation(t,e,o)},500)}})},t.prototype.sendCursor=function(t){$.ajax({url:this.path+this.renderRevisionPath()+"/cursor",type:"POST",data:JSON.stringify(t),contentType:"application/json",processData:!1,timeout:1e3})},t.prototype.registerCallbacks=function(t){this.callbacks=t},t.prototype.trigger=function(t){var e=Array.prototype.slice.call(arguments,1),o=this.callbacks&&this.callbacks[t];o&&o.apply(this,e)},t}(),ot.EditorClient=function(){"use strict";function t(t,e){this.cursorBefore=t,this.cursorAfter=e}function e(t,e){this.clientId=t,this.cursor=e}function o(t,e,o,r,n){this.id=t,this.listEl=e,this.editorAdapter=o,this.name=r,this.li=document.createElement("li"),r&&(this.li.textContent=r,this.listEl.appendChild(this.li)),this.setColor(r?s(r):Math.random()),n&&this.updateCursor(n)}function r(t,e,o,r){h.call(this,t),this.serverAdapter=o,this.editorAdapter=r,this.undoManager=new u,this.lastOperation=null,this.initializeClientList(),this.initializeClients(e);var n=this;this.editorAdapter.registerCallbacks({change:function(t,e){n.onChange(t,e)},cursorActivity:function(){n.onCursorActivity()},blur:function(){n.onBlur()}}),this.editorAdapter.registerUndo(function(){n.undo()}),this.editorAdapter.registerRedo(function(){n.redo()}),this.serverAdapter.registerCallbacks({client_left:function(t){n.onClientLeft(t)},set_name:function(t,e){n.getClientObject(t).setName(e)},ack:function(){n.serverAck()},operation:function(t){n.applyServer(l.fromJSON(t))},cursor:function(t,e){e?n.getClientObject(t).updateCursor(n.transformCursor(c.fromJSON(e))):n.getClientObject(t).removeCursor()},clients:function(t){var e;for(e in n.clients)n.clients.hasOwnProperty(e)&&!t.hasOwnProperty(e)&&n.onClientLeft(e);for(e in t)if(t.hasOwnProperty(e)&&n.clients.hasOwnProperty(e)){var o=t[e];o?n.clients[e].updateCursor(n.transformCursor(c.fromJSON(o))):n.clients[e].removeCursor()}},reconnect:function(){n.serverReconnect()}})}function n(t,e,o){function r(t){var e=Math.round(255*t).toString(16);return 1===e.length?"0"+e:e}return"#"+r(t)+r(e)+r(o)}function i(t,e,o){if(0===e)return n(o,o,o);var r=.5>o?o*(1+e):o+e-e*o,i=2*o-r,s=function(t){return 0>t&&(t+=1),t>1&&(t-=1),1>6*t?i+6*(r-i)*t:1>2*t?r:2>3*t?i+6*(r-i)*(2/3-t):i};return n(s(t+1/3),s(t),s(t-1/3))}function s(t){for(var e=1,o=0;t.length>o;o++)e=17*(e+t.charCodeAt(o))%360;return e/360}function a(t,e){function o(){}o.prototype=e.prototype,t.prototype=new o,t.prototype.constructor=t}function p(t){t.parentNode&&t.parentNode.removeChild(t)}var h=ot.Client,c=ot.Cursor,u=ot.UndoManager,l=ot.TextOperation,f=ot.WrappedOperation;return t.prototype.invert=function(){return new t(this.cursorAfter,this.cursorBefore)},t.prototype.compose=function(e){return new t(this.cursorBefore,e.cursorAfter)},t.prototype.transform=function(e){return new t(this.cursorBefore.transform(e),this.cursorAfter.transform(e))},e.fromJSON=function(t){return new e(t.clientId,t.cursor&&c.fromJSON(t.cursor))},e.prototype.transform=function(t){return new e(this.clientId,this.cursor&&this.cursor.transform(t))},o.prototype.setColor=function(t){this.hue=t,this.color=i(t,.75,.5),this.lightColor=i(t,.5,.9),this.li&&(this.li.style.color=this.color)},o.prototype.setName=function(t){this.name=t,this.li.textContent=t,this.li.parentNode||this.listEl.appendChild(this.li),this.setColor(s(t))},o.prototype.updateCursor=function(t){this.removeCursor(),this.cursor=t,this.mark=this.editorAdapter.setOtherCursor(t,t.position===t.selectionEnd?this.color:this.lightColor,this.id)},o.prototype.remove=function(){this.li&&p(this.li),this.removeCursor()},o.prototype.removeCursor=function(){this.mark&&this.mark.clear()},a(r,h),r.prototype.addClient=function(t,e){this.clients[t]=new o(t,this.clientListEl,this.editorAdapter,e.name||t,e.cursor?c.fromJSON(e.cursor):null)},r.prototype.initializeClients=function(t){this.clients={};for(var e in t)t.hasOwnProperty(e)&&this.addClient(e,t[e])},r.prototype.getClientObject=function(t){var e=this.clients[t];return e?e:this.clients[t]=new o(t,this.clientListEl,this.editorAdapter)},r.prototype.onClientLeft=function(t){console.log("User disconnected: "+t);var e=this.clients[t];e&&(e.remove(),delete this.clients[t])},r.prototype.initializeClientList=function(){this.clientListEl=document.createElement("ul")},r.prototype.applyUnredo=function(t){this.undoManager.add(t.invert(this.editorAdapter.getValue())),this.editorAdapter.applyOperation(t.wrapped),this.cursor=t.meta.cursorAfter,this.editorAdapter.setCursor(this.cursor),this.applyClient(t.wrapped)},r.prototype.undo=function(){var t=this;this.undoManager.canUndo()&&this.undoManager.performUndo(function(e){t.applyUnredo(e)})},r.prototype.redo=function(){var t=this;this.undoManager.canRedo()&&this.undoManager.performRedo(function(e){t.applyUnredo(e)})},r.prototype.onChange=function(e,o){var r=this.cursor;this.updateCursor();var n=new t(r,this.cursor);new f(e,n);var i;!this.undoManager.dontCompose&&this.lastOperation&&this.lastOperation.shouldBeComposedWith(e)?(i=!0,this.lastOperation=this.lastOperation.compose(e)):(i=!1,this.lastOperation=e);var s=new t(this.cursor,r);this.undoManager.add(new f(o,s),i),this.applyClient(e)},r.prototype.updateCursor=function(){this.cursor=this.editorAdapter.getCursor()},r.prototype.onCursorActivity=function(){var t=this.cursor;this.updateCursor(),t&&this.cursor.equals(t)||this.sendCursor(this.cursor)},r.prototype.onBlur=function(){this.cursor=null,this.sendCursor(null)},r.prototype.sendCursor=function(t){this.state instanceof h.AwaitingWithBuffer||this.serverAdapter.sendCursor(t)},r.prototype.sendOperation=function(t,e){this.serverAdapter.sendOperation(t,e.toJSON(),this.cursor)},r.prototype.applyOperation=function(t){this.editorAdapter.applyOperation(t),this.updateCursor(),this.undoManager.transform(new f(t,null))},r}();
+if(ot===void 0)var ot={};if(ot.TextOperation=function(){"use strict";function t(){return this&&this.constructor===t?(this.ops=[],this.baseLength=0,this.targetLength=0,void 0):new t}t.prototype.equals=function(t){if(this.baseLength!==t.baseLength)return!1;if(this.targetLength!==t.targetLength)return!1;if(this.ops.length!==t.ops.length)return!1;for(var e=0;this.ops.length>e;e++)if(this.ops[e]!==t.ops[e])return!1;return!0};var e=t.isRetain=function(t){return"number"==typeof t&&t>0},o=t.isInsert=function(t){return"string"==typeof t},r=t.isDelete=function(t){return"number"==typeof t&&0>t};return t.prototype.retain=function(t){if("number"!=typeof t)throw Error("retain expects an integer");return 0===t?this:(this.baseLength+=t,this.targetLength+=t,e(this.ops[this.ops.length-1])?this.ops[this.ops.length-1]+=t:this.ops.push(t),this)},t.prototype.insert=function(t){if("string"!=typeof t)throw Error("insert expects a string");if(""===t)return this;this.targetLength+=t.length;var e=this.ops;return o(e[e.length-1])?e[e.length-1]+=t:r(e[e.length-1])?o(e[e.length-2])?e[e.length-2]+=t:(e[e.length]=e[e.length-1],e[e.length-2]=t):e.push(t),this},t.prototype["delete"]=function(t){if("string"==typeof t&&(t=t.length),"number"!=typeof t)throw Error("delete expects an integer or a string");return 0===t?this:(t>0&&(t=-t),this.baseLength-=t,r(this.ops[this.ops.length-1])?this.ops[this.ops.length-1]+=t:this.ops.push(t),this)},t.prototype.isNoop=function(){return 0===this.ops.length||1===this.ops.length&&e(this.ops[0])},t.prototype.toString=function(){var t=Array.prototype.map||function(t){for(var e=this,o=[],r=0,n=e.length;n>r;r++)o[r]=t(e[r]);return o};return t.call(this.ops,function(t){return e(t)?"retain "+t:o(t)?"insert '"+t+"'":"delete "+-t}).join(", ")},t.prototype.toJSON=function(){return this.ops},t.fromJSON=function(n){for(var i=new t,s=0,a=n.length;a>s;s++){var p=n[s];if(e(p))i.retain(p);else if(o(p))i.insert(p);else{if(!r(p))throw Error("unknown operation: "+JSON.stringify(p));i["delete"](p)}}return i},t.prototype.apply=function(t){var r=this;if(t.length!==r.baseLength)throw Error("The operation's base length must be equal to the string's length.");for(var n=[],i=0,s=0,a=this.ops,p=0,h=a.length;h>p;p++){var u=a[p];if(e(u)){if(s+u>t.length)throw Error("Operation can't retain more characters than are left in the string.");n[i++]=t.slice(s,s+u),s+=u}else o(u)?n[i++]=u:s-=u}if(s!==t.length)throw Error("The operation didn't operate on the whole string.");return n.join("")},t.prototype.invert=function(r){for(var n=0,i=new t,s=this.ops,a=0,p=s.length;p>a;a++){var h=s[a];e(h)?(i.retain(h),n+=h):o(h)?i["delete"](h.length):(i.insert(r.slice(n,n-h)),n-=h)}return i},t.prototype.compose=function(n){var i=this;if(i.targetLength!==n.baseLength)throw Error("The base length of the second operation has to be the target length of the first operation");for(var s=new t,a=i.ops,p=n.ops,h=0,u=0,c=a[h++],l=p[u++];;){if(c===void 0&&l===void 0)break;if(r(c))s["delete"](c),c=a[h++];else if(o(l))s.insert(l),l=p[u++];else{if(c===void 0)throw Error("Cannot compose operations: first operation is too short.");if(l===void 0)throw Error("Cannot compose operations: first operation is too long.");if(e(c)&&e(l))c>l?(s.retain(l),c-=l,l=p[u++]):c===l?(s.retain(c),c=a[h++],l=p[u++]):(s.retain(c),l-=c,c=a[h++]);else if(o(c)&&r(l))c.length>-l?(c=c.slice(-l),l=p[u++]):c.length===-l?(c=a[h++],l=p[u++]):(l+=c.length,c=a[h++]);else if(o(c)&&e(l))c.length>l?(s.insert(c.slice(0,l)),c=c.slice(l),l=p[u++]):c.length===l?(s.insert(c),c=a[h++],l=p[u++]):(s.insert(c),l-=c.length,c=a[h++]);else{if(!e(c)||!r(l))throw Error("This shouldn't happen: op1: "+JSON.stringify(c)+", op2: "+JSON.stringify(l));c>-l?(s["delete"](l),c+=l,l=p[u++]):c===-l?(s["delete"](l),c=a[h++],l=p[u++]):(s["delete"](c),l+=c,c=a[h++])}}}return s},t.prototype.shouldBeComposedWith=function(n){function i(e){var o=e.ops,r=t.isRetain;switch(o.length){case 1:return o[0];case 2:return r(o[0])?o[1]:r(o[1])?o[0]:null;case 3:if(r(o[0])&&r(o[2]))return o[1]}return null}function s(t){return e(t.ops[0])?t.ops[0]:0}if(this.isNoop()||n.isNoop())return!0;var a=s(this),p=s(n),h=i(this),u=i(n);return h&&u?o(h)&&o(u)?a+h.length===p:r(h)&&r(u)?p-u===a||a===p:!1:!1},t.transform=function(n,i){if(n.baseLength!==i.baseLength)throw Error("Both operations have to have the same base length");for(var s=new t,a=new t,p=n.ops,h=i.ops,u=0,c=0,l=p[u++],f=h[c++];;){if(l===void 0&&f===void 0)break;if(o(l))s.insert(l),a.retain(l.length),l=p[u++];else if(o(f))s.retain(f.length),a.insert(f),f=h[c++];else{if(l===void 0)throw Error("Cannot compose operations: first operation is too short.");if(f===void 0)throw Error("Cannot compose operations: first operation is too long.");var d;if(e(l)&&e(f))l>f?(d=f,l-=f,f=h[c++]):l===f?(d=f,l=p[u++],f=h[c++]):(d=l,f-=l,l=p[u++]),s.retain(d),a.retain(d);else if(r(l)&&r(f))-l>-f?(l-=f,f=h[c++]):l===f?(l=p[u++],f=h[c++]):(f-=l,l=p[u++]);else if(r(l)&&e(f))-l>f?(d=f,l+=f,f=h[c++]):-l===f?(d=f,l=p[u++],f=h[c++]):(d=-l,f+=l,l=p[u++]),s["delete"](d);else{if(!e(l)||!r(f))throw Error("The two operations aren't compatible");l>-f?(d=-f,l+=f,f=h[c++]):l===-f?(d=l,l=p[u++],f=h[c++]):(d=l,f+=l,l=p[u++]),a["delete"](d)}}}return[s,a]},t}(),"object"==typeof module&&(module.exports=ot.TextOperation),ot===void 0)var ot={};if(ot.Cursor=function(t){"use strict";function e(t,e){this.position=t,this.selectionEnd=e}var o=t.ot?t.ot.TextOperation:require("./text-operation");return e.fromJSON=function(t){return new e(t.position,t.selectionEnd)},e.prototype.equals=function(t){return this.position===t.position&&this.selectionEnd===t.selectionEnd},e.prototype.compose=function(t){return t},e.prototype.transform=function(t){function r(e){for(var r=e,n=t.ops,i=0,s=t.ops.length;s>i&&(o.isRetain(n[i])?e-=n[i]:o.isInsert(n[i])?r+=n[i].length:(r-=Math.min(e,-n[i]),e+=n[i]),!(0>e));i++);return r}var n=r(this.position);return this.position===this.selectionEnd?new e(n,n):new e(n,r(this.selectionEnd))},e}(this),"object"==typeof module&&(module.exports=ot.Cursor),ot===void 0)var ot={};if(ot.WrappedOperation=function(){"use strict";function t(t,e){this.wrapped=t,this.meta=e}function e(t,e){for(var o in t)t.hasOwnProperty(o)&&(e[o]=t[o])}function o(t,o){if(t&&"object"==typeof t){if("function"==typeof t.compose)return t.compose(o);var r={};return e(t,r),e(o,r),r}return o}function r(t,e){return t&&"object"==typeof t&&"function"==typeof t.transform?t.transform(e):t}return t.prototype.apply=function(){return this.wrapped.apply.apply(this.wrapped,arguments)},t.prototype.invert=function(){var e=this.meta;return new t(this.wrapped.invert.apply(this.wrapped,arguments),e&&"object"==typeof e&&"function"==typeof e.invert?e.invert.apply(e,arguments):e)},t.prototype.compose=function(e){return new t(this.wrapped.compose(e.wrapped),o(this.meta,e.meta))},t.transform=function(e,o){var n=e.wrapped.constructor.transform,i=n(e.wrapped,o.wrapped);return[new t(i[0],r(e.meta,o.wrapped)),new t(i[1],r(o.meta,e.wrapped))]},t}(this),"object"==typeof module&&(module.exports=ot.WrappedOperation),ot===void 0)var ot={};if(ot.UndoManager=function(){"use strict";function t(t){this.maxItems=t||50,this.state=o,this.dontCompose=!1,this.undoStack=[],this.redoStack=[]}function e(t,e){for(var o=[],r=e.constructor,n=t.length-1;n>=0;n--){var i=r.transform(t[n],e);"function"==typeof i[0].isNoop&&i[0].isNoop()||o.push(i[0]),e=i[1]}return o.reverse()}var o="normal",r="undoing",n="redoing";return t.prototype.add=function(t,e){if(this.state===r)this.redoStack.push(t),this.dontCompose=!0;else if(this.state===n)this.undoStack.push(t),this.dontCompose=!0;else{var o=this.undoStack;!this.dontCompose&&e&&o.length>0?o.push(t.compose(o.pop())):(o.push(t),o.length>this.maxItems&&o.shift()),this.dontCompose=!1,this.redoStack=[]}},t.prototype.transform=function(t){this.undoStack=e(this.undoStack,t),this.redoStack=e(this.redoStack,t)},t.prototype.performUndo=function(t){if(this.state=r,0===this.undoStack.length)throw Error("undo not possible");t(this.undoStack.pop()),this.state=o},t.prototype.performRedo=function(t){if(this.state=n,0===this.redoStack.length)throw Error("redo not possible");t(this.redoStack.pop()),this.state=o},t.prototype.canUndo=function(){return 0!==this.undoStack.length},t.prototype.canRedo=function(){return 0!==this.redoStack.length},t.prototype.isUndoing=function(){return this.state===r},t.prototype.isRedoing=function(){return this.state===n},t}(),"object"==typeof module&&(module.exports=ot.UndoManager),ot===void 0)var ot={};ot.Client=function(){"use strict";function t(t){this.revision=t,this.state=n}function e(){}function o(t){this.outstanding=t}function r(t,e){this.outstanding=t,this.buffer=e}t.prototype.setState=function(t){this.state=t},t.prototype.applyClient=function(t){this.setState(this.state.applyClient(this,t))},t.prototype.applyServer=function(t){this.revision++,this.setState(this.state.applyServer(this,t))},t.prototype.serverAck=function(){this.revision++,this.setState(this.state.serverAck(this))},t.prototype.serverReconnect=function(){"function"==typeof this.state.resend&&this.state.resend(this)},t.prototype.transformCursor=function(t){return this.state.transformCursor(t)},t.prototype.sendOperation=function(){throw Error("sendOperation must be defined in child class")},t.prototype.applyOperation=function(){throw Error("applyOperation must be defined in child class")},t.Synchronized=e,e.prototype.applyClient=function(t,e){return t.sendOperation(t.revision,e),new o(e)},e.prototype.applyServer=function(t,e){return t.applyOperation(e),this},e.prototype.serverAck=function(){throw Error("There is no pending operation.")},e.prototype.transformCursor=function(t){return t};var n=new e;return t.AwaitingConfirm=o,o.prototype.applyClient=function(t,e){return new r(this.outstanding,e)},o.prototype.applyServer=function(t,e){var r=e.constructor.transform(this.outstanding,e);return t.applyOperation(r[1]),new o(r[0])},o.prototype.serverAck=function(){return n},o.prototype.transformCursor=function(t){return t.transform(this.outstanding)},o.prototype.resend=function(t){t.sendOperation(t.revision,this.outstanding)},t.AwaitingWithBuffer=r,r.prototype.applyClient=function(t,e){var o=this.buffer.compose(e);return new r(this.outstanding,o)},r.prototype.applyServer=function(t,e){var o=e.constructor.transform,n=o(this.outstanding,e),i=o(this.buffer,n[1]);return t.applyOperation(i[1]),new r(n[0],i[0])},r.prototype.serverAck=function(t){return t.sendOperation(t.revision,this.buffer),new o(this.buffer)},r.prototype.transformCursor=function(t){return t.transform(this.outstanding).transform(this.buffer)},r.prototype.resend=function(t){t.sendOperation(t.revision,this.outstanding)},t}(this),"object"==typeof module&&(module.exports=ot.Client),ot.CodeMirrorAdapter=function(){"use strict";function t(t){this.cm=t,this.ignoreNextChange=!1,i(this,"onChange"),i(this,"onCursorActivity"),i(this,"onFocus"),i(this,"onBlur"),t.on("change",this.onChange),t.on("cursorActivity",this.onCursorActivity),t.on("focus",this.onFocus),t.on("blur",this.onBlur)}function e(t,e){return t.line<e.line?-1:t.line>e.line?1:t.ch<e.ch?-1:t.ch>e.ch?1:0}function o(t,o){return 0===e(t,o)}function r(t,o){return 0>=e(t,o)}function n(t){return t.indexFromPos({line:t.lastLine(),ch:0})+t.getLine(t.lastLine()).length}function i(t,e){var o=t[e];t[e]=function(){o.apply(t,arguments)}}var s=ot.TextOperation,a=ot.Cursor;t.prototype.detach=function(){this.cm.off("change",this.onChange),this.cm.off("cursorActivity",this.onCursorActivity),this.cm.off("focus",this.onFocus),this.cm.off("blur",this.onBlur)},t.operationFromCodeMirrorChange=function(t,e){function o(t){return t[t.length-1]}function i(t){if(0===t.length)return 0;for(var e=0,o=0;t.length>o;o++)e+=t[o].length;return e+t.length-1}function a(t,e){return function(n){return r(n,e.from)?t(n):r(e.to,n)?t({line:n.line+(e.text.length>0?e.text.length-1:0)-(e.to.line-e.from.line),ch:e.to.line<n.line?n.ch:1>=e.text.length?n.ch-(e.to.ch-e.from.ch)+i(e.text):n.ch-e.to.ch+(e.text.length>0?o(e.text).length:0)})+i(e.removed)-i(e.text):e.from.line===n.line?t(e.from)+n.ch-e.from.ch:t(e.from)+i(e.removed.slice(0,n.line-e.from.line))+1+n.ch}}for(var p=[],h=0;t;)p[h++]=t,t=t.next;var u=n(e),c=(new s).retain(u),l=(new s).retain(u),f=function(t){return e.indexFromPos(t)};for(h=p.length-1;h>=0;h--){t=p[h],f=a(f,t);var d=f(t.from),g=u-d-i(t.text);c=(new s).retain(d)["delete"](i(t.removed)).insert(t.text.join("\n")).retain(g).compose(c),l=l.compose((new s).retain(d)["delete"](i(t.text)).insert(t.removed.join("\n")).retain(g)),u+=i(t.removed)-i(t.text)}return[c,l]},t.applyOperationToCodeMirror=function(t,e){e.operation(function(){for(var o=t.ops,r=0,n=0,i=o.length;i>n;n++){var a=o[n];if(s.isRetain(a))r+=a;else if(s.isInsert(a))e.replaceRange(a,e.posFromIndex(r)),r+=a.length;else if(s.isDelete(a)){var p=e.posFromIndex(r),h=e.posFromIndex(r-a);e.replaceRange("",p,h)}}})},t.prototype.registerCallbacks=function(t){this.callbacks=t},t.prototype.onChange=function(e,o){if(!this.ignoreNextChange){var r=t.operationFromCodeMirrorChange(o,this.cm);this.trigger("change",r[0],r[1])}this.ignoreNextChange=!1},t.prototype.onCursorActivity=t.prototype.onFocus=function(){this.trigger("cursorActivity")},t.prototype.onBlur=function(){this.cm.somethingSelected()||this.trigger("blur")},t.prototype.getValue=function(){return this.cm.getValue()},t.prototype.getCursor=function(){var t,e=this.cm,r=e.getCursor(),n=e.indexFromPos(r);if(e.somethingSelected()){var i=e.getCursor(!0),s=o(r,i)?e.getCursor(!1):i;t=e.indexFromPos(s)}else t=n;return new a(n,t)},t.prototype.setCursor=function(t){this.cm.setSelection(this.cm.posFromIndex(t.position),this.cm.posFromIndex(t.selectionEnd))};var p=function(){var t={},e=document.createElement("style");document.documentElement.getElementsByTagName("head")[0].appendChild(e);var o=e.sheet;return function(e){t[e]||(t[e]=!0,o.insertRule(e,(o.cssRules||o.rules).length))}}();return t.prototype.setOtherCursor=function(t,e,o){var r=this.cm.posFromIndex(t.position);if(t.position===t.selectionEnd){var n=this.cm.cursorCoords(r),i=document.createElement("pre");return i.className="other-client",i.style.borderLeftWidth="2px",i.style.borderLeftStyle="solid",i.innerHTML="&nbsp;",i.style.borderLeftColor=e,i.style.height=.9*(n.bottom-n.top)+"px",i.style.marginTop=n.top-n.bottom+"px",i.setAttribute("data-clientid",o),this.cm.addWidget(r,i,!1),{clear:function(){var t=i.parentNode;t&&t.removeChild(i)}}}var s=/^#([0-9a-fA-F]{6})$/.exec(e);if(!s)throw Error("only six-digit hex colors are allowed.");var a="selection-"+s[1],h="."+a+" { background: "+e+"; }";p(h);var u,c;return t.selectionEnd>t.position?(u=r,c=this.cm.posFromIndex(t.selectionEnd)):(u=this.cm.posFromIndex(t.selectionEnd),c=r),this.cm.markText(u,c,{className:a})},t.prototype.trigger=function(t){var e=Array.prototype.slice.call(arguments,1),o=this.callbacks&&this.callbacks[t];o&&o.apply(this,e)},t.prototype.applyOperation=function(e){this.ignoreNextChange=!0,t.applyOperationToCodeMirror(e,this.cm)},t.prototype.registerUndo=function(t){this.cm.undo=t},t.prototype.registerRedo=function(t){this.cm.redo=t},t}(),ot.SocketIOAdapter=function(){"use strict";function t(t){this.socket=t;var e=this;t.on("client_left",function(t){e.trigger("client_left",t)}).on("set_name",function(t,o){e.trigger("set_name",t,o)}).on("ack",function(){e.trigger("ack")}).on("operation",function(t,o,r){e.trigger("operation",o),e.trigger("cursor",t,r)}).on("cursor",function(t,o){e.trigger("cursor",t,o)}).on("reconnect",function(){e.trigger("reconnect")})}return t.prototype.sendOperation=function(t,e,o){this.socket.emit("operation",t,e,o)},t.prototype.sendCursor=function(t){this.socket.emit("cursor",t)},t.prototype.registerCallbacks=function(t){this.callbacks=t},t.prototype.trigger=function(t){var e=Array.prototype.slice.call(arguments,1),o=this.callbacks&&this.callbacks[t];o&&o.apply(this,e)},t}(),ot.AjaxAdapter=function(){"use strict";function t(t,e,o){"/"!==t[t.length-1]&&(t+="/"),this.path=t,this.ownUserName=e,this.majorRevision=o.major||0,this.minorRevision=o.minor||0,this.poll()}return t.prototype.renderRevisionPath=function(){return"revision/"+this.majorRevision+"-"+this.minorRevision},t.prototype.handleResponse=function(t){var e,o=t.operations;for(e=0;o.length>e;e++)o[e].user===this.ownUserName?this.trigger("ack"):this.trigger("operation",o[e].operation);o.length>0&&(this.majorRevision+=o.length,this.minorRevision=0);var r=t.events;if(r){for(e=0;r.length>e;e++){var n=r[e].user;if(n!==this.ownUserName)switch(r[e].event){case"joined":this.trigger("set_name",n,n);break;case"left":this.trigger("client_left",n);break;case"cursor":this.trigger("cursor",n,r[e].cursor)}}this.minorRevision+=r.length}var i=t.users;i&&(delete i[this.ownUserName],this.trigger("clients",i)),t.revision&&(this.majorRevision=t.revision.major,this.minorRevision=t.revision.minor)},t.prototype.poll=function(){var t=this;$.ajax({url:this.path+this.renderRevisionPath(),type:"GET",dataType:"json",timeout:5e3,success:function(e){t.handleResponse(e),t.poll()},error:function(){setTimeout(function(){t.poll()},500)}})},t.prototype.sendOperation=function(t,e,o){if(t!==this.majorRevision)throw Error("Revision numbers out of sync");var r=this;$.ajax({url:this.path+this.renderRevisionPath(),type:"POST",data:JSON.stringify({operation:e,cursor:o}),contentType:"application/json",processData:!1,success:function(){},error:function(){setTimeout(function(){r.sendOperation(t,e,o)},500)}})},t.prototype.sendCursor=function(t){$.ajax({url:this.path+this.renderRevisionPath()+"/cursor",type:"POST",data:JSON.stringify(t),contentType:"application/json",processData:!1,timeout:1e3})},t.prototype.registerCallbacks=function(t){this.callbacks=t},t.prototype.trigger=function(t){var e=Array.prototype.slice.call(arguments,1),o=this.callbacks&&this.callbacks[t];o&&o.apply(this,e)},t}(),ot.EditorClient=function(){"use strict";function t(t,e){this.cursorBefore=t,this.cursorAfter=e}function e(t,e){this.clientId=t,this.cursor=e}function o(t,e,o,r,n){this.id=t,this.listEl=e,this.editorAdapter=o,this.name=r,this.li=document.createElement("li"),r&&(this.li.textContent=r,this.listEl.appendChild(this.li)),this.setColor(r?s(r):Math.random()),n&&this.updateCursor(n)}function r(t,e,o,r){h.call(this,t),this.serverAdapter=o,this.editorAdapter=r,this.undoManager=new c,this.lastOperation=null,this.initializeClientList(),this.initializeClients(e);var n=this;this.editorAdapter.registerCallbacks({change:function(t,e){n.onChange(t,e)},cursorActivity:function(){n.onCursorActivity()},blur:function(){n.onBlur()}}),this.editorAdapter.registerUndo(function(){n.undo()}),this.editorAdapter.registerRedo(function(){n.redo()}),this.serverAdapter.registerCallbacks({client_left:function(t){n.onClientLeft(t)},set_name:function(t,e){n.getClientObject(t).setName(e)},ack:function(){n.serverAck()},operation:function(t){n.applyServer(l.fromJSON(t))},cursor:function(t,e){e?n.getClientObject(t).updateCursor(n.transformCursor(u.fromJSON(e))):n.getClientObject(t).removeCursor()},clients:function(t){var e;for(e in n.clients)n.clients.hasOwnProperty(e)&&!t.hasOwnProperty(e)&&n.onClientLeft(e);for(e in t)if(t.hasOwnProperty(e)&&n.clients.hasOwnProperty(e)){var o=t[e];o?n.clients[e].updateCursor(n.transformCursor(u.fromJSON(o))):n.clients[e].removeCursor()}},reconnect:function(){n.serverReconnect()}})}function n(t,e,o){function r(t){var e=Math.round(255*t).toString(16);return 1===e.length?"0"+e:e}return"#"+r(t)+r(e)+r(o)}function i(t,e,o){if(0===e)return n(o,o,o);var r=.5>o?o*(1+e):o+e-e*o,i=2*o-r,s=function(t){return 0>t&&(t+=1),t>1&&(t-=1),1>6*t?i+6*(r-i)*t:1>2*t?r:2>3*t?i+6*(r-i)*(2/3-t):i};return n(s(t+1/3),s(t),s(t-1/3))}function s(t){for(var e=1,o=0;t.length>o;o++)e=17*(e+t.charCodeAt(o))%360;return e/360}function a(t,e){function o(){}o.prototype=e.prototype,t.prototype=new o,t.prototype.constructor=t}function p(t){t.parentNode&&t.parentNode.removeChild(t)}var h=ot.Client,u=ot.Cursor,c=ot.UndoManager,l=ot.TextOperation,f=ot.WrappedOperation;return t.prototype.invert=function(){return new t(this.cursorAfter,this.cursorBefore)},t.prototype.compose=function(e){return new t(this.cursorBefore,e.cursorAfter)},t.prototype.transform=function(e){return new t(this.cursorBefore.transform(e),this.cursorAfter.transform(e))},e.fromJSON=function(t){return new e(t.clientId,t.cursor&&u.fromJSON(t.cursor))},e.prototype.transform=function(t){return new e(this.clientId,this.cursor&&this.cursor.transform(t))},o.prototype.setColor=function(t){this.hue=t,this.color=i(t,.75,.5),this.lightColor=i(t,.5,.9),this.li&&(this.li.style.color=this.color)},o.prototype.setName=function(t){this.name=t,this.li.textContent=t,this.li.parentNode||this.listEl.appendChild(this.li),this.setColor(s(t))},o.prototype.updateCursor=function(t){this.removeCursor(),this.cursor=t,this.mark=this.editorAdapter.setOtherCursor(t,t.position===t.selectionEnd?this.color:this.lightColor,this.id)},o.prototype.remove=function(){this.li&&p(this.li),this.removeCursor()},o.prototype.removeCursor=function(){this.mark&&this.mark.clear()},a(r,h),r.prototype.addClient=function(t,e){this.clients[t]=new o(t,this.clientListEl,this.editorAdapter,e.name||t,e.cursor?u.fromJSON(e.cursor):null)},r.prototype.initializeClients=function(t){this.clients={};for(var e in t)t.hasOwnProperty(e)&&this.addClient(e,t[e])},r.prototype.getClientObject=function(t){var e=this.clients[t];return e?e:this.clients[t]=new o(t,this.clientListEl,this.editorAdapter)},r.prototype.onClientLeft=function(t){console.log("User disconnected: "+t);var e=this.clients[t];e&&(e.remove(),delete this.clients[t])},r.prototype.initializeClientList=function(){this.clientListEl=document.createElement("ul")},r.prototype.applyUnredo=function(t){this.undoManager.add(t.invert(this.editorAdapter.getValue())),this.editorAdapter.applyOperation(t.wrapped),this.cursor=t.meta.cursorAfter,this.editorAdapter.setCursor(this.cursor),this.applyClient(t.wrapped)},r.prototype.undo=function(){var t=this;this.undoManager.canUndo()&&this.undoManager.performUndo(function(e){t.applyUnredo(e)})},r.prototype.redo=function(){var t=this;this.undoManager.canRedo()&&this.undoManager.performRedo(function(e){t.applyUnredo(e)})},r.prototype.onChange=function(e,o){var r=this.cursor;this.updateCursor();var n=new t(r,this.cursor);new f(e,n);var i;!this.undoManager.dontCompose&&this.lastOperation&&this.lastOperation.shouldBeComposedWith(e)?(i=!0,this.lastOperation=this.lastOperation.compose(e)):(i=!1,this.lastOperation=e);var s=new t(this.cursor,r);this.undoManager.add(new f(o,s),i),this.applyClient(e)},r.prototype.updateCursor=function(){this.cursor=this.editorAdapter.getCursor()},r.prototype.onCursorActivity=function(){var t=this.cursor;this.updateCursor(),t&&this.cursor.equals(t)||this.sendCursor(this.cursor)},r.prototype.onBlur=function(){this.cursor=null,this.sendCursor(null)},r.prototype.sendCursor=function(t){this.state instanceof h.AwaitingWithBuffer||this.serverAdapter.sendCursor(t)},r.prototype.sendOperation=function(t,e){this.serverAdapter.sendOperation(t,e.toJSON(),this.cursor)},r.prototype.applyOperation=function(t){this.editorAdapter.applyOperation(t),this.updateCursor(),this.undoManager.transform(new f(t,null))},r}();
View
167 dist/ot.js
@@ -276,7 +276,7 @@ ot.TextOperation = (function () {
throw new Error("Cannot compose operations: first operation is too short.");
}
if (typeof op2 === 'undefined') {
- throw new Error("Cannot compose operations: fist operation is too long.");
+ throw new Error("Cannot compose operations: first operation is too long.");
}
if (isRetain(op1) && isRetain(op2)) {
@@ -990,17 +990,11 @@ ot.CodeMirrorAdapter = (function () {
function CodeMirrorAdapter (cm) {
this.cm = cm;
this.ignoreNextChange = false;
- this.changeRanges = [];
- this.docLength = this.cm.indexFromPos({ line: this.cm.lastLine(), ch: 0 }) +
- this.cm.getLine(this.cm.lastLine()).length;
- this.oldValue = this.cm.getValue();
- bind(this, 'onBeforeChange');
bind(this, 'onChange');
bind(this, 'onCursorActivity');
bind(this, 'onFocus');
bind(this, 'onBlur');
- cm.on('beforeChange', this.onBeforeChange);
cm.on('change', this.onChange);
cm.on('cursorActivity', this.onCursorActivity);
cm.on('focus', this.onFocus);
@@ -1009,60 +1003,108 @@ ot.CodeMirrorAdapter = (function () {
// Removes all event listeners from the CodeMirror instance.
CodeMirrorAdapter.prototype.detach = function () {
- this.cm.off('beforeChange', this.onBeforeChange);
this.cm.off('change', this.onChange);
this.cm.off('cursorActivity', this.onCursorActivity);
this.cm.off('focus', this.onFocus);
this.cm.off('blur', this.onBlur);
};
- function eqPos (a, b) { return a.line === b.line && a.ch === b.ch; }
+ function cmpPos (a, b) {
+ if (a.line < b.line) { return -1; }
+ if (a.line > b.line) { return 1; }
+ if (a.ch < b.ch) { return -1; }
+ if (a.ch > b.ch) { return 1; }
+ return 0;
+ }
+ function posEq (a, b) { return cmpPos(a, b) === 0; }
+ function posLe (a, b) { return cmpPos(a, b) <= 0; }
+
+ function codemirrorDocLength (doc) {
+ return doc.indexFromPos({ line: doc.lastLine(), ch: 0 }) +
+ doc.getLine(doc.lastLine()).length;
+ }
// Converts a CodeMirror change object into a TextOperation and its inverse
- // and returns them as a two-element array. Apart from the CodeMirror change
- // object, it requires several other pieces of information:
- //
- // * `doc`: the CodeMirror document (a standalone document or simply the CodeMirror
- // instance)
- // * `docStartLength`: the length of the document in characters before the change.
- // * `changeRanges`: an array of `{ from, to, fromIndex, replacedText }` objects that contain
- // the same from and to position objects in the same order as the linked
- // list in the change object. `fromIndex` is the zero-based position in
- // the pre-change document corresponding to `from`. `replacedText` contains
- // the text that got deleted by the change.
- CodeMirrorAdapter.operationFromCodeMirrorChange = function (change, doc, docStartLength, changeRanges) {
- var docLength = docStartLength;
- var operation = new TextOperation().retain(docLength);
- var inverse = new TextOperation().retain(docLength);
-
- var i = 0;
+ // and returns them as a two-element array.
+ CodeMirrorAdapter.operationFromCodeMirrorChange = function (change, doc) {
+ // Approach: Replay the changes, beginning with the most recent one, and
+ // construct the operation and its inverse. We have to convert the position
+ // in the pre-change coordinate system to an index. We have a method to
+ // convert a position in the coordinate system after all changes to an index,
+ // namely CodeMirror's `indexFromPos` method. We can use the information of
+ // a single change object to convert a post-change coordinate system to a
+ // pre-change coordinate system. We can now proceed inductively to get a
+ // pre-change coordinate system for all changes in the linked list.
+ // A disadvantage of this approach is its complexity `O(n^2)` in the length
+ // of the linked list of changes.
+
+ var changes = [], i = 0;
while (change) {
- var changeRange = changeRanges[i];
- if (!changeRange || !eqPos(changeRange.from, change.from) || !eqPos(changeRange.to, change.to)) {
- throw new Error("Not enough information in 'changeRanges' array.");
- }
- var text = change.text.join('\n');
- var replacedText = changeRange.replacedText;
- var restLength = docLength - changeRange.fromIndex - replacedText.length;
-
- operation = operation.compose(new TextOperation()
- .retain(changeRange.fromIndex)
- ['delete'](replacedText.length)
- .insert(text)
- .retain(restLength)
- );
+ changes[i++] = change;
+ change = change.next;
+ }
+
+ var docEndLength = codemirrorDocLength(doc);
+ var operation = new TextOperation().retain(docEndLength);
+ var inverse = new TextOperation().retain(docEndLength);
+
+ var indexFromPos = function (pos) {
+ return doc.indexFromPos(pos);
+ };
+
+ function last (arr) { return arr[arr.length - 1]; }
+
+ function sumLengths (strArr) {
+ if (strArr.length === 0) { return 0; }
+ var sum = 0;
+ for (var i = 0; i < strArr.length; i++) { sum += strArr[i].length; }
+ return sum + strArr.length - 1;
+ }
- inverse = new TextOperation()
- .retain(changeRange.fromIndex)
- ['delete'](text.length)
- .insert(replacedText)
+ function updateIndexFromPos (indexFromPos, change) {
+ return function (pos) {
+ if (posLe(pos, change.from)) { return indexFromPos(pos); }
+ if (posLe(change.to, pos)) {
+ return indexFromPos({
+ line: pos.line + (change.text.length > 0 ? change.text.length - 1 : 0) - (change.to.line - change.from.line),
+ ch: (change.to.line < pos.line) ?
+ pos.ch :
+ (change.text.length <= 1) ?
+ pos.ch - (change.to.ch - change.from.ch) + sumLengths(change.text) :
+ pos.ch - change.to.ch + (change.text.length > 0 ? last(change.text).length : 0)
+ }) + sumLengths(change.removed) - sumLengths(change.text);
+ }
+ if (change.from.line === pos.line) {
+ return indexFromPos(change.from) + pos.ch - change.from.ch;
+ }
+ return indexFromPos(change.from) +
+ sumLengths(change.removed.slice(0, pos.line - change.from.line)) +
+ 1 + pos.ch;
+ };
+ }
+
+ for (i = changes.length - 1; i >= 0; i--) {
+ change = changes[i];
+ indexFromPos = updateIndexFromPos(indexFromPos, change);
+
+ var fromIndex = indexFromPos(change.from);
+ var restLength = docEndLength - fromIndex - sumLengths(change.text);
+
+ operation = new TextOperation()
+ .retain(fromIndex)
+ ['delete'](sumLengths(change.removed))
+ .insert(change.text.join('\n'))
.retain(restLength)
- .compose(inverse);
+ .compose(operation);
- docLength += -replacedText.length + text.length;
+ inverse = inverse.compose(new TextOperation()
+ .retain(fromIndex)
+ ['delete'](sumLengths(change.text))
+ .insert(change.removed.join('\n'))
+ .retain(restLength)
+ );
- change = change.next;
- i++;
+ docEndLength += sumLengths(change.removed) - sumLengths(change.text);
}
return [operation, inverse];
@@ -1093,37 +1135,12 @@ ot.CodeMirrorAdapter = (function () {
this.callbacks = cb;
};
- // Returns a `changeRange` object (as described in the documentation for the
- // `operationFromCodeMirrorChange` method) from a change object returned by
- // the `'beforeChange'` event. Must be called directly from the event handler.
- // Note that the objects obtained from the `beforeChange` events are only in
- // one-to-one correspondents with the elements of the linked list of change
- // objects if the document doesn't contain read-only ranges!
- CodeMirrorAdapter.getChangeRange = function (doc, change) {
- return {
- from: change.from,
- fromIndex: doc.indexFromPos(change.from),
- to: change.to,
- replacedText: doc.getRange(change.from, change.to)
- };
- };
-
- CodeMirrorAdapter.prototype.onBeforeChange = function (_, change) {
- this.changeRanges.push(CodeMirrorAdapter.getChangeRange(this.cm, change));
- };
-
CodeMirrorAdapter.prototype.onChange = function (_, change) {
if (!this.ignoreNextChange) {
- var pair = CodeMirrorAdapter.operationFromCodeMirrorChange(
- change, this.cm,
- this.docLength, this.changeRanges
- );
+ var pair = CodeMirrorAdapter.operationFromCodeMirrorChange(change, this.cm);
this.trigger('change', pair[0], pair[1]);
}
this.ignoreNextChange = false;
- this.changeRanges = [];
- this.docLength = this.cm.indexFromPos({ line: this.cm.lastLine(), ch: 0 }) +
- this.cm.getLine(this.cm.lastLine()).length;
};
CodeMirrorAdapter.prototype.onCursorActivity =
@@ -1146,7 +1163,7 @@ ot.CodeMirrorAdapter = (function () {
var selectionEnd;
if (cm.somethingSelected()) {
var startPos = cm.getCursor(true);
- var selectionEndPos = eqPos(cursorPos, startPos) ? cm.getCursor(false) : startPos;
+ var selectionEndPos = posEq(cursorPos, startPos) ? cm.getCursor(false) : startPos;
selectionEnd = cm.indexFromPos(selectionEndPos);
} else {
selectionEnd = position;
View
165 lib/codemirror-adapter.js
@@ -9,17 +9,11 @@ ot.CodeMirrorAdapter = (function () {
function CodeMirrorAdapter (cm) {
this.cm = cm;
this.ignoreNextChange = false;
- this.changeRanges = [];
- this.docLength = this.cm.indexFromPos({ line: this.cm.lastLine(), ch: 0 }) +
- this.cm.getLine(this.cm.lastLine()).length;
- this.oldValue = this.cm.getValue();
- bind(this, 'onBeforeChange');
bind(this, 'onChange');
bind(this, 'onCursorActivity');
bind(this, 'onFocus');
bind(this, 'onBlur');
- cm.on('beforeChange', this.onBeforeChange);
cm.on('change', this.onChange);
cm.on('cursorActivity', this.onCursorActivity);
cm.on('focus', this.onFocus);
@@ -28,60 +22,108 @@ ot.CodeMirrorAdapter = (function () {
// Removes all event listeners from the CodeMirror instance.
CodeMirrorAdapter.prototype.detach = function () {
- this.cm.off('beforeChange', this.onBeforeChange);
this.cm.off('change', this.onChange);
this.cm.off('cursorActivity', this.onCursorActivity);
this.cm.off('focus', this.onFocus);
this.cm.off('blur', this.onBlur);
};
- function eqPos (a, b) { return a.line === b.line && a.ch === b.ch; }
+ function cmpPos (a, b) {
+ if (a.line < b.line) { return -1; }
+ if (a.line > b.line) { return 1; }
+ if (a.ch < b.ch) { return -1; }
+ if (a.ch > b.ch) { return 1; }
+ return 0;
+ }
+ function posEq (a, b) { return cmpPos(a, b) === 0; }
+ function posLe (a, b) { return cmpPos(a, b) <= 0; }
+
+ function codemirrorDocLength (doc) {
+ return doc.indexFromPos({ line: doc.lastLine(), ch: 0 }) +
+ doc.getLine(doc.lastLine()).length;
+ }
// Converts a CodeMirror change object into a TextOperation and its inverse
- // and returns them as a two-element array. Apart from the CodeMirror change
- // object, it requires several other pieces of information:
- //
- // * `doc`: the CodeMirror document (a standalone document or simply the CodeMirror
- // instance)
- // * `docStartLength`: the length of the document in characters before the change.
- // * `changeRanges`: an array of `{ from, to, fromIndex, replacedText }` objects that contain
- // the same from and to position objects in the same order as the linked
- // list in the change object. `fromIndex` is the zero-based position in
- // the pre-change document corresponding to `from`. `replacedText` contains
- // the text that got deleted by the change.
- CodeMirrorAdapter.operationFromCodeMirrorChange = function (change, doc, docStartLength, changeRanges) {
- var docLength = docStartLength;
- var operation = new TextOperation().retain(docLength);
- var inverse = new TextOperation().retain(docLength);
-
- var i = 0;
+ // and returns them as a two-element array.
+ CodeMirrorAdapter.operationFromCodeMirrorChange = function (change, doc) {
+ // Approach: Replay the changes, beginning with the most recent one, and
+ // construct the operation and its inverse. We have to convert the position
+ // in the pre-change coordinate system to an index. We have a method to
+ // convert a position in the coordinate system after all changes to an index,
+ // namely CodeMirror's `indexFromPos` method. We can use the information of
+ // a single change object to convert a post-change coordinate system to a
+ // pre-change coordinate system. We can now proceed inductively to get a
+ // pre-change coordinate system for all changes in the linked list.
+ // A disadvantage of this approach is its complexity `O(n^2)` in the length
+ // of the linked list of changes.
+
+ var changes = [], i = 0;
while (change) {
- var changeRange = changeRanges[i];
- if (!changeRange || !eqPos(changeRange.from, change.from) || !eqPos(changeRange.to, change.to)) {
- throw new Error("Not enough information in 'changeRanges' array.");
- }
- var text = change.text.join('\n');
- var replacedText = changeRange.replacedText;
- var restLength = docLength - changeRange.fromIndex - replacedText.length;
-
- operation = operation.compose(new TextOperation()
- .retain(changeRange.fromIndex)
- ['delete'](replacedText.length)
- .insert(text)
- .retain(restLength)
- );
+ changes[i++] = change;
+ change = change.next;
+ }
+
+ var docEndLength = codemirrorDocLength(doc);
+ var operation = new TextOperation().retain(docEndLength);
+ var inverse = new TextOperation().retain(docEndLength);
+
+ var indexFromPos = function (pos) {
+ return doc.indexFromPos(pos);
+ };
+
+ function last (arr) { return arr[arr.length - 1]; }
+
+ function sumLengths (strArr) {
+ if (strArr.length === 0) { return 0; }
+ var sum = 0;
+ for (var i = 0; i < strArr.length; i++) { sum += strArr[i].length; }
+ return sum + strArr.length - 1;
+ }
+
+ function updateIndexFromPos (indexFromPos, change) {
+ return function (pos) {
+ if (posLe(pos, change.from)) { return indexFromPos(pos); }
+ if (posLe(change.to, pos)) {
+ return indexFromPos({
+ line: pos.line + (change.text.length > 0 ? change.text.length - 1 : 0) - (change.to.line - change.from.line),
+ ch: (change.to.line < pos.line) ?
+ pos.ch :
+ (change.text.length <= 1) ?
+ pos.ch - (change.to.ch - change.from.ch) + sumLengths(change.text) :
+ pos.ch - change.to.ch + (change.text.length > 0 ? last(change.text).length : 0)
+ }) + sumLengths(change.removed) - sumLengths(change.text);
+ }
+ if (change.from.line === pos.line) {
+ return indexFromPos(change.from) + pos.ch - change.from.ch;
+ }
+ return indexFromPos(change.from) +
+ sumLengths(change.removed.slice(0, pos.line - change.from.line)) +
+ 1 + pos.ch;
+ };
+ }
- inverse = new TextOperation()
- .retain(changeRange.fromIndex)
- ['delete'](text.length)
- .insert(replacedText)
+ for (i = changes.length - 1; i >= 0; i--) {
+ change = changes[i];
+ indexFromPos = updateIndexFromPos(indexFromPos, change);
+
+ var fromIndex = indexFromPos(change.from);
+ var restLength = docEndLength - fromIndex - sumLengths(change.text);
+
+ operation = new TextOperation()
+ .retain(fromIndex)
+ ['delete'](sumLengths(change.removed))
+ .insert(change.text.join('\n'))
.retain(restLength)
- .compose(inverse);
+ .compose(operation);
- docLength += -replacedText.length + text.length;
+ inverse = inverse.compose(new TextOperation()
+ .retain(fromIndex)
+ ['delete'](sumLengths(change.text))
+ .insert(change.removed.join('\n'))
+ .retain(restLength)
+ );
- change = change.next;
- i++;
+ docEndLength += sumLengths(change.removed) - sumLengths(change.text);
}
return [operation, inverse];
@@ -112,37 +154,12 @@ ot.CodeMirrorAdapter = (function () {
this.callbacks = cb;
};
- // Returns a `changeRange` object (as described in the documentation for the
- // `operationFromCodeMirrorChange` method) from a change object returned by
- // the `'beforeChange'` event. Must be called directly from the event handler.
- // Note that the objects obtained from the `beforeChange` events are only in
- // one-to-one correspondents with the elements of the linked list of change
- // objects if the document doesn't contain read-only ranges!
- CodeMirrorAdapter.getChangeRange = function (doc, change) {
- return {
- from: change.from,
- fromIndex: doc.indexFromPos(change.from),
- to: change.to,
- replacedText: doc.getRange(change.from, change.to)
- };
- };
-
- CodeMirrorAdapter.prototype.onBeforeChange = function (_, change) {
- this.changeRanges.push(CodeMirrorAdapter.getChangeRange(this.cm, change));
- };
-
CodeMirrorAdapter.prototype.onChange = function (_, change) {
if (!this.ignoreNextChange) {
- var pair = CodeMirrorAdapter.operationFromCodeMirrorChange(
- change, this.cm,
- this.docLength, this.changeRanges
- );
+ var pair = CodeMirrorAdapter.operationFromCodeMirrorChange(change, this.cm);
this.trigger('change', pair[0], pair[1]);
}
this.ignoreNextChange = false;
- this.changeRanges = [];
- this.docLength = this.cm.indexFromPos({ line: this.cm.lastLine(), ch: 0 }) +
- this.cm.getLine(this.cm.lastLine()).length;
};
CodeMirrorAdapter.prototype.onCursorActivity =
@@ -165,7 +182,7 @@ ot.CodeMirrorAdapter = (function () {
var selectionEnd;
if (cm.somethingSelected()) {
var startPos = cm.getCursor(true);
- var selectionEndPos = eqPos(cursorPos, startPos) ? cm.getCursor(false) : startPos;
+ var selectionEndPos = posEq(cursorPos, startPos) ? cm.getCursor(false) : startPos;
selectionEnd = cm.indexFromPos(selectionEndPos);
} else {
selectionEnd = position;
View
2  lib/text-operation.js
@@ -267,7 +267,7 @@ ot.TextOperation = (function () {
throw new Error("Cannot compose operations: first operation is too short.");
}
if (typeof op2 === 'undefined') {
- throw new Error("Cannot compose operations: fist operation is too long.");
+ throw new Error("Cannot compose operations: first operation is too long.");
}
if (isRetain(op1) && isRetain(op2)) {
View
12 test/helpers.js
@@ -10,8 +10,12 @@
function randomString (n) {
var str = '';
while (n--) {
- var chr = randomInt(26) + 97;
- str = str + String.fromCharCode(chr);
+ if (Math.random() < 0.15) {
+ str += '\n';
+ } else {
+ var chr = randomInt(26) + 97;
+ str += String.fromCharCode(chr);
+ }
}
return str;
}
@@ -23,11 +27,11 @@
left = str.length - operation.baseLength;
if (left === 0) { break; }
var r = Math.random();
- var l = 1 + randomInt(Math.min(left, 20));
+ var l = 1 + randomInt(Math.min(left - 1, 20));
if (r < 0.2) {
operation.insert(randomString(l));
} else if (r < 0.4) {
- operation['delete'](str.slice(operation.baseLength, operation.baseLength + l));
+ operation['delete'](l);
} else {
operation.retain(l);
}
View
0  test/phantomjs/codemirror/.gitattributes 100644 → 100755
File mode changed
View
1  test/phantomjs/codemirror/.gitignore 100644 → 100755
@@ -1,3 +1,4 @@
/node_modules
/npm-debug.log
test.html
+.tern-*
View
0  test/phantomjs/codemirror/.travis.yml 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/CONTRIBUTING.md 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/LICENSE 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/README.md 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/dialog/dialog.css 100644 → 100755
File mode changed
View
4 test/phantomjs/codemirror/addon/dialog/dialog.js 100644 → 100755
@@ -25,6 +25,7 @@
var inp = dialog.getElementsByTagName("input")[0], button;
if (inp) {
CodeMirror.on(inp, "keydown", function(e) {
+ if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
if (e.keyCode == 13 || e.keyCode == 27) {
CodeMirror.e_stop(e);
close();
@@ -32,6 +33,9 @@
if (e.keyCode == 13) callback(inp.value);
}
});
+ if (options && options.onKeyUp) {
+ CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
+ }
if (options && options.value) inp.value = options.value;
inp.focus();
CodeMirror.on(inp, "blur", close);
View
54 test/phantomjs/codemirror/addon/display/placeholder.js
@@ -0,0 +1,54 @@
+(function() {
+ CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
+ var prev = old && old != CodeMirror.Init;
+ if (val && !prev) {
+ cm.on("focus", onFocus);
+ cm.on("blur", onBlur);
+ cm.on("change", onChange);
+ onChange(cm);
+ } else if (!val && prev) {
+ cm.off("focus", onFocus);
+ cm.off("blur", onBlur);
+ cm.off("change", onChange);
+ clearPlaceholder(cm);
+ var wrapper = cm.getWrapperElement();
+ wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
+ }
+
+ if (val && !cm.hasFocus()) onBlur(cm);
+ });
+
+ function clearPlaceholder(cm) {
+ if (cm._placeholder) {
+ cm._placeholder.parentNode.removeChild(cm._placeholder);
+ cm._placeholder = null;
+ }
+ }
+ function setPlaceholder(cm) {
+ clearPlaceholder(cm);
+ var elt = cm._placeholder = document.createElement("pre");
+ elt.style.cssText = "height: 0; overflow: visible";
+ elt.className = "CodeMirror-placeholder";
+ elt.appendChild(document.createTextNode(cm.getOption("placeholder")));
+ cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
+ }
+
+ function onFocus(cm) {
+ clearPlaceholder(cm);
+ }
+ function onBlur(cm) {
+ if (isEmpty(cm)) setPlaceholder(cm);
+ }
+ function onChange(cm) {
+ var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
+ wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
+
+ if (cm.hasFocus()) return;
+ if (empty) setPlaceholder(cm);
+ else clearPlaceholder(cm);
+ }
+
+ function isEmpty(cm) {
+ return (cm.lineCount() === 1) && (cm.getLine(0) === "");
+ }
+})();
View
31 test/phantomjs/codemirror/addon/edit/closebrackets.js 100644 → 100755
@@ -1,5 +1,6 @@
(function() {
var DEFAULT_BRACKETS = "()[]{}''\"\"";
+ var SPACE_CHAR_REGEX = /\s/;
CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
var wasOn = old && old != CodeMirror.Init;
@@ -10,17 +11,39 @@
});
function buildKeymap(pairs) {
- var map = {name : "autoCloseBrackets"};
+ var map = {
+ name : "autoCloseBrackets",
+ Backspace: function(cm) {
+ if (cm.somethingSelected()) return CodeMirror.Pass;
+ var cur = cm.getCursor(), line = cm.getLine(cur.line);
+ if (cur.ch && cur.ch < line.length &&
+ pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0)
+ cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1));
+ else
+ return CodeMirror.Pass;
+ }
+ };
+ var closingBrackets = [];
for (var i = 0; i < pairs.length; i += 2) (function(left, right) {
+ if (left != right) closingBrackets.push(right);
+ function surround(cm) {
+ var selection = cm.getSelection();
+ cm.replaceSelection(left + selection + right);
+ }
function maybeOverwrite(cm) {
var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1));
- if (ahead != right) return CodeMirror.Pass;
+ if (ahead != right || cm.somethingSelected()) return CodeMirror.Pass;
else cm.execCommand("goCharRight");
}
map["'" + left + "'"] = function(cm) {
+ if (cm.somethingSelected()) return surround(cm);
if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return;
- var cur = cm.getCursor("start"), ahead = CodeMirror.Pos(cur.line, cur.ch + 1);
- cm.replaceSelection(left + right, {head: ahead, anchor: ahead});
+ var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1);
+ var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch);
+ if (line.length == cur.ch || closingBrackets.indexOf(nextChar) >= 0 || SPACE_CHAR_REGEX.test(nextChar))
+ cm.replaceSelection(left + right, {head: ahead, anchor: ahead});
+ else
+ return CodeMirror.Pass;
};
if (left != right) map["'" + right + "'"] = maybeOverwrite;
})(pairs.charAt(i), pairs.charAt(i + 1));
View
3  test/phantomjs/codemirror/addon/edit/closetag.js 100644 → 100755
@@ -55,8 +55,7 @@
if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
var lowerTagName = tagName.toLowerCase();
// Don't process the '>' at the end of an end-tag or self-closing tag
- if (tok.type == "tag" && state.type == "closeTag" ||
- /\/\s*$/.test(tok.string) ||
+ if (tok.type == "tag" && state.type == "closeTag" || tok.string.indexOf("/") > -1 ||
dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1)
return CodeMirror.Pass;
View
0  test/phantomjs/codemirror/addon/edit/continuecomment.js 100644 → 100755
File mode changed
View
43 test/phantomjs/codemirror/addon/edit/continuelist.js 100644 → 100755
@@ -1,28 +1,25 @@
(function() {
+ 'use strict';
+
+ var listRE = /^(\s*)([*+-]|(\d+)\.)(\s*)/,
+ unorderedBullets = '*+-';
+
CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
- var pos = cm.getCursor(), token = cm.getTokenAt(pos);
- var space;
- if (token.className == "string") {
- var full = cm.getRange({line: pos.line, ch: 0}, {line: pos.line, ch: token.end});
- var listStart = /\*|\d+\./, listContinue;
- if (token.string.search(listStart) == 0) {
- var reg = /^[\W]*(\d+)\./g;
- var matches = reg.exec(full);
- if(matches)
- listContinue = (parseInt(matches[1]) + 1) + ". ";
- else
- listContinue = "* ";
- space = full.slice(0, token.start);
- if (!/^\s*$/.test(space)) {
- space = "";
- for (var i = 0; i < token.start; ++i) space += " ";
- }
- }
+ var pos = cm.getCursor(),
+ inList = cm.getStateAfter(pos.line).list,
+ match;
+
+ if (!inList || !(match = cm.getLine(pos.line).match(listRE))) {
+ cm.execCommand('newlineAndIndent');
+ return;
}
- if (space != null)
- cm.replaceSelection("\n" + space + listContinue, "end");
- else
- cm.execCommand("newlineAndIndent");
+ var indent = match[1], after = match[4];
+ var bullet = unorderedBullets.indexOf(match[2]) >= 0
+ ? match[2]
+ : (parseInt(match[3], 10) + 1) + '.';
+
+ cm.replaceSelection('\n' + indent + bullet + after, 'end');
};
-})();
+
+}());
View
0  test/phantomjs/codemirror/addon/edit/matchbrackets.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/fold/collapserange.js 100644 → 100755
File mode changed
View
142 test/phantomjs/codemirror/addon/fold/foldcode.js 100644 → 100755
@@ -1,109 +1,67 @@
-// the tagRangeFinder function is
-// Copyright (C) 2011 by Daniel Glazman <daniel@glazman.org>
-// released under the MIT license (../../LICENSE) like the rest of CodeMirror
-CodeMirror.tagRangeFinder = function(cm, start) {
+CodeMirror.tagRangeFinder = (function() {
var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
- var xmlNAMERegExp = new RegExp("^[" + nameStartChar + "][" + nameChar + "]*");
+ var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
- var lineText = cm.getLine(start.line);
- var found = false;
- var tag = null;
- var pos = start.ch;
- while (!found) {
- pos = lineText.indexOf("<", pos);
- if (-1 == pos) // no tag on line
- return;
- if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag
- pos++;
- continue;
- }
- // ok we seem to have a start tag
- if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name...
- pos++;
- continue;
+ return function(cm, start) {
+ var line = start.line, ch = start.ch, lineText = cm.getLine(line);
+
+ function nextLine() {
+ if (line >= cm.lastLine()) return;
+ ch = 0;
+ lineText = cm.getLine(++line);
+ return true;
}
- var gtPos = lineText.indexOf(">", pos + 1);
- if (-1 == gtPos) { // end of start tag not in line
- var l = start.line + 1;
- var foundGt = false;
- var lastLine = cm.lineCount();
- while (l < lastLine && !foundGt) {
- var lt = cm.getLine(l);
- gtPos = lt.indexOf(">");
- if (-1 != gtPos) { // found a >
- foundGt = true;
- var slash = lt.lastIndexOf("/", gtPos);
- if (-1 != slash && slash < gtPos) {
- var str = lineText.substr(slash, gtPos - slash + 1);
- if (!str.match( /\/\s*\>/ )) // yep, that's the end of empty tag
- return;
- }
- }
- l++;
+ function toTagEnd() {
+ for (;;) {
+ var gt = lineText.indexOf(">", ch);
+ if (gt == -1) { if (nextLine()) continue; else return; }
+ var lastSlash = lineText.lastIndexOf("/", gt);
+ var selfClose = lastSlash > -1 && /^\s*$/.test(lineText.slice(lastSlash + 1, gt));
+ ch = gt + 1;
+ return selfClose ? "selfClose" : "regular";
}
- found = true;
}
- else {
- var slashPos = lineText.lastIndexOf("/", gtPos);
- if (-1 == slashPos) { // cannot be empty tag
- found = true;
- // don't continue
- }
- else { // empty tag?
- // check if really empty tag
- var str = lineText.substr(slashPos, gtPos - slashPos + 1);
- if (!str.match( /\/\s*\>/ )) { // finally not empty
- found = true;
- // don't continue
- }
+ function toNextTag() {
+ for (;;) {
+ xmlTagStart.lastIndex = ch;
+ var found = xmlTagStart.exec(lineText);
+ if (!found) { if (nextLine()) continue; else return; }
+ ch = found.index + found[0].length;
+ return found;
}
}
- if (found) {
- var subLine = lineText.substr(pos + 1);
- tag = subLine.match(xmlNAMERegExp);
- if (tag) {
- // we have an element name, wooohooo !
- tag = tag[0];
- // do we have the close tag on same line ???
- if (-1 != lineText.indexOf("</" + tag + ">", pos)) // yep
- {
- found = false;
- }
- // we don't, so we have a candidate...
+
+ var stack = [], startCh;
+ for (;;) {
+ var openTag = toNextTag(), end;
+ if (!openTag || line != start.line || !(end = toTagEnd())) return;
+ if (!openTag[1] && end != "selfClose") {
+ stack.push(openTag[2]);
+ startCh = ch;
+ break;
}
- else
- found = false;
}
- if (!found)
- pos++;
- }
- if (found) {
- var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)";
- var startTagRegExp = new RegExp(startTag);
- var endTag = "</" + tag + ">";
- var depth = 1;
- var l = start.line + 1;
- var lastLine = cm.lineCount();
- while (l < lastLine) {
- lineText = cm.getLine(l);
- var match = lineText.match(startTagRegExp);
- if (match) {
- for (var i = 0; i < match.length; i++) {
- if (match[i] == endTag)
- depth--;
- else
- depth++;
- if (!depth) return {from: CodeMirror.Pos(start.line, gtPos + 1),
- to: CodeMirror.Pos(l, match.index)};
+ for (;;) {
+ var next = toNextTag(), end, tagLine = line, tagCh = ch - (next ? next[0].length : 0);
+ if (!next || !(end = toTagEnd())) return;
+ if (end == "selfClose") continue;
+ if (next[1]) { // closing tag
+ for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
+ stack.length = i;
+ break;
}
+ if (!stack.length) return {
+ from: CodeMirror.Pos(start.line, startCh),
+ to: CodeMirror.Pos(tagLine, tagCh)
+ };
+ } else { // opening tag
+ stack.push(next[2]);
}
- l++;
}
- return;
- }
-};
+ };
+})();
CodeMirror.braceRangeFinder = function(cm, start) {
var line = start.line, lineText = cm.getLine(line);
View
0  test/phantomjs/codemirror/addon/format/formatting.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/hint/javascript-hint.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/hint/pig-hint.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/hint/python-hint.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/hint/show-hint.css 100644 → 100755
File mode changed
View
41 test/phantomjs/codemirror/addon/hint/show-hint.js 100644 → 100755
@@ -1,6 +1,7 @@
CodeMirror.showHint = function(cm, getHints, options) {
if (!options) options = {};
var startCh = cm.getCursor().ch, continued = false;
+ var closeOn = options.closeCharacters || /[\s()\[\]{};:]/;
function startHinting() {
// We want a single cursor position.
@@ -12,12 +13,22 @@ CodeMirror.showHint = function(cm, getHints, options) {
return showHints(getHints(cm, options));
}
+ function getText(completion) {
+ if (typeof completion == "string") return completion;
+ else return completion.text;
+ }
+
+ function pickCompletion(cm, data, completion) {
+ if (completion.hint) completion.hint(cm, data, completion);
+ else cm.replaceRange(getText(completion), data.from, data.to);
+ }
+
function showHints(data) {
if (!data || !data.list.length) return;
var completions = data.list;
// When there is only one completion, use it directly.
if (!continued && options.completeSingle !== false && completions.length == 1) {
- cm.replaceRange(completions[0], data.from, data.to);
+ pickCompletion(cm, data, completions[0]);
return true;
}
@@ -25,9 +36,12 @@ CodeMirror.showHint = function(cm, getHints, options) {
var hints = document.createElement("ul"), selectedHint = 0;
hints.className = "CodeMirror-hints";
for (var i = 0; i < completions.length; ++i) {
- var elt = hints.appendChild(document.createElement("li"));
- elt.className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active");
- elt.appendChild(document.createTextNode(completions[i]));
+ var elt = hints.appendChild(document.createElement("li")), completion = completions[i];
+ var className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active");
+ if (completion.className != null) className = completion.className + " " + className;
+ elt.className = className;
+ if (completion.render) completion.render(elt, data, completion);
+ else elt.appendChild(document.createTextNode(getText(completion)));
elt.hintId = i;
}
var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null);
@@ -63,9 +77,10 @@ CodeMirror.showHint = function(cm, getHints, options) {
function changeActive(i) {
i = Math.max(0, Math.min(i, completions.length - 1));
if (selectedHint == i) return;
- hints.childNodes[selectedHint].className = "CodeMirror-hint";
- var node = hints.childNodes[selectedHint = i];
- node.className = "CodeMirror-hint CodeMirror-hint-active";
+ var node = hints.childNodes[selectedHint];
+ node.className = node.className.replace(" CodeMirror-hint-active", "");
+ node = hints.childNodes[selectedHint = i];
+ node.className += " CodeMirror-hint-active";
if (node.offsetTop < hints.scrollTop)
hints.scrollTop = node.offsetTop - 3;
else if (node.offsetTop + node.offsetHeight > hints.scrollTop + hints.clientHeight)
@@ -113,11 +128,12 @@ CodeMirror.showHint = function(cm, getHints, options) {
CodeMirror.on(hints, "dblclick", function(e) {
var t = e.target || e.srcElement;
if (t.hintId != null) {selectedHint = t.hintId; pick();}
- setTimeout(function(){cm.focus();}, 20);
});
CodeMirror.on(hints, "click", function(e) {
var t = e.target || e.srcElement;
if (t.hintId != null) changeActive(t.hintId);
+ });
+ CodeMirror.on(hints, "mousedown", function() {
setTimeout(function(){cm.focus();}, 20);
});
@@ -134,16 +150,17 @@ CodeMirror.showHint = function(cm, getHints, options) {
cm.off("scroll", onScroll);
}
function pick() {
- cm.replaceRange(completions[selectedHint], data.from, data.to);
+ pickCompletion(cm, data, completions[selectedHint]);
close();
}
var once, lastPos = cm.getCursor(), lastLen = cm.getLine(lastPos.line).length;
function cursorActivity() {
clearTimeout(once);
- var pos = cm.getCursor(), len = cm.getLine(pos.line).length;
- if (pos.line != lastPos.line || len - pos.ch != lastLen - lastPos.ch ||
- pos.ch < startCh || cm.somethingSelected())
+ var pos = cm.getCursor(), line = cm.getLine(pos.line);
+ if (pos.line != lastPos.line || line.length - pos.ch != lastLen - lastPos.ch ||
+ pos.ch < startCh || cm.somethingSelected() ||
+ (pos.ch && closeOn.test(line.charAt(pos.ch - 1))))
close();
else
once = setTimeout(function(){close(); continued = true; startHinting();}, 70);
View
0  test/phantomjs/codemirror/addon/hint/simple-hint.css 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/hint/simple-hint.js 100644 → 100755
File mode changed
View
2  test/phantomjs/codemirror/addon/hint/xml-hint.js 100644 → 100755
@@ -50,7 +50,7 @@
if(text.length >= 0) {
- var regex = new RegExp('<([^!?][^\\s/>]*).*?>', 'g');
+ var regex = new RegExp('<([^!?][^\\s/>]*)[\\s\\S]*?>', 'g');
var matches = [];
var match;
View
10 test/phantomjs/codemirror/addon/lint/javascript-lint.js 100644 → 100755
@@ -9,13 +9,19 @@
"Unmatched ", " and instead saw", " is not defined",
"Unclosed string", "Stopping, unable to continue" ];
- CodeMirror.javascriptValidator = function(text) {
- JSHINT(text);
+ function validator(options, text) {
+ JSHINT(text, options);
var errors = JSHINT.data().errors, result = [];
if (errors) parseErrors(errors, result);
return result;
+ }
+
+ CodeMirror.javascriptValidatorWithOptions = function(options) {
+ return function(text) { return validator(options, text); };
};
+ CodeMirror.javascriptValidator = CodeMirror.javascriptValidatorWithOptions(null);
+
function cleanup(error) {
// All problems are warnings by default
fixWith(error, warnings, "warning", true);
View
0  test/phantomjs/codemirror/addon/lint/json-lint.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/lint/lint.css 100644 → 100755
File mode changed
View
1  test/phantomjs/codemirror/addon/lint/lint.js 100644 → 100755
@@ -127,6 +127,7 @@ CodeMirror.validate = (function() {
if (state.hasGutter)
cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1));
}
+ if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
}
function onChange(cm) {
View
0  test/phantomjs/codemirror/addon/mode/loadmode.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/mode/multiplex.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/mode/overlay.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/runmode/colorize.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/runmode/runmode-standalone.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/runmode/runmode.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/runmode/runmode.node.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/search/match-highlighter.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/search/search.js 100644 → 100755
File mode changed
View
17 test/phantomjs/codemirror/addon/search/searchcursor.js 100644 → 100755
@@ -17,16 +17,15 @@
this.matches = function(reverse, pos) {
if (reverse) {
query.lastIndex = 0;
- var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0;
- while (match) {
- start += match.index + 1;
- line = line.slice(start);
- query.lastIndex = 0;
- var newmatch = query.exec(line);
- if (newmatch) match = newmatch;
- else break;
+ var line = cm.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start;
+ for (;;) {
+ query.lastIndex = cutOff;
+ var newMatch = query.exec(line);
+ if (!newMatch) break;
+ match = newMatch;
+ start = match.index;
+ cutOff = match.index + 1;
}
- start--;
} else {
query.lastIndex = pos.ch;
var line = cm.getLine(pos.line), match = query.exec(line),
View
0  test/phantomjs/codemirror/addon/selection/active-line.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/addon/selection/mark-selection.js 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/activeline.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/bidi.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/btree.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/buffers.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/changemode.html 100644 → 100755
File mode changed
View
8 test/phantomjs/codemirror/demo/closebrackets.html 100644 → 100755
@@ -13,14 +13,18 @@
</style>
</head>
<body>
-
+
<h1>CodeMirror: Closebrackets Demo</h1>
-
+
<p>Type a bracket like '[', '(', '{', '&quot;', or '''
and <a href="../doc/manual.html#addon_closebrackets">the addon</a>
will auto-close it. Type the closing variant when directly in
front of a matching character and it will overwrite it.</p>
+ <p>If you backspace over a starting bracket while inside empty brackets
+ (e.g. <code>{|}</code>), it will delete the closing bracket for you.</p>
+
+
<form><textarea id="code" name="code">(function() {
var DEFAULT_BRACKETS = "()[]{}''\"\"";
View
0  test/phantomjs/codemirror/demo/closetag.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/collapserange.html 100644 → 100755
File mode changed
View
2  test/phantomjs/codemirror/demo/complete.html 100644 → 100755
@@ -53,7 +53,7 @@
</textarea></form>
<p>Press <strong>ctrl-space</strong> to activate autocompletion. See
-the code (<a href="../addon/hint/simple-hint.js">here</a>
+the code (<a href="../addon/hint/show-hint.js">here</a>
and <a href="../addon/hint/javascript-hint.js">here</a>) to figure out
how it works.</p>
View
0  test/phantomjs/codemirror/demo/emacs.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/folding.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/formatting.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/fullscreen.html 100644 → 100755
File mode changed
View
49 test/phantomjs/codemirror/demo/indentwrap.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>CodeMirror: Indented wrapped line demo</title>
+ <link rel="stylesheet" href="../lib/codemirror.css">
+ <script src="../lib/codemirror.js"></script>
+ <script src="../mode/xml/xml.js"></script>
+ <link rel="stylesheet" href="../doc/docs.css">
+
+ <style type="text/css">
+ .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
+ </style>
+ </head>
+ <body>
+ <h1>CodeMirror: Indented wrapped line demo</h1>
+
+ <form><textarea id="code" name="code">
+<!doctype html>
+<body>
+ <h2 id="overview">Overview</h2>
+
+ <p>CodeMirror is a code-editor component that can be embedded in Web pages. The core library provides <em>only</em> the editor component, no accompanying buttons, auto-completion, or other IDE functionality. It does provide a rich API on top of which such functionality can be straightforwardly implemented. See the <a href="#addons">add-ons</a> included in the distribution, and the <a href="https://github.com/jagthedrummer/codemirror-ui">CodeMirror UI</a> project, for reusable implementations of extra features.</p>
+
+ <p>CodeMirror works with language-specific modes. Modes are JavaScript programs that help color (and optionally indent) text written in a given language. The distribution comes with a number of modes (see the <a href="../mode/"><code>mode/</code></a> directory), and it isn't hard to <a href="#modeapi">write new ones</a> for other languages.</p>
+</body>
+</textarea></form>
+
+ <p>This page uses a hack on top of the <code>"renderLine"</code>
+ event to make wrapped text line up with the base indentation of
+ the line.</p>
+
+ <script>
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+ lineNumbers: true,
+ lineWrapping: true,
+ mode: "text/html"
+ });
+ var charWidth = editor.defaultCharWidth(), basePadding = 4;
+ editor.on("renderLine", function(cm, line, elt) {
+ var off = CodeMirror.countColumn(line.text, null, cm.getOption("tabSize")) * charWidth;
+ elt.style.textIndent = "-" + off + "px";
+ elt.style.paddingLeft = (basePadding + off) + "px";
+ });
+ editor.refresh();
+ </script>
+
+ </body>
+</html>
View
0  test/phantomjs/codemirror/demo/lint.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/loadmode.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/marker.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/markselection.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/matchhighlighter.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/multiplex.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/mustache.html 100644 → 100755
File mode changed
View
36 test/phantomjs/codemirror/demo/placeholder.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>CodeMirror: Placeholder demo</title>
+ <link rel="stylesheet" href="../lib/codemirror.css">
+ <script src="../lib/codemirror.js"></script>
+ <script src="../addon/display/placeholder.js"></script>
+ <link rel="stylesheet" href="../doc/docs.css">
+
+ <style type="text/css">
+ .CodeMirror { border: 1px solid silver; }
+ .CodeMirror-empty { outline: 1px solid #c22; }
+ .CodeMirror-empty.CodeMirror-focused { outline: none; }
+ .CodeMirror pre.CodeMirror-placeholder { color: #999; }
+ </style>
+ </head>
+ <body>
+ <h1>CodeMirror: Placeholder demo</h1>
+
+ <form><textarea id="code" name="code" placeholder="Code goes here..."></textarea></form>
+
+ <p>The <a href="../doc/manual.html#addon_placeholder">placeholder</a>
+ plug-in adds an option <code>placeholder</code> that can be set to
+ make text appear in the editor when it is empty and not focused.
+ If the source textarea has a <code>placeholder</code> attribute,
+ it will automatically be inherited.</p>
+
+ <script>
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+ lineNumbers: true
+ });
+ </script>
+
+ </body>
+</html>
View
0  test/phantomjs/codemirror/demo/preview.html 100644 → 100755
File mode changed
View
7 test/phantomjs/codemirror/demo/resize.html 100644 → 100755
@@ -33,12 +33,15 @@
}
</textarea></form>
-<p>By setting a few CSS properties, CodeMirror can be made to
+<p>By setting a few CSS properties, and giving
+the <a href="../doc/manual.html#option_viewportMargin"><code>viewportMargin</code></a>
+a value of <code>Infinity</code>, CodeMirror can be made to
automatically resize to fit its content.</p>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
- lineNumbers: true
+ lineNumbers: true,
+ viewportMargin: Infinity
});
</script>
View
0  test/phantomjs/codemirror/demo/runmode.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/search.html 100644 → 100755
File mode changed
View
73 test/phantomjs/codemirror/demo/spanaffectswrapping_shim.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>CodeMirror: Automatically derive odd wrapping behavior for your browser</title>
+ <link rel="stylesheet" href="../doc/docs.css">
+ </head>
+ <body>
+ <h1>CodeMirror: odd wrapping shim</h1>
+
+ <p>This is a hack to automatically derive
+ a <code>spanAffectsWrapping</code> regexp for a browser. See the
+ comments above that variable
+ in <a href="../lib/codemirror.js"><code>lib/codemirror.js</code></a>
+ for some more details.</p>
+
+ <div style="white-space: pre-wrap; width: 50px;" id="area"></div>
+ <pre id="output"></pre>
+
+ <script id="script">
+ var a = document.getElementById("area"), bad = Object.create(null);
+ var chars = "a~`!@#$%^&*()-_=+}{[]\|'\"/?.>,<:;", l = chars.length;
+ for (var x = 0; x < l; ++x) for (var y = 0; y < l; ++y) {
+ var s1 = "foooo" + chars.charAt(x), s2 = chars.charAt(y) + "br";
+ a.appendChild(document.createTextNode(s1 + s2));
+ var h1 = a.offsetHeight;
+ a.innerHTML = "";
+ a.appendChild(document.createElement("span")).appendChild(document.createTextNode(s1));
+ a.appendChild(document.createElement("span")).appendChild(document.createTextNode(s2));
+ if (a.offsetHeight != h1)
+ bad[chars.charAt(x)] = (bad[chars.charAt(x)] || "") + chars.charAt(y);
+ a.innerHTML = "";
+ }
+
+ var re = "";
+ function toREElt(str) {
+ if (str.length > 1) {
+ var invert = false;
+ if (str.length > chars.length * .6) {
+ invert = true;
+ var newStr = "";
+ for (var i = 0; i < l; ++i) if (str.indexOf(chars.charAt(i)) == -1) newStr += chars.charAt(i);
+ str = newStr;
+ }
+ str = str.replace(/[\-\.\]\"\'\\\/\^a]/g, function(orig) { return orig == "a" ? "\\w" : "\\" + orig; });
+ return "[" + (invert ? "^" : "") + str + "]";
+ } else if (str == "a") {
+ return "\\w";
+ } else if (/[?$*()+{}[\]\.|/\'\"]/.test(str)) {
+ return "\\" + str;
+ } else {
+ return str;
+ }
+ }
+
+ var newRE = "";
+ for (;;) {
+ var left = null;
+ for (var left in bad) break;
+ if (left == null) break;
+ var right = bad[left];
+ delete bad[left];
+ for (var other in bad) if (bad[other] == right) {
+ left += other;
+ delete bad[other];
+ }
+ newRE += (newRE ? "|" : "") + toREElt(left) + toREElt(right);
+ }
+
+ document.getElementById("output").appendChild(document.createTextNode("Your regexp is: " + (newRE || "^$")));
+ </script>
+ </body>
+</html>
View
2  test/phantomjs/codemirror/demo/theme.html 100644 → 100755
@@ -15,6 +15,7 @@
<link rel="stylesheet" href="../theme/rubyblue.css">
<link rel="stylesheet" href="../theme/lesser-dark.css">
<link rel="stylesheet" href="../theme/xq-dark.css">
+ <link rel="stylesheet" href="../theme/xq-light.css">
<link rel="stylesheet" href="../theme/ambiance.css">
<link rel="stylesheet" href="../theme/blackboard.css">
<link rel="stylesheet" href="../theme/vibrant-ink.css">
@@ -62,6 +63,7 @@
<option>twilight</option>
<option>vibrant-ink</option>
<option>xq-dark</option>
+ <option>xq-light</option>
</select>
</p>
View
0  test/phantomjs/codemirror/demo/variableheight.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/vim.html 100644 → 100755
File mode changed
View
2  test/phantomjs/codemirror/demo/visibletabs.html 100644 → 100755
@@ -37,7 +37,7 @@
</textarea></form>
<p>Tabs inside the editor are spans with the
-class <code>cm-tab</code>, and can be styled.
+class <code>cm-tab</code>, and can be styled.</p>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
View
0  test/phantomjs/codemirror/demo/widget.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/demo/xmlcomplete.html 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/doc/baboon.png 100644 → 100755
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
0  test/phantomjs/codemirror/doc/baboon_vector.svg 100644 → 100755
File mode changed
View
3  test/phantomjs/codemirror/doc/compress.html 100644 → 100755
@@ -89,8 +89,10 @@
<option value="http://codemirror.net/mode/javascript/javascript.js">javascript.js</option>
<option value="http://codemirror.net/mode/jinja2/jinja2.js">jinja2.js</option>
<option value="http://codemirror.net/mode/less/less.js">less.js</option>
+ <option value="http://codemirror.net/mode/livescript/livescript.js">livescript.js</option>
<option value="http://codemirror.net/mode/lua/lua.js">lua.js</option>
<option value="http://codemirror.net/mode/markdown/markdown.js">markdown.js</option>
+ <option value="http://codemirror.net/mode/mirc/mirc.js">mirc.js</option>
<option value="http://codemirror.net/mode/mysql/mysql.js">mysql.js</option>
<option value="http://codemirror.net/mode/ntriples/ntriples.js">ntriples.js</option>
<option value="http://codemirror.net/mode/ocaml/ocaml.js">ocaml.js</option>
@@ -118,6 +120,7 @@
<option value="http://codemirror.net/mode/sql/sql.js">sql.js</option>
<option value="http://codemirror.net/mode/sparql/sparql.js">sparql.js</option>
<option value="http://codemirror.net/mode/stex/stex.js">stex.js</option>
+ <option value="http://codemirror.net/mode/tcl/tcl.js">tcl.js</option>
<option value="http://codemirror.net/mode/tiddlywiki/tiddlywiki.js">tiddlywiki.js</option>
<option value="http://codemirror.net/mode/tiki/tiki.js">tiki.js</option>
<option value="http://codemirror.net/mode/turtle/turtle.js">turtle.js</option>
View
0  test/phantomjs/codemirror/doc/docs.css 100644 → 100755
File mode changed
View
0  test/phantomjs/codemirror/doc/internals.html 100644 → 100755
File mode changed
View
56 test/phantomjs/codemirror/doc/manual.html 100644 → 100755
@@ -352,7 +352,7 @@ <h2 id="events">Events</h2>
<dl>
<dt id="event_change"><code>"change" (instance, changeObj)</code></dt>
<dd>Fires every time the content of the editor is changed.
- The <code>changeObj</code> is a <code>{from, to, text,
+ The <code>changeObj</code> is a <code>{from, to, text, removed,
next}</code> object containing information about the changes
that occurred as second argument. <code>from</code>
and <code>to</code> are the positions (in the pre-change
@@ -360,8 +360,10 @@ <h2 id="events">Events</h2>
example, it might be <code>{ch:0, line:18}</code> if the
position is at the beginning of line #19). <code>text</code> is
an array of strings representing the text that replaced the
- changed range (split by line). If multiple changes happened
- during a single operation, the object will have
+ changed range (split by line). <code>removed</code> is the text
+ that used to be between <code>from</code> and <code>to</code>,
+ which is overwritten by this change. If multiple changes
+ happened during a single operation, the object will have
a <code>next</code> property pointing to another change object
(which may point to another, etc).</dd>
@@ -466,9 +468,12 @@ <h2 id="events">Events</h2>
is associated with the <em>start</em> of the line. Mostly useful
when you need to find out when your <a href="#setGutterMarker">gutter
markers</a> on a given line are removed.</dd>
- <dt id="event_line_change"><code>"change" ()</code></dt>
+ <dt id="event_line_change"><code>"change" (line, changeObj)</code></dt>
<dd>Fires when the line's text content is changed in any way
- (but the line is not deleted outright).</dd>
+ (but the line is not deleted outright). The <code>change</code>
+ object is similar to the one passed
+ to <a href="#event_change">change event</a> on the editor
+ object.</dd>
</dl>
<p>Marked range handles, as returned
@@ -523,6 +528,17 @@ <h2 id="keymaps">Keymaps</h2>
for example, <code>Shift-Ctrl-Space</code> would be a valid key
identifier.</p>
+ <p>Common example: map the Tab key to insert spaces instead of a tab
+ character.</p>
+
+ <pre data-lang="javascript">
+{
+ Tab: function(cm) {
+ var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
+ cm.replaceSelection(spaces, "end", "+input");
+ }
+}</pre>
+
<p>Alternatively, a character can be specified directly by
surrounding it in single quotes, for example <code>'$'</code>
or <code>'q'</code>. Due to limitations in the way browsers fire
@@ -777,6 +793,9 @@ <h3 id="api_selection">Cursor and selection methods</h3>
to <a href="#extendSelection"><code>extendSelection</code></a>
to leave the selection anchor in place.</dd>
+ <dt id="hasFocus"><code>cm.hasFocus() → bool</code></dt>
+ <dd>Tells you whether the editor currently has focus.</dd>
+
<dt id="findPosH"><code>cm.findPosH(start, amount, unit, visually) → object</code></dt>
<dd>Used to find the target position for horizontal cursor
motion. <code>start</code> is a <code>{line, ch}</code>
@@ -835,7 +854,7 @@ <h3 id="api_configuration">Configuration methods</h3>
searched. <code>mode</code> can be a <a href="#option_mode">mode
spec</a> or a mode object (an object with
a <a href="#token"><code>token</code></a> method).
- The <code>option</code> parameter is optional. If given it
+ The <code>options</code> parameter is optional. If given, it
should be an object. Currently, only the <code>opaque</code>
option is recognized. This defaults to off, but can be given to
allow the overlay styling, when not <code>null</code>, to
@@ -1159,12 +1178,14 @@ <h3 id="api_sizing">Sizing, scrolling and positioning methods</h3>
clientHeight}</code> object that represents the current scroll
position, the size of the scrollable area, and the size of the
visible area (minus scrollbars).</dd>
- <dt id="scrollIntoView"><code>cm.scrollIntoView(pos)</code></dt>
+ <dt id="scrollIntoView"><code>cm.scrollIntoView(pos, margin)</code></dt>
<dd>Scrolls the given element into view. <code>pos</code> may be
either a <code>{line, ch}</code> position, referring to a given
character, <code>null</code>, to refer to the cursor, or
a <code>{left, top, right, bottom}</code> object, in
- editor-local coordinates.</dd>
+ editor-local coordinates. The <code>margin</code> parameter is
+ optional. When given, it indicates the amount of pixels around
+ the given area that should be made visible as well.</dd>
<dt id="cursorCoords"><code>cm.cursorCoords(where, mode) → object</code></dt>
<dd>Returns an <code>{left, top, bottom}</code> object
@@ -1184,10 +1205,13 @@ <h3 id="api_sizing">Sizing, scrolling and positioning methods</h3>
it'll give the size of the whole character, rather than just the
position that the cursor would have when it would sit at that
position.</dd>
- <dt id="coordsChar"><code>cm.coordsChar(object) → pos</code></dt>
- <dd>Given an <code>{left, top}</code> object (in page coordinates),
- returns the <code>{line, ch}</code> position that corresponds to
- it.</dd>
+ <dt id="coordsChar"><code>cm.coordsChar(object, mode) → pos</code></dt>
+ <dd>Given an <code>{left, top}</code> object, returns
+ the <code>{line, ch}</code> position that corresponds to it. The
+ optional <code>mode</code> parameter determines relative to what