baidu_umeditor_rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +31 -0
  6. data/Rakefile +2 -0
  7. data/app/assets/javascripts/umeditor.js +2 -0
  8. data/baidu_umeditor_rails.gemspec +23 -0
  9. data/lib/baidu_umeditor_rails/asset_installer.rb +64 -0
  10. data/lib/baidu_umeditor_rails/asset_manifest.rb +120 -0
  11. data/lib/baidu_umeditor_rails/engine.rb +20 -0
  12. data/lib/baidu_umeditor_rails/version.rb +4 -0
  13. data/lib/baidu_umeditor_rails.rb +3 -0
  14. data/lib/tasks/baidu_umeditor_rails_tasks.rake +15 -0
  15. data/vendor/assets/javascripts/umeditor/dialogs/emotion/emotion.css +87 -0
  16. data/vendor/assets/javascripts/umeditor/dialogs/emotion/emotion.js +272 -0
  17. data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/0.gif +0 -0
  18. data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/bface.gif +0 -0
  19. data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/cface.gif +0 -0
  20. data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/fface.gif +0 -0
  21. data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/jxface2.gif +0 -0
  22. data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/neweditor-tab-bg.png +0 -0
  23. data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/tface.gif +0 -0
  24. data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/wface.gif +0 -0
  25. data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/yface.gif +0 -0
  26. data/vendor/assets/javascripts/umeditor/dialogs/formula/formula.css +32 -0
  27. data/vendor/assets/javascripts/umeditor/dialogs/formula/formula.html +212 -0
  28. data/vendor/assets/javascripts/umeditor/dialogs/formula/formula.js +124 -0
  29. data/vendor/assets/javascripts/umeditor/dialogs/formula/images/formula.png +0 -0
  30. data/vendor/assets/javascripts/umeditor/dialogs/image/image.css +42 -0
  31. data/vendor/assets/javascripts/umeditor/dialogs/image/image.js +445 -0
  32. data/vendor/assets/javascripts/umeditor/dialogs/image/images/close.png +0 -0
  33. data/vendor/assets/javascripts/umeditor/dialogs/image/images/upload1.png +0 -0
  34. data/vendor/assets/javascripts/umeditor/dialogs/image/images/upload2.png +0 -0
  35. data/vendor/assets/javascripts/umeditor/dialogs/link/link.js +73 -0
  36. data/vendor/assets/javascripts/umeditor/dialogs/map/map.html +148 -0
  37. data/vendor/assets/javascripts/umeditor/dialogs/map/map.js +263 -0
  38. data/vendor/assets/javascripts/umeditor/dialogs/video/images/center_focus.jpg +0 -0
  39. data/vendor/assets/javascripts/umeditor/dialogs/video/images/left_focus.jpg +0 -0
  40. data/vendor/assets/javascripts/umeditor/dialogs/video/images/none_focus.jpg +0 -0
  41. data/vendor/assets/javascripts/umeditor/dialogs/video/images/right_focus.jpg +0 -0
  42. data/vendor/assets/javascripts/umeditor/dialogs/video/video.css +59 -0
  43. data/vendor/assets/javascripts/umeditor/dialogs/video/video.js +282 -0
  44. data/vendor/assets/javascripts/umeditor/index.html +277 -0
  45. data/vendor/assets/javascripts/umeditor/lang/en/en.js +150 -0
  46. data/vendor/assets/javascripts/umeditor/lang/en/images/addimage.png +0 -0
  47. data/vendor/assets/javascripts/umeditor/lang/en/images/alldeletebtnhoverskin.png +0 -0
  48. data/vendor/assets/javascripts/umeditor/lang/en/images/alldeletebtnupskin.png +0 -0
  49. data/vendor/assets/javascripts/umeditor/lang/en/images/background.png +0 -0
  50. data/vendor/assets/javascripts/umeditor/lang/en/images/button.png +0 -0
  51. data/vendor/assets/javascripts/umeditor/lang/en/images/copy.png +0 -0
  52. data/vendor/assets/javascripts/umeditor/lang/en/images/deletedisable.png +0 -0
  53. data/vendor/assets/javascripts/umeditor/lang/en/images/deleteenable.png +0 -0
  54. data/vendor/assets/javascripts/umeditor/lang/en/images/imglabel.png +0 -0
  55. data/vendor/assets/javascripts/umeditor/lang/en/images/listbackground.png +0 -0
  56. data/vendor/assets/javascripts/umeditor/lang/en/images/localimage.png +0 -0
  57. data/vendor/assets/javascripts/umeditor/lang/en/images/music.png +0 -0
  58. data/vendor/assets/javascripts/umeditor/lang/en/images/rotateleftdisable.png +0 -0
  59. data/vendor/assets/javascripts/umeditor/lang/en/images/rotateleftenable.png +0 -0
  60. data/vendor/assets/javascripts/umeditor/lang/en/images/rotaterightdisable.png +0 -0
  61. data/vendor/assets/javascripts/umeditor/lang/en/images/rotaterightenable.png +0 -0
  62. data/vendor/assets/javascripts/umeditor/lang/en/images/upload.png +0 -0
  63. data/vendor/assets/javascripts/umeditor/lang/zh-cn/images/copy.png +0 -0
  64. data/vendor/assets/javascripts/umeditor/lang/zh-cn/images/imglabel.png +0 -0
  65. data/vendor/assets/javascripts/umeditor/lang/zh-cn/images/localimage.png +0 -0
  66. data/vendor/assets/javascripts/umeditor/lang/zh-cn/images/music.png +0 -0
  67. data/vendor/assets/javascripts/umeditor/lang/zh-cn/images/upload.png +0 -0
  68. data/vendor/assets/javascripts/umeditor/lang/zh-cn/zh-cn.js +150 -0
  69. data/vendor/assets/javascripts/umeditor/themes/default/css/umeditor.css +773 -0
  70. data/vendor/assets/javascripts/umeditor/themes/default/css/umeditor.min.css +8 -0
  71. data/vendor/assets/javascripts/umeditor/themes/default/images/caret.png +0 -0
  72. data/vendor/assets/javascripts/umeditor/themes/default/images/close.png +0 -0
  73. data/vendor/assets/javascripts/umeditor/themes/default/images/icons.gif +0 -0
  74. data/vendor/assets/javascripts/umeditor/themes/default/images/icons.png +0 -0
  75. data/vendor/assets/javascripts/umeditor/themes/default/images/ok.gif +0 -0
  76. data/vendor/assets/javascripts/umeditor/themes/default/images/pop-bg.png +0 -0
  77. data/vendor/assets/javascripts/umeditor/themes/default/images/spacer.gif +0 -0
  78. data/vendor/assets/javascripts/umeditor/themes/default/images/videologo.gif +0 -0
  79. data/vendor/assets/javascripts/umeditor/third-party/jquery.min.js +6 -0
  80. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/Symbola.eot +0 -0
  81. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/Symbola.otf +0 -0
  82. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/Symbola.svg +5102 -0
  83. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/Symbola.ttf +0 -0
  84. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/Symbola.woff +0 -0
  85. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/STIXFontLicense2010.txt +103 -0
  86. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneral-webfont.eot +0 -0
  87. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneral-webfont.svg +3318 -0
  88. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneral-webfont.ttf +0 -0
  89. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneral-webfont.woff +0 -0
  90. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbol-webfont.eot +0 -0
  91. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbol-webfont.svg +1738 -0
  92. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbol-webfont.ttf +0 -0
  93. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbol-webfont.woff +0 -0
  94. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbolita-webfont.eot +0 -0
  95. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbolita-webfont.svg +1137 -0
  96. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbolita-webfont.ttf +0 -0
  97. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbolita-webfont.woff +0 -0
  98. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralitalic-webfont.eot +0 -0
  99. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralitalic-webfont.svg +1089 -0
  100. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralitalic-webfont.ttf +0 -0
  101. data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralitalic-webfont.woff +0 -0
  102. data/vendor/assets/javascripts/umeditor/third-party/mathquill/mathquill.css +357 -0
  103. data/vendor/assets/javascripts/umeditor/third-party/mathquill/mathquill.js +3888 -0
  104. data/vendor/assets/javascripts/umeditor/third-party/mathquill/mathquill.min.js +2 -0
  105. data/vendor/assets/javascripts/umeditor/umeditor.config.js +249 -0
  106. data/vendor/assets/javascripts/umeditor/umeditor.js +10923 -0
  107. data/vendor/assets/javascripts/umeditor/umeditor.min.js +264 -0
  108. metadata +179 -0
@@ -0,0 +1,357 @@
1
+ /*
2
+ * LaTeX Math in pure HTML and CSS -- No images whatsoever
3
+ * v0.xa
4
+ * by Jay and Han
5
+ * Lesser GPL Licensed: http: //www.gnu.org/licenses/lgpl.html
6
+ *
7
+ * This file is automatically included by mathquill.js
8
+ *
9
+ */
10
+ @font-face {
11
+ font-family: Symbola;
12
+ src: url(font/Symbola.eot);
13
+ src: local("Symbola Regular"), local("Symbola"), url(font/Symbola.ttf) format("truetype"), url(font/Symbola.otf) format("opentype"), url(font/Symbola.svg#Symbola) format("svg");
14
+ }
15
+ .mathquill-editable {
16
+ display: -moz-inline-box;
17
+ display: inline-block;
18
+ white-space: pre-wrap;
19
+ }
20
+ .mathquill-editable .cursor {
21
+ border-left: 1px solid black;
22
+ margin-right: -1px;
23
+ position: relative;
24
+ z-index: 1;
25
+ padding: 0;
26
+ display: -moz-inline-box;
27
+ display: inline-block;
28
+ }
29
+ .mathquill-editable .cursor.blink {
30
+ visibility: hidden;
31
+ }
32
+ .mathquill-editable,
33
+ .mathquill-embedded-latex .mathquill-editable {
34
+ border: 1px solid gray;
35
+ padding: 2px;
36
+ }
37
+ .mathquill-embedded-latex .mathquill-editable {
38
+ margin: 1px;
39
+ }
40
+ .mathquill-editable.hasCursor,
41
+ .mathquill-editable .hasCursor {
42
+ -webkit-box-shadow: #68b4df 0 0 3px 2px;
43
+ -moz-box-shadow: #68b4df 0 0 3px 2px;
44
+ box-shadow: #68b4df 0 0 3px 2px;
45
+ }
46
+ .mathquill-editable .latex-command-input {
47
+ color: inherit;
48
+ font-family: "Courier New", monospace;
49
+ border: 1px solid gray;
50
+ padding-right: 1px;
51
+ margin-right: 1px;
52
+ margin-left: 2px;
53
+ }
54
+ .mathquill-editable .latex-command-input.empty {
55
+ background: transparent;
56
+ }
57
+ .mathquill-editable .latex-command-input.hasCursor {
58
+ border-color: ActiveBorder;
59
+ }
60
+ .mathquill-editable.empty:after,
61
+ .mathquill-textbox:after,
62
+ .mathquill-rendered-math .empty:after {
63
+ visibility: hidden;
64
+ content: 'c';
65
+ }
66
+ .mathquill-editable .cursor:only-child:after,
67
+ .mathquill-editable .textarea + .cursor:last-child:after {
68
+ visibility: hidden;
69
+ content: 'c';
70
+ }
71
+ .mathquill-textbox {
72
+ overflow-x: auto;
73
+ overflow-y: hidden;
74
+ }
75
+ .mathquill-rendered-math {
76
+ font-variant: normal;
77
+ font-weight: normal;
78
+ font-style: normal;
79
+ font-size: 115%;
80
+ line-height: 1;
81
+ display: -moz-inline-box;
82
+ display: inline-block;
83
+ }
84
+ .mathquill-rendered-math .non-leaf,
85
+ .mathquill-rendered-math .scaled {
86
+ display: -moz-inline-box;
87
+ display: inline-block;
88
+ }
89
+ .mathquill-rendered-math var,
90
+ .mathquill-rendered-math .text,
91
+ .mathquill-rendered-math .nonSymbola {
92
+ font-family: "Times New Roman", Symbola, serif;
93
+ line-height: .9;
94
+ }
95
+ .mathquill-rendered-math * {
96
+ font-size: inherit;
97
+ line-height: inherit;
98
+ margin: 0;
99
+ padding: 0;
100
+ border-color: black;
101
+ -webkit-user-select: none;
102
+ -moz-user-select: none;
103
+ user-select: none;
104
+ }
105
+ .mathquill-rendered-math .empty {
106
+ background: #ccc;
107
+ }
108
+ .mathquill-rendered-math.empty {
109
+ background: transparent;
110
+ }
111
+ .mathquill-rendered-math .text {
112
+ font-size: 87%;
113
+ }
114
+ .mathquill-rendered-math .font {
115
+ font: 1em "Times New Roman", Symbola, serif;
116
+ }
117
+ .mathquill-rendered-math .font * {
118
+ font-family: inherit;
119
+ font-style: inherit;
120
+ }
121
+ .mathquill-rendered-math b,
122
+ .mathquill-rendered-math b.font {
123
+ font-weight: bolder;
124
+ }
125
+ .mathquill-rendered-math var,
126
+ .mathquill-rendered-math i,
127
+ .mathquill-rendered-math i.font {
128
+ font-syle: italic;
129
+ }
130
+ .mathquill-rendered-math var.florin {
131
+ margin: 0 -0.1em;
132
+ }
133
+ .mathquill-rendered-math big {
134
+ font-size: 125%;
135
+ }
136
+ .mathquill-rendered-math .roman {
137
+ font-style: normal;
138
+ }
139
+ .mathquill-rendered-math .sans-serif {
140
+ font-family: sans-serif, Symbola, serif;
141
+ }
142
+ .mathquill-rendered-math .monospace {
143
+ font-family: monospace, Symbola, serif;
144
+ }
145
+ .mathquill-rendered-math .overline {
146
+ border-top: 1px solid black;
147
+ margin-top: 1px;
148
+ }
149
+ .mathquill-rendered-math .underline {
150
+ border-bottom: 1px solid black;
151
+ margin-bottom: 1px;
152
+ }
153
+ .mathquill-rendered-math .binary-operator {
154
+ padding: 0 0.2em;
155
+ display: -moz-inline-box;
156
+ display: inline-block;
157
+ }
158
+ .mathquill-rendered-math .unary-operator {
159
+ padding-left: 0.2em;
160
+ }
161
+ .mathquill-rendered-math sup,
162
+ .mathquill-rendered-math sub {
163
+ position: relative;
164
+ font-size: 90%;
165
+ }
166
+ .mathquill-rendered-math sup .binary-operator,
167
+ .mathquill-rendered-math sub .binary-operator {
168
+ padding: 0 .1em;
169
+ }
170
+ .mathquill-rendered-math sup .unary-operator,
171
+ .mathquill-rendered-math sub .unary-operator {
172
+ padding-left: .1em;
173
+ }
174
+ .mathquill-rendered-math sup.limit,
175
+ .mathquill-rendered-math sub.limit,
176
+ .mathquill-rendered-math sup.nthroot,
177
+ .mathquill-rendered-math sub.nthroot {
178
+ font-size: 80%;
179
+ }
180
+ .mathquill-rendered-math sup .fraction,
181
+ .mathquill-rendered-math sub .fraction {
182
+ font-size: 70%;
183
+ vertical-align: -0.4em;
184
+ }
185
+ .mathquill-rendered-math sup .numerator,
186
+ .mathquill-rendered-math sub .numerator {
187
+ padding-bottom: 0;
188
+ }
189
+ .mathquill-rendered-math sup .denominator,
190
+ .mathquill-rendered-math sub .denominator {
191
+ padding-top: 0;
192
+ }
193
+ .mathquill-rendered-math sup {
194
+ vertical-align: .5em;
195
+ }
196
+ .mathquill-rendered-math sup.limit,
197
+ .mathquill-rendered-math sup.nthroot {
198
+ vertical-align: 0.8em;
199
+ }
200
+ .mathquill-rendered-math sup.nthroot {
201
+ margin-right: -0.6em;
202
+ margin-left: .2em;
203
+ min-width: .5em;
204
+ }
205
+ .mathquill-rendered-math sub {
206
+ vertical-align: -0.4em;
207
+ }
208
+ .mathquill-rendered-math sub.limit {
209
+ vertical-align: -0.6em;
210
+ }
211
+ .mathquill-rendered-math .paren {
212
+ padding: 0 .1em;
213
+ vertical-align: bottom;
214
+ -webkit-transform-origin: bottom center;
215
+ -moz-transform-origin: bottom center;
216
+ -ms-transform-origin: bottom center;
217
+ -o-transform-origin: bottom center;
218
+ transform-origin: bottom center;
219
+ }
220
+ .mathquill-rendered-math .array {
221
+ vertical-align: middle;
222
+ text-align: center;
223
+ }
224
+ .mathquill-rendered-math .array > span {
225
+ display: block;
226
+ }
227
+ .mathquill-rendered-math .non-italicized-function {
228
+ font-family: Symbola, "Times New Roman", serif;
229
+ line-height: .9;
230
+ font-style: normal;
231
+ padding-right: .2em;
232
+ }
233
+ .mathquill-rendered-math .fraction {
234
+ font-size: 90%;
235
+ text-align: center;
236
+ vertical-align: -0.5em;
237
+ padding: 0 .2em;
238
+ }
239
+ .mathquill-rendered-math .fraction,
240
+ .mathquill-rendered-math x:-moz-any-link {
241
+ display: -moz-groupbox;
242
+ }
243
+ .mathquill-rendered-math .fraction,
244
+ .mathquill-rendered-math x:-moz-any-link,
245
+ .mathquill-rendered-math x:default {
246
+ display: inline-block;
247
+ }
248
+ .mathquill-rendered-math .numerator,
249
+ .mathquill-rendered-math .denominator {
250
+ display: block;
251
+ }
252
+ .mathquill-rendered-math .numerator {
253
+ padding: 0 0.1em;
254
+ margin-bottom: -0.1em;
255
+ }
256
+ .mathquill-rendered-math .denominator {
257
+ border-top: 1px solid;
258
+ float: right;
259
+ width: 100%;
260
+ padding: .1em .1em 0 .1em;
261
+ margin-right: -0.1em;
262
+ margin-left: -0.1em;
263
+ }
264
+ .mathquill-rendered-math .sqrt-prefix {
265
+ padding-top: 0;
266
+ position: relative;
267
+ top: .1em;
268
+ vertical-align: top;
269
+ -webkit-transform-origin: top;
270
+ -moz-transform-origin: top;
271
+ -ms-transform-origin: top;
272
+ -o-transform-origin: top;
273
+ transform-origin: top;
274
+ }
275
+ .mathquill-rendered-math .sqrt-stem {
276
+ border-top: 1px solid;
277
+ margin-top: 1px;
278
+ padding-left: .15em;
279
+ padding-right: .2em;
280
+ margin-right: .1em;
281
+ }
282
+ .mathquill-rendered-math .vector-prefix {
283
+ display: block;
284
+ text-align: center;
285
+ line-height: .25em;
286
+ margin-bottom: -0.1em;
287
+ font-size: 0.75em;
288
+ }
289
+ .mathquill-rendered-math .vector-stem {
290
+ display: block;
291
+ }
292
+ .mathquill-rendered-math,
293
+ .mathquill-rendered-math .mathquill-editable {
294
+ cursor: text;
295
+ font-family: Symbola, "Times New Roman", serif;
296
+ }
297
+ .mathquill-rendered-math .selection,
298
+ .mathquill-editable .selection,
299
+ .mathquill-rendered-math .selection .non-leaf,
300
+ .mathquill-editable .selection .non-leaf,
301
+ .mathquill-rendered-math .selection .scaled,
302
+ .mathquill-editable .selection .scaled {
303
+ background: #B4D5FE !important;
304
+ background: Highlight !important;
305
+ color: HighlightText;
306
+ border-color: HighlightText;
307
+ }
308
+ .mathquill-rendered-math .selection .matrixed,
309
+ .mathquill-editable .selection .matrixed {
310
+ background: #39F !important;
311
+ }
312
+ .mathquill-rendered-math .selection .matrixed-container,
313
+ .mathquill-editable .selection .matrixed-container {
314
+ filter: progid:DXImageTransform.Microsoft.Chroma(color='#3399FF') !important;
315
+ }
316
+ .mathquill-rendered-math .selection.blur,
317
+ .mathquill-editable .selection.blur,
318
+ .mathquill-rendered-math .selection.blur .non-leaf,
319
+ .mathquill-editable .selection.blur .non-leaf,
320
+ .mathquill-rendered-math .selection.blur .scaled,
321
+ .mathquill-editable .selection.blur .scaled,
322
+ .mathquill-rendered-math .selection.blur .matrixed,
323
+ .mathquill-editable .selection.blur .matrixed {
324
+ background: #D4D4D4 !important;
325
+ color: black;
326
+ border-color: black;
327
+ }
328
+ .mathquill-rendered-math .selection.blur .matrixed-container,
329
+ .mathquill-editable .selection.blur .matrixed-container {
330
+ filter: progid:DXImageTransform.Microsoft.Chroma(color='#D4D4D4') !important;
331
+ }
332
+ .mathquill-editable .textarea,
333
+ .mathquill-rendered-math .textarea {
334
+ position: relative;
335
+ -webkit-user-select: text;
336
+ -moz-user-select: text;
337
+ user-select: text;
338
+ }
339
+ .mathquill-editable .textarea textarea,
340
+ .mathquill-rendered-math .textarea textarea,
341
+ .mathquill-editable .selectable,
342
+ .mathquill-rendered-math .selectable {
343
+ -webkit-user-select: text;
344
+ -moz-user-select: text;
345
+ user-select: text;
346
+ position: absolute;
347
+ clip: rect(1em 1em 1em 1em);
348
+ }
349
+ .mathquill-rendered-math .matrixed {
350
+ background: white;
351
+ display: -moz-inline-box;
352
+ display: inline-block;
353
+ }
354
+ .mathquill-rendered-math .matrixed-container {
355
+ filter: progid:DXImageTransform.Microsoft.Chroma(color='white');
356
+ margin-top: -0.1em;
357
+ }
@@ -0,0 +1,3888 @@
1
+ /**
2
+ * Copyleft 2010-2011 Jay and Han (laughinghan@gmail.com)
3
+ * under the GNU Lesser General Public License
4
+ * http://www.gnu.org/licenses/lgpl.html
5
+ * Project Website: http://mathquill.com
6
+ */
7
+
8
+ (function() {
9
+
10
+ var jQuery = window.jQuery,
11
+ undefined,
12
+ _, //temp variable of prototypes
13
+ mqCmdId = 'mathquill-command-id',
14
+ mqBlockId = 'mathquill-block-id',
15
+ min = Math.min,
16
+ max = Math.max;
17
+
18
+ var __slice = [].slice;
19
+
20
+ function noop() {}
21
+
22
+ /**
23
+ * sugar to make defining lots of commands easier.
24
+ * TODO: rethink this.
25
+ */
26
+ function bind(cons /*, args... */) {
27
+ var args = __slice.call(arguments, 1);
28
+ return function() {
29
+ return cons.apply(this, args);
30
+ };
31
+ }
32
+
33
+ /**
34
+ * a development-only debug method. This definition and all
35
+ * calls to `pray` will be stripped from the minified
36
+ * build of mathquill.
37
+ *
38
+ * This function must be called by name to be removed
39
+ * at compile time. Do not define another function
40
+ * with the same name, and only call this function by
41
+ * name.
42
+ */
43
+ function pray(message, cond) {
44
+ if (!cond) throw new Error('prayer failed: '+message);
45
+ }
46
+ var P = (function(prototype, ownProperty, undefined) {
47
+ // helper functions that also help minification
48
+ function isObject(o) { return typeof o === 'object'; }
49
+ function isFunction(f) { return typeof f === 'function'; }
50
+
51
+ // a function that gets reused to make uninitialized objects
52
+ function BareConstructor() {}
53
+
54
+ function P(_superclass /* = Object */, definition) {
55
+ // handle the case where no superclass is given
56
+ if (definition === undefined) {
57
+ definition = _superclass;
58
+ _superclass = Object;
59
+ }
60
+
61
+ // C is the class to be returned.
62
+ //
63
+ // It delegates to instantiating an instance of `Bare`, so that it
64
+ // will always return a new instance regardless of the calling
65
+ // context.
66
+ //
67
+ // TODO: the Chrome inspector shows all created objects as `C`
68
+ // rather than `Object`. Setting the .name property seems to
69
+ // have no effect. Is there a way to override this behavior?
70
+ function C() {
71
+ var self = new Bare;
72
+ if (isFunction(self.init)) self.init.apply(self, arguments);
73
+ return self;
74
+ }
75
+
76
+ // C.Bare is a class with a noop constructor. Its prototype is the
77
+ // same as C, so that instances of C.Bare are also instances of C.
78
+ // New objects can be allocated without initialization by calling
79
+ // `new MyClass.Bare`.
80
+ function Bare() {}
81
+ C.Bare = Bare;
82
+
83
+ // Set up the prototype of the new class.
84
+ var _super = BareConstructor[prototype] = _superclass[prototype];
85
+ var proto = Bare[prototype] = C[prototype] = new BareConstructor;
86
+
87
+ // other variables, as a minifier optimization
88
+ var extensions;
89
+
90
+
91
+ // set the constructor property on the prototype, for convenience
92
+ proto.constructor = C;
93
+
94
+ C.mixin = function(def) {
95
+ Bare[prototype] = C[prototype] = P(C, def)[prototype];
96
+ return C;
97
+ }
98
+
99
+ return (C.open = function(def) {
100
+ extensions = {};
101
+
102
+ if (isFunction(def)) {
103
+ // call the defining function with all the arguments you need
104
+ // extensions captures the return value.
105
+ extensions = def.call(C, proto, _super, C, _superclass);
106
+ }
107
+ else if (isObject(def)) {
108
+ // if you passed an object instead, we'll take it
109
+ extensions = def;
110
+ }
111
+
112
+ // ...and extend it
113
+ if (isObject(extensions)) {
114
+ for (var ext in extensions) {
115
+ if (ownProperty.call(extensions, ext)) {
116
+ proto[ext] = extensions[ext];
117
+ }
118
+ }
119
+ }
120
+
121
+ // if there's no init, we assume we're inheriting a non-pjs class, so
122
+ // we default to applying the superclass's constructor.
123
+ if (!isFunction(proto.init)) {
124
+ proto.init = _superclass;
125
+ }
126
+
127
+ return C;
128
+ })(definition);
129
+ }
130
+
131
+ // ship it
132
+ return P;
133
+
134
+ // as a minifier optimization, we've closured in a few helper functions
135
+ // and the string 'prototype' (C[p] is much shorter than C.prototype)
136
+ })('prototype', ({}).hasOwnProperty);
137
+ /*************************************************
138
+ * Textarea Manager
139
+ *
140
+ * An abstraction layer wrapping the textarea in
141
+ * an object with methods to manipulate and listen
142
+ * to events on, that hides all the nasty cross-
143
+ * browser incompatibilities behind a uniform API.
144
+ *
145
+ * Design goal: This is a *HARD* internal
146
+ * abstraction barrier. Cross-browser
147
+ * inconsistencies are not allowed to leak through
148
+ * and be dealt with by event handlers. All future
149
+ * cross-browser issues that arise must be dealt
150
+ * with here, and if necessary, the API updated.
151
+ *
152
+ * Organization:
153
+ * - key values map and stringify()
154
+ * - manageTextarea()
155
+ * + defer() and flush()
156
+ * + event handler logic
157
+ * + attach event handlers and export methods
158
+ ************************************************/
159
+
160
+ var manageTextarea = (function() {
161
+ // The following [key values][1] map was compiled from the
162
+ // [DOM3 Events appendix section on key codes][2] and
163
+ // [a widely cited report on cross-browser tests of key codes][3],
164
+ // except for 10: 'Enter', which I've empirically observed in Safari on iOS
165
+ // and doesn't appear to conflict with any other known key codes.
166
+ //
167
+ // [1]: http://www.w3.org/TR/2012/WD-DOM-Level-3-Events-20120614/#keys-keyvalues
168
+ // [2]: http://www.w3.org/TR/2012/WD-DOM-Level-3-Events-20120614/#fixed-virtual-key-codes
169
+ // [3]: http://unixpapa.com/js/key.html
170
+ var KEY_VALUES = {
171
+ 8: 'Backspace',
172
+ 9: 'Tab',
173
+
174
+ 10: 'Enter', // for Safari on iOS
175
+
176
+ 13: 'Enter',
177
+
178
+ 16: 'Shift',
179
+ 17: 'Control',
180
+ 18: 'Alt',
181
+ 20: 'CapsLock',
182
+
183
+ 27: 'Esc',
184
+
185
+ 32: 'Spacebar',
186
+
187
+ 33: 'PageUp',
188
+ 34: 'PageDown',
189
+ 35: 'End',
190
+ 36: 'Home',
191
+
192
+ 37: 'Left',
193
+ 38: 'Up',
194
+ 39: 'Right',
195
+ 40: 'Down',
196
+
197
+ 45: 'Insert',
198
+
199
+ 46: 'Del',
200
+
201
+ 144: 'NumLock'
202
+ };
203
+
204
+ // To the extent possible, create a normalized string representation
205
+ // of the key combo (i.e., key code and modifier keys).
206
+ function stringify(evt) {
207
+ var which = evt.which || evt.keyCode;
208
+ var keyVal = KEY_VALUES[which];
209
+ var key;
210
+ var modifiers = [];
211
+
212
+ if (evt.ctrlKey) modifiers.push('Ctrl');
213
+ if (evt.originalEvent && evt.originalEvent.metaKey) modifiers.push('Meta');
214
+ if (evt.altKey) modifiers.push('Alt');
215
+ if (evt.shiftKey) modifiers.push('Shift');
216
+
217
+ key = keyVal || String.fromCharCode(which);
218
+
219
+ if (!modifiers.length && !keyVal) return key;
220
+
221
+ modifiers.push(key);
222
+ return modifiers.join('-');
223
+ }
224
+
225
+ // create a textarea manager that calls callbacks at useful times
226
+ // and exports useful public methods
227
+ return function manageTextarea(el, opts) {
228
+ var keydown = null;
229
+ var keypress = null;
230
+
231
+ if (!opts) opts = {};
232
+ var textCallback = opts.text || noop;
233
+ var keyCallback = opts.key || noop;
234
+ var pasteCallback = opts.paste || noop;
235
+ var onCut = opts.cut || noop;
236
+
237
+ var textarea = jQuery(el);
238
+ var target = jQuery(opts.container || textarea);
239
+
240
+ // checkTextareaFor() is called after keypress or paste events to
241
+ // say "Hey, I think something was just typed" or "pasted" (resp.),
242
+ // so that at all subsequent opportune times (next event or timeout),
243
+ // will check for expected typed or pasted text.
244
+ // Need to check repeatedly because #135: in Safari 5.1 (at least),
245
+ // after selecting something and then typing, the textarea is
246
+ // incorrectly reported as selected during the input event (but not
247
+ // subsequently).
248
+ var checkTextarea = noop, timeoutId;
249
+ function checkTextareaFor(checker) {
250
+ checkTextarea = checker;
251
+ clearTimeout(timeoutId);
252
+ timeoutId = setTimeout(checker);
253
+ }
254
+ target.bind('keydown keypress input keyup focusout paste', function() { checkTextarea(); });
255
+
256
+
257
+ // -*- public methods -*- //
258
+ function select(text) {
259
+ // check textarea at least once/one last time before munging (so
260
+ // no race condition if selection happens after keypress/paste but
261
+ // before checkTextarea), then never again ('cos it's been munged)
262
+ checkTextarea();
263
+ checkTextarea = noop;
264
+ clearTimeout(timeoutId);
265
+
266
+ textarea.val(text);
267
+ if (text) textarea[0].select();
268
+ }
269
+
270
+ // -*- helper subroutines -*- //
271
+
272
+ // Determine whether there's a selection in the textarea.
273
+ // This will always return false in IE < 9, which don't support
274
+ // HTMLTextareaElement::selection{Start,End}.
275
+ function hasSelection() {
276
+ var dom = textarea[0];
277
+
278
+ if (!('selectionStart' in dom)) return false;
279
+ return dom.selectionStart !== dom.selectionEnd;
280
+ }
281
+
282
+ function popText(callback) {
283
+ var text = textarea.val();
284
+ textarea.val('');
285
+ if (text) callback(text);
286
+ }
287
+
288
+ function handleKey() {
289
+ keyCallback(stringify(keydown), keydown);
290
+ }
291
+
292
+ // -*- event handlers -*- //
293
+ function onKeydown(e) {
294
+ keydown = e;
295
+ keypress = null;
296
+
297
+ handleKey();
298
+ }
299
+
300
+ function onKeypress(e) {
301
+ // call the key handler for repeated keypresses.
302
+ // This excludes keypresses that happen directly
303
+ // after keydown. In that case, there will be
304
+ // no previous keypress, so we skip it here
305
+ if (keydown && keypress) handleKey();
306
+
307
+ keypress = e;
308
+
309
+ checkTextareaFor(typedText);
310
+ }
311
+ function typedText() {
312
+ // If there is a selection, the contents of the textarea couldn't
313
+ // possibly have just been typed in.
314
+ // This happens in browsers like Firefox and Opera that fire
315
+ // keypress for keystrokes that are not text entry and leave the
316
+ // selection in the textarea alone, such as Ctrl-C.
317
+ // Note: we assume that browsers that don't support hasSelection()
318
+ // also never fire keypress on keystrokes that are not text entry.
319
+ // This seems reasonably safe because:
320
+ // - all modern browsers including IE 9+ support hasSelection(),
321
+ // making it extremely unlikely any browser besides IE < 9 won't
322
+ // - as far as we know IE < 9 never fires keypress on keystrokes
323
+ // that aren't text entry, which is only as reliable as our
324
+ // tests are comprehensive, but the IE < 9 way to do
325
+ // hasSelection() is poorly documented and is also only as
326
+ // reliable as our tests are comprehensive
327
+ // If anything like #40 or #71 is reported in IE < 9, see
328
+ // b1318e5349160b665003e36d4eedd64101ceacd8
329
+ if (hasSelection()) return;
330
+
331
+ popText(textCallback);
332
+ }
333
+
334
+ function onBlur() { keydown = keypress = null; }
335
+
336
+ function onPaste(e) {
337
+ // browsers are dumb.
338
+ //
339
+ // In Linux, middle-click pasting causes onPaste to be called,
340
+ // when the textarea is not necessarily focused. We focus it
341
+ // here to ensure that the pasted text actually ends up in the
342
+ // textarea.
343
+ //
344
+ // It's pretty nifty that by changing focus in this handler,
345
+ // we can change the target of the default action. (This works
346
+ // on keydown too, FWIW).
347
+ //
348
+ // And by nifty, we mean dumb (but useful sometimes).
349
+ textarea.focus();
350
+
351
+ checkTextareaFor(pastedText);
352
+ }
353
+ function pastedText() {
354
+ popText(pasteCallback);
355
+ }
356
+
357
+ // -*- attach event handlers -*- //
358
+ target.bind({
359
+ keydown: onKeydown,
360
+ keypress: onKeypress,
361
+ focusout: onBlur,
362
+ cut: onCut,
363
+ paste: onPaste
364
+ });
365
+
366
+ // -*- export public methods -*- //
367
+ return {
368
+ select: select
369
+ };
370
+ };
371
+ }());
372
+ var Parser = P(function(_, _super, Parser) {
373
+ // The Parser object is a wrapper for a parser function.
374
+ // Externally, you use one to parse a string by calling
375
+ // var result = SomeParser.parse('Me Me Me! Parse Me!');
376
+ // You should never call the constructor, rather you should
377
+ // construct your Parser from the base parsers and the
378
+ // parser combinator methods.
379
+
380
+ function parseError(stream, message) {
381
+ if (stream) {
382
+ stream = "'"+stream+"'";
383
+ }
384
+ else {
385
+ stream = 'EOF';
386
+ }
387
+
388
+ throw 'Parse Error: '+message+' at '+stream;
389
+ }
390
+
391
+ _.init = function(body) { this._ = body; };
392
+
393
+ _.parse = function(stream) {
394
+ return this.skip(eof)._(stream, success, parseError);
395
+
396
+ function success(stream, result) { return result; }
397
+ };
398
+
399
+ // -*- primitive combinators -*- //
400
+ _.or = function(alternative) {
401
+ pray('or is passed a parser', alternative instanceof Parser);
402
+
403
+ var self = this;
404
+
405
+ return Parser(function(stream, onSuccess, onFailure) {
406
+ return self._(stream, onSuccess, failure);
407
+
408
+ function failure(newStream) {
409
+ return alternative._(stream, onSuccess, onFailure);
410
+ }
411
+ });
412
+ };
413
+
414
+ _.then = function(next) {
415
+ var self = this;
416
+
417
+ return Parser(function(stream, onSuccess, onFailure) {
418
+ return self._(stream, success, onFailure);
419
+
420
+ function success(newStream, result) {
421
+ var nextParser = (next instanceof Parser ? next : next(result));
422
+ pray('a parser is returned', nextParser instanceof Parser);
423
+ return nextParser._(newStream, onSuccess, onFailure);
424
+ }
425
+ });
426
+ };
427
+
428
+ // -*- optimized iterative combinators -*- //
429
+ _.many = function() {
430
+ var self = this;
431
+
432
+ return Parser(function(stream, onSuccess, onFailure) {
433
+ var xs = [];
434
+ while (self._(stream, success, failure));
435
+ return onSuccess(stream, xs);
436
+
437
+ function success(newStream, x) {
438
+ stream = newStream;
439
+ xs.push(x);
440
+ return true;
441
+ }
442
+
443
+ function failure() {
444
+ return false;
445
+ }
446
+ });
447
+ };
448
+
449
+ _.times = function(min, max) {
450
+ if (arguments.length < 2) max = min;
451
+ var self = this;
452
+
453
+ return Parser(function(stream, onSuccess, onFailure) {
454
+ var xs = [];
455
+ var result = true;
456
+ var failure;
457
+
458
+ for (var i = 0; i < min; i += 1) {
459
+ result = self._(stream, success, firstFailure);
460
+ if (!result) return onFailure(stream, failure);
461
+ }
462
+
463
+ for (; i < max && result; i += 1) {
464
+ result = self._(stream, success, secondFailure);
465
+ }
466
+
467
+ return onSuccess(stream, xs);
468
+
469
+ function success(newStream, x) {
470
+ xs.push(x);
471
+ stream = newStream;
472
+ return true;
473
+ }
474
+
475
+ function firstFailure(newStream, msg) {
476
+ failure = msg;
477
+ stream = newStream;
478
+ return false;
479
+ }
480
+
481
+ function secondFailure(newStream, msg) {
482
+ return false;
483
+ }
484
+ });
485
+ };
486
+
487
+ // -*- higher-level combinators -*- //
488
+ _.result = function(res) { return this.then(succeed(res)); };
489
+ _.atMost = function(n) { return this.times(0, n); };
490
+ _.atLeast = function(n) {
491
+ var self = this;
492
+ return self.times(n).then(function(start) {
493
+ return self.many().map(function(end) {
494
+ return start.concat(end);
495
+ });
496
+ });
497
+ };
498
+
499
+ _.map = function(fn) {
500
+ return this.then(function(result) { return succeed(fn(result)); });
501
+ };
502
+
503
+ _.skip = function(two) {
504
+ return this.then(function(result) { return two.result(result); });
505
+ };
506
+
507
+ // -*- primitive parsers -*- //
508
+ var string = this.string = function(str) {
509
+ var len = str.length;
510
+ var expected = "expected '"+str+"'";
511
+
512
+ return Parser(function(stream, onSuccess, onFailure) {
513
+ var head = stream.slice(0, len);
514
+
515
+ if (head === str) {
516
+ return onSuccess(stream.slice(len), head);
517
+ }
518
+ else {
519
+ return onFailure(stream, expected);
520
+ }
521
+ });
522
+ };
523
+
524
+ var regex = this.regex = function(re) {
525
+ pray('regexp parser is anchored', re.toString().charAt(1) === '^');
526
+
527
+ var expected = 'expected '+re;
528
+
529
+ return Parser(function(stream, onSuccess, onFailure) {
530
+ var match = re.exec(stream);
531
+
532
+ if (match) {
533
+ var result = match[0];
534
+ return onSuccess(stream.slice(result.length), result);
535
+ }
536
+ else {
537
+ return onFailure(stream, expected);
538
+ }
539
+ });
540
+ };
541
+
542
+ var succeed = Parser.succeed = function(result) {
543
+ return Parser(function(stream, onSuccess) {
544
+ return onSuccess(stream, result);
545
+ });
546
+ };
547
+
548
+ var fail = Parser.fail = function(msg) {
549
+ return Parser(function(stream, _, onFailure) {
550
+ return onFailure(stream, msg);
551
+ });
552
+ };
553
+
554
+ var letter = Parser.letter = regex(/^[a-z]/i);
555
+ var letters = Parser.letters = regex(/^[a-z]*/i);
556
+ var digit = Parser.digit = regex(/^[0-9]/);
557
+ var digits = Parser.digits = regex(/^[0-9]*/);
558
+ var whitespace = Parser.whitespace = regex(/^\s+/);
559
+ var optWhitespace = Parser.optWhitespace = regex(/^\s*/);
560
+
561
+ var any = Parser.any = Parser(function(stream, onSuccess, onFailure) {
562
+ if (!stream) return onFailure(stream, 'expected any character');
563
+
564
+ return onSuccess(stream.slice(1), stream.charAt(0));
565
+ });
566
+
567
+ var all = Parser.all = Parser(function(stream, onSuccess, onFailure) {
568
+ return onSuccess('', stream);
569
+ });
570
+
571
+ var eof = Parser.eof = Parser(function(stream, onSuccess, onFailure) {
572
+ if (stream) return onFailure(stream, 'expected EOF');
573
+
574
+ return onSuccess(stream, stream);
575
+ });
576
+ });
577
+ /*************************************************
578
+ * Base classes of the MathQuill virtual DOM tree
579
+ *
580
+ * Only doing tree node manipulation via these
581
+ * adopt/ disown methods guarantees well-formedness
582
+ * of the tree.
583
+ ************************************************/
584
+
585
+ // L = 'left'
586
+ // R = 'right'
587
+ //
588
+ // the contract is that they can be used as object properties
589
+ // and (-L) === R, and (-R) === L.
590
+ var L = -1;
591
+ var R = 1;
592
+
593
+ function prayDirection(dir) {
594
+ pray('a direction was passed', dir === L || dir === R);
595
+ }
596
+
597
+ /**
598
+ * Tiny extension of jQuery adding directionalized DOM manipulation methods.
599
+ *
600
+ * Funny how Pjs v3 almost just works with `jQuery.fn.init`.
601
+ *
602
+ * jQuery features that don't work on $:
603
+ * - jQuery.*, like jQuery.ajax, obviously (Pjs doesn't and shouldn't
604
+ * copy constructor properties)
605
+ *
606
+ * - jQuery(function), the shortcut for `jQuery(document).ready(function)`,
607
+ * because `jQuery.fn.init` is idiosyncratic and Pjs doing, essentially,
608
+ * `jQuery.fn.init.apply(this, arguments)` isn't quite right, you need:
609
+ *
610
+ * _.init = function(s, c) { jQuery.fn.init.call(this, s, c, $(document)); };
611
+ *
612
+ * if you actually give a shit (really, don't bother),
613
+ * see https://github.com/jquery/jquery/blob/1.7.2/src/core.js#L889
614
+ *
615
+ * - jQuery(selector), because jQuery translates that to
616
+ * `jQuery(document).find(selector)`, but Pjs doesn't (should it?) let
617
+ * you override the result of a constructor call
618
+ * + note that because of the jQuery(document) shortcut-ness, there's also
619
+ * the 3rd-argument-needs-to-be-`$(document)` thing above, but the fix
620
+ * for that (as can be seen above) is really easy. This problem requires
621
+ * a way more intrusive fix
622
+ *
623
+ * And that's it! Everything else just magically works because jQuery internally
624
+ * uses `this.constructor()` everywhere (hence calling `$`), but never ever does
625
+ * `this.constructor.find` or anything like that, always doing `jQuery.find`.
626
+ */
627
+ var $ = P(jQuery, function(_) {
628
+ _.insDirOf = function(dir, el) {
629
+ return dir === L ?
630
+ this.insertBefore(el.first()) : this.insertAfter(el.last());
631
+ };
632
+ _.insAtDirEnd = function(dir, el) {
633
+ return dir === L ? this.prependTo(el) : this.appendTo(el);
634
+ };
635
+ });
636
+
637
+ var Point = P(function(_) {
638
+ _.parent = 0;
639
+ _[L] = 0;
640
+ _[R] = 0;
641
+
642
+ _.init = function(parent, leftward, rightward) {
643
+ this.parent = parent;
644
+ this[L] = leftward;
645
+ this[R] = rightward;
646
+ };
647
+ });
648
+
649
+ /**
650
+ * MathQuill virtual-DOM tree-node abstract base class
651
+ */
652
+ var Node = P(function(_) {
653
+ _[L] = 0;
654
+ _[R] = 0
655
+ _.parent = 0;
656
+
657
+ _.init = function() {
658
+ this.ends = {};
659
+ this.ends[L] = 0;
660
+ this.ends[R] = 0;
661
+ };
662
+
663
+ _.children = function() {
664
+ return Fragment(this.ends[L], this.ends[R]);
665
+ };
666
+
667
+ _.eachChild = function(fn) {
668
+ return this.children().each(fn);
669
+ };
670
+
671
+ _.foldChildren = function(fold, fn) {
672
+ return this.children().fold(fold, fn);
673
+ };
674
+
675
+ _.adopt = function(parent, leftward, rightward) {
676
+ Fragment(this, this).adopt(parent, leftward, rightward);
677
+ return this;
678
+ };
679
+
680
+ _.disown = function() {
681
+ Fragment(this, this).disown();
682
+ return this;
683
+ };
684
+ });
685
+
686
+ /**
687
+ * An entity outside the virtual tree with one-way pointers (so it's only a
688
+ * "view" of part of the tree, not an actual node/entity in the tree) that
689
+ * delimits a doubly-linked list of sibling nodes.
690
+ * It's like a fanfic love-child between HTML DOM DocumentFragment and the Range
691
+ * classes: like DocumentFragment, its contents must be sibling nodes
692
+ * (unlike Range, whose contents are arbitrary contiguous pieces of subtrees),
693
+ * but like Range, it has only one-way pointers to its contents, its contents
694
+ * have no reference to it and in fact may still be in the visible tree (unlike
695
+ * DocumentFragment, whose contents must be detached from the visible tree
696
+ * and have their 'parent' pointers set to the DocumentFragment).
697
+ */
698
+ var Fragment = P(function(_) {
699
+ _.init = function(leftEnd, rightEnd) {
700
+ pray('no half-empty fragments', !leftEnd === !rightEnd);
701
+
702
+ this.ends = {};
703
+
704
+ if (!leftEnd) return;
705
+
706
+ pray('left end node is passed to Fragment', leftEnd instanceof Node);
707
+ pray('right end node is passed to Fragment', rightEnd instanceof Node);
708
+ pray('leftEnd and rightEnd have the same parent',
709
+ leftEnd.parent === rightEnd.parent);
710
+
711
+ this.ends[L] = leftEnd;
712
+ this.ends[R] = rightEnd;
713
+ };
714
+
715
+ function prayWellFormed(parent, leftward, rightward) {
716
+ pray('a parent is always present', parent);
717
+ pray('leftward is properly set up', (function() {
718
+ // either it's empty and `rightward` is the left end child (possibly empty)
719
+ if (!leftward) return parent.ends[L] === rightward;
720
+
721
+ // or it's there and its [R] and .parent are properly set up
722
+ return leftward[R] === rightward && leftward.parent === parent;
723
+ })());
724
+
725
+ pray('rightward is properly set up', (function() {
726
+ // either it's empty and `leftward` is the right end child (possibly empty)
727
+ if (!rightward) return parent.ends[R] === leftward;
728
+
729
+ // or it's there and its [L] and .parent are properly set up
730
+ return rightward[L] === leftward && rightward.parent === parent;
731
+ })());
732
+ }
733
+
734
+ _.adopt = function(parent, leftward, rightward) {
735
+ prayWellFormed(parent, leftward, rightward);
736
+
737
+ var self = this;
738
+ self.disowned = false;
739
+
740
+ var leftEnd = self.ends[L];
741
+ if (!leftEnd) return this;
742
+
743
+ var rightEnd = self.ends[R];
744
+
745
+ if (leftward) {
746
+ // NB: this is handled in the ::each() block
747
+ // leftward[R] = leftEnd
748
+ } else {
749
+ parent.ends[L] = leftEnd;
750
+ }
751
+
752
+ if (rightward) {
753
+ rightward[L] = rightEnd;
754
+ } else {
755
+ parent.ends[R] = rightEnd;
756
+ }
757
+
758
+ self.ends[R][R] = rightward;
759
+
760
+ self.each(function(el) {
761
+ el[L] = leftward;
762
+ el.parent = parent;
763
+ if (leftward) leftward[R] = el;
764
+
765
+ leftward = el;
766
+ });
767
+
768
+ return self;
769
+ };
770
+
771
+ _.disown = function() {
772
+ var self = this;
773
+ var leftEnd = self.ends[L];
774
+
775
+ // guard for empty and already-disowned fragments
776
+ if (!leftEnd || self.disowned) return self;
777
+
778
+ self.disowned = true;
779
+
780
+ var rightEnd = self.ends[R]
781
+ var parent = leftEnd.parent;
782
+
783
+ prayWellFormed(parent, leftEnd[L], leftEnd);
784
+ prayWellFormed(parent, rightEnd, rightEnd[R]);
785
+
786
+ if (leftEnd[L]) {
787
+ leftEnd[L][R] = rightEnd[R];
788
+ } else {
789
+ parent.ends[L] = rightEnd[R];
790
+ }
791
+
792
+ if (rightEnd[R]) {
793
+ rightEnd[R][L] = leftEnd[L];
794
+ } else {
795
+ parent.ends[R] = leftEnd[L];
796
+ }
797
+
798
+ return self;
799
+ };
800
+
801
+ _.each = function(fn) {
802
+ var self = this;
803
+ var el = self.ends[L];
804
+ if (!el) return self;
805
+
806
+ for (;el !== self.ends[R][R]; el = el[R]) {
807
+ if (fn.call(self, el) === false) break;
808
+ }
809
+
810
+ return self;
811
+ };
812
+
813
+ _.fold = function(fold, fn) {
814
+ this.each(function(el) {
815
+ fold = fn.call(this, fold, el);
816
+ });
817
+
818
+ return fold;
819
+ };
820
+ });
821
+ /*************************************************
822
+ * Abstract classes of math blocks and commands.
823
+ ************************************************/
824
+
825
+ var uuid = (function() {
826
+ var id = 0;
827
+
828
+ return function() { return id += 1; };
829
+ })();
830
+
831
+ /**
832
+ * Math tree node base class.
833
+ * Some math-tree-specific extensions to Node.
834
+ * Both MathBlock's and MathCommand's descend from it.
835
+ */
836
+ var MathElement = P(Node, function(_, _super) {
837
+ _.init = function(obj) {
838
+ _super.init.call(this);
839
+ this.id = uuid();
840
+ MathElement[this.id] = this;
841
+ };
842
+
843
+ _.toString = function() {
844
+ return '[MathElement '+this.id+']';
845
+ };
846
+
847
+ _.bubble = function(event /*, args... */) {
848
+ var args = __slice.call(arguments, 1);
849
+
850
+ for (var ancestor = this; ancestor; ancestor = ancestor.parent) {
851
+ var res = ancestor[event] && ancestor[event].apply(ancestor, args);
852
+ if (res === false) break;
853
+ }
854
+
855
+ return this;
856
+ };
857
+
858
+ _.postOrder = function(fn /*, args... */) {
859
+ var args = __slice.call(arguments, 1);
860
+
861
+ if (typeof fn === 'string') {
862
+ var methodName = fn;
863
+ fn = function(el) {
864
+ if (methodName in el) el[methodName].apply(el, args);
865
+ };
866
+ }
867
+
868
+ (function recurse(desc) {
869
+ desc.eachChild(recurse);
870
+ fn(desc);
871
+ })(this);
872
+ };
873
+
874
+ _.jQ = $();
875
+ _.jQadd = function(jQ) { this.jQ = this.jQ.add(jQ); };
876
+
877
+ this.jQize = function(html) {
878
+ // Sets the .jQ of the entire math subtree rooted at this command.
879
+ // Expects .createBlocks() to have been called already, since it
880
+ // calls .html().
881
+ var jQ = $(html);
882
+ jQ.find('*').andSelf().each(function() {
883
+ var jQ = $(this),
884
+ cmdId = jQ.attr('mathquill-command-id'),
885
+ blockId = jQ.attr('mathquill-block-id');
886
+ if (cmdId) MathElement[cmdId].jQadd(jQ);
887
+ if (blockId) MathElement[blockId].jQadd(jQ);
888
+ });
889
+ return jQ;
890
+ };
891
+
892
+ _.finalizeInsert = function() {
893
+ var self = this;
894
+ self.postOrder('finalizeTree');
895
+
896
+ // note: this order is important.
897
+ // empty elements need the empty box provided by blur to
898
+ // be present in order for their dimensions to be measured
899
+ // correctly in redraw.
900
+ self.postOrder('blur');
901
+
902
+ // adjust context-sensitive spacing
903
+ self.postOrder('respace');
904
+ if (self[R].respace) self[R].respace();
905
+ if (self[L].respace) self[L].respace();
906
+
907
+ self.postOrder('redraw');
908
+ self.bubble('redraw');
909
+ };
910
+ });
911
+
912
+ /**
913
+ * Commands and operators, like subscripts, exponents, or fractions.
914
+ * Descendant commands are organized into blocks.
915
+ */
916
+ var MathCommand = P(MathElement, function(_, _super) {
917
+ _.init = function(ctrlSeq, htmlTemplate, textTemplate) {
918
+ var cmd = this;
919
+ _super.init.call(cmd);
920
+
921
+ if (!cmd.ctrlSeq) cmd.ctrlSeq = ctrlSeq;
922
+ if (htmlTemplate) cmd.htmlTemplate = htmlTemplate;
923
+ if (textTemplate) cmd.textTemplate = textTemplate;
924
+ };
925
+
926
+ // obvious methods
927
+ _.replaces = function(replacedFragment) {
928
+ replacedFragment.disown();
929
+ this.replacedFragment = replacedFragment;
930
+ };
931
+ _.isEmpty = function() {
932
+ return this.foldChildren(true, function(isEmpty, child) {
933
+ return isEmpty && child.isEmpty();
934
+ });
935
+ };
936
+
937
+ _.parser = function() {
938
+ var block = latexMathParser.block;
939
+ var self = this;
940
+
941
+ return block.times(self.numBlocks()).map(function(blocks) {
942
+ self.blocks = blocks;
943
+
944
+ for (var i = 0; i < blocks.length; i += 1) {
945
+ blocks[i].adopt(self, self.ends[R], 0);
946
+ }
947
+
948
+ return self;
949
+ });
950
+ };
951
+
952
+ // createLeftOf(cursor) and the methods it calls
953
+ _.createLeftOf = function(cursor) {
954
+ var cmd = this;
955
+ var replacedFragment = cmd.replacedFragment;
956
+
957
+ cmd.createBlocks();
958
+ MathElement.jQize(cmd.html());
959
+ if (replacedFragment) {
960
+ replacedFragment.adopt(cmd.ends[L], 0, 0);
961
+ replacedFragment.jQ.appendTo(cmd.ends[L].jQ);
962
+ }
963
+
964
+ cursor.jQ.before(cmd.jQ);
965
+ cursor[L] = cmd.adopt(cursor.parent, cursor[L], cursor[R]);
966
+
967
+ cmd.finalizeInsert(cursor);
968
+
969
+ cmd.placeCursor(cursor);
970
+ };
971
+ _.createBlocks = function() {
972
+ var cmd = this,
973
+ numBlocks = cmd.numBlocks(),
974
+ blocks = cmd.blocks = Array(numBlocks);
975
+
976
+ for (var i = 0; i < numBlocks; i += 1) {
977
+ var newBlock = blocks[i] = MathBlock();
978
+ newBlock.adopt(cmd, cmd.ends[R], 0);
979
+ }
980
+ };
981
+ _.respace = noop; //placeholder for context-sensitive spacing
982
+ _.placeCursor = function(cursor) {
983
+ //insert the cursor at the right end of the first empty child, searching
984
+ //left-to-right, or if none empty, the right end child
985
+ cursor.insAtRightEnd(this.foldChildren(this.ends[L], function(leftward, child) {
986
+ return leftward.isEmpty() ? leftward : child;
987
+ }));
988
+ };
989
+
990
+ // remove()
991
+ _.remove = function() {
992
+ this.disown();
993
+ this.jQ.remove();
994
+
995
+ this.postOrder(function(el) { delete MathElement[el.id]; });
996
+
997
+ return this;
998
+ };
999
+
1000
+ // methods involved in creating and cross-linking with HTML DOM nodes
1001
+ /*
1002
+ They all expect an .htmlTemplate like
1003
+ '<span>&0</span>'
1004
+ or
1005
+ '<span><span>&0</span><span>&1</span></span>'
1006
+
1007
+ See html.test.js for more examples.
1008
+
1009
+ Requirements:
1010
+ - For each block of the command, there must be exactly one "block content
1011
+ marker" of the form '&<number>' where <number> is the 0-based index of the
1012
+ block. (Like the LaTeX \newcommand syntax, but with a 0-based rather than
1013
+ 1-based index, because JavaScript because C because Dijkstra.)
1014
+ - The block content marker must be the sole contents of the containing
1015
+ element, there can't even be surrounding whitespace, or else we can't
1016
+ guarantee sticking to within the bounds of the block content marker when
1017
+ mucking with the HTML DOM.
1018
+ - The HTML not only must be well-formed HTML (of course), but also must
1019
+ conform to the XHTML requirements on tags, specifically all tags must
1020
+ either be self-closing (like '<br/>') or come in matching pairs.
1021
+ Close tags are never optional.
1022
+
1023
+ Note that &<number> isn't well-formed HTML; if you wanted a literal '&123',
1024
+ your HTML template would have to have '&amp;123'.
1025
+ */
1026
+ _.numBlocks = function() {
1027
+ var matches = this.htmlTemplate.match(/&\d+/g);
1028
+ return matches ? matches.length : 0;
1029
+ };
1030
+ _.html = function() {
1031
+ // Render the entire math subtree rooted at this command, as HTML.
1032
+ // Expects .createBlocks() to have been called already, since it uses the
1033
+ // .blocks array of child blocks.
1034
+ //
1035
+ // See html.test.js for example templates and intended outputs.
1036
+ //
1037
+ // Given an .htmlTemplate as described above,
1038
+ // - insert the mathquill-command-id attribute into all top-level tags,
1039
+ // which will be used to set this.jQ in .jQize().
1040
+ // This is straightforward:
1041
+ // * tokenize into tags and non-tags
1042
+ // * loop through top-level tokens:
1043
+ // * add #cmdId attribute macro to top-level self-closing tags
1044
+ // * else add #cmdId attribute macro to top-level open tags
1045
+ // * skip the matching top-level close tag and all tag pairs
1046
+ // in between
1047
+ // - for each block content marker,
1048
+ // + replace it with the contents of the corresponding block,
1049
+ // rendered as HTML
1050
+ // + insert the mathquill-block-id attribute into the containing tag
1051
+ // This is even easier, a quick regex replace, since block tags cannot
1052
+ // contain anything besides the block content marker.
1053
+ //
1054
+ // Two notes:
1055
+ // - The outermost loop through top-level tokens should never encounter any
1056
+ // top-level close tags, because we should have first encountered a
1057
+ // matching top-level open tag, all inner tags should have appeared in
1058
+ // matching pairs and been skipped, and then we should have skipped the
1059
+ // close tag in question.
1060
+ // - All open tags should have matching close tags, which means our inner
1061
+ // loop should always encounter a close tag and drop nesting to 0. If
1062
+ // a close tag is missing, the loop will continue until i >= tokens.length
1063
+ // and token becomes undefined. This will not infinite loop, even in
1064
+ // production without pray(), because it will then TypeError on .slice().
1065
+
1066
+ var cmd = this;
1067
+ var blocks = cmd.blocks;
1068
+ var cmdId = ' mathquill-command-id=' + cmd.id;
1069
+ var tokens = cmd.htmlTemplate.match(/<[^<>]+>|[^<>]+/g);
1070
+
1071
+ pray('no unmatched angle brackets', tokens.join('') === this.htmlTemplate);
1072
+
1073
+ // add cmdId to all top-level tags
1074
+ for (var i = 0, token = tokens[0]; token; i += 1, token = tokens[i]) {
1075
+ // top-level self-closing tags
1076
+ if (token.slice(-2) === '/>') {
1077
+ tokens[i] = token.slice(0,-2) + cmdId + '/>';
1078
+ }
1079
+ // top-level open tags
1080
+ else if (token.charAt(0) === '<') {
1081
+ pray('not an unmatched top-level close tag', token.charAt(1) !== '/');
1082
+
1083
+ tokens[i] = token.slice(0,-1) + cmdId + '>';
1084
+
1085
+ // skip matching top-level close tag and all tag pairs in between
1086
+ var nesting = 1;
1087
+ do {
1088
+ i += 1, token = tokens[i];
1089
+ pray('no missing close tags', token);
1090
+ // close tags
1091
+ if (token.slice(0,2) === '</') {
1092
+ nesting -= 1;
1093
+ }
1094
+ // non-self-closing open tags
1095
+ else if (token.charAt(0) === '<' && token.slice(-2) !== '/>') {
1096
+ nesting += 1;
1097
+ }
1098
+ } while (nesting > 0);
1099
+ }
1100
+ }
1101
+ return tokens.join('').replace(/>&(\d+)/g, function($0, $1) {
1102
+ return ' mathquill-block-id=' + blocks[$1].id + '>' + blocks[$1].join('html');
1103
+ });
1104
+ };
1105
+
1106
+ // methods to export a string representation of the math tree
1107
+ _.latex = function() {
1108
+ return this.foldChildren(this.ctrlSeq, function(latex, child) {
1109
+ return latex + '{' + (child.latex() || ' ') + '}';
1110
+ });
1111
+ };
1112
+ _.textTemplate = [''];
1113
+ _.text = function() {
1114
+ var cmd = this, i = 0;
1115
+ return cmd.foldChildren(cmd.textTemplate[i], function(text, child) {
1116
+ i += 1;
1117
+ var child_text = child.text();
1118
+ if (text && cmd.textTemplate[i] === '('
1119
+ && child_text[0] === '(' && child_text.slice(-1) === ')')
1120
+ return text + child_text.slice(1, -1) + cmd.textTemplate[i];
1121
+ return text + child.text() + (cmd.textTemplate[i] || '');
1122
+ });
1123
+ };
1124
+ });
1125
+
1126
+ /**
1127
+ * Lightweight command without blocks or children.
1128
+ */
1129
+ var Symbol = P(MathCommand, function(_, _super) {
1130
+ _.init = function(ctrlSeq, html, text) {
1131
+ if (!text) text = ctrlSeq && ctrlSeq.length > 1 ? ctrlSeq.slice(1) : ctrlSeq;
1132
+
1133
+ _super.init.call(this, ctrlSeq, html, [ text ]);
1134
+ };
1135
+
1136
+ _.parser = function() { return Parser.succeed(this); };
1137
+ _.numBlocks = function() { return 0; };
1138
+
1139
+ _.replaces = function(replacedFragment) {
1140
+ replacedFragment.remove();
1141
+ };
1142
+ _.createBlocks = noop;
1143
+ _.latex = function(){ return this.ctrlSeq; };
1144
+ _.text = function(){ return this.textTemplate; };
1145
+ _.placeCursor = noop;
1146
+ _.isEmpty = function(){ return true; };
1147
+ });
1148
+
1149
+ /**
1150
+ * Children and parent of MathCommand's. Basically partitions all the
1151
+ * symbols and operators that descend (in the Math DOM tree) from
1152
+ * ancestor operators.
1153
+ */
1154
+ var MathBlock = P(MathElement, function(_) {
1155
+ _.join = function(methodName) {
1156
+ return this.foldChildren('', function(fold, child) {
1157
+ return fold + child[methodName]();
1158
+ });
1159
+ };
1160
+ _.latex = function() { return this.join('latex'); };
1161
+ _.text = function() {
1162
+ return this.ends[L] === this.ends[R] ?
1163
+ this.ends[L].text() :
1164
+ '(' + this.join('text') + ')'
1165
+ ;
1166
+ };
1167
+ _.isEmpty = function() {
1168
+ return this.ends[L] === 0 && this.ends[R] === 0;
1169
+ };
1170
+ _.write = function(cursor, ch, replacedFragment) {
1171
+ var cmd;
1172
+ if (ch.match(/^[a-eg-zA-Z]$/)) //exclude f because want florin
1173
+ cmd = Variable(ch);
1174
+ else if (cmd = CharCmds[ch] || LatexCmds[ch])
1175
+ cmd = cmd(ch);
1176
+ else
1177
+ cmd = VanillaSymbol(ch);
1178
+
1179
+ if (replacedFragment) cmd.replaces(replacedFragment);
1180
+
1181
+ cmd.createLeftOf(cursor);
1182
+ };
1183
+ _.focus = function() {
1184
+ this.jQ.addClass('hasCursor');
1185
+ this.jQ.removeClass('empty');
1186
+
1187
+ return this;
1188
+ };
1189
+ _.blur = function() {
1190
+ this.jQ.removeClass('hasCursor');
1191
+ if (this.isEmpty())
1192
+ this.jQ.addClass('empty');
1193
+
1194
+ return this;
1195
+ };
1196
+ });
1197
+
1198
+ /**
1199
+ * Math tree fragment base class.
1200
+ * Some math-tree-specific extensions to Fragment.
1201
+ */
1202
+ var MathFragment = P(Fragment, function(_, _super) {
1203
+ _.init = function(leftEnd, rightEnd) {
1204
+ // just select one thing if only one argument
1205
+ _super.init.call(this, leftEnd, rightEnd || leftEnd);
1206
+ this.jQ = this.fold($(), function(jQ, child){ return child.jQ.add(jQ); });
1207
+ };
1208
+ _.latex = function() {
1209
+ return this.fold('', function(latex, el){ return latex + el.latex(); });
1210
+ };
1211
+ _.remove = function() {
1212
+ this.jQ.remove();
1213
+
1214
+ this.each(function(el) {
1215
+ el.postOrder(function(desc) {
1216
+ delete MathElement[desc.id];
1217
+ });
1218
+ });
1219
+
1220
+ return this.disown();
1221
+ };
1222
+ });
1223
+ /*********************************************
1224
+ * Root math elements with event delegation.
1225
+ ********************************************/
1226
+
1227
+ function createRoot(jQ, root, textbox, editable) {
1228
+ var contents = jQ.contents().detach();
1229
+
1230
+ if (!textbox) {
1231
+ jQ.addClass('mathquill-rendered-math');
1232
+ }
1233
+
1234
+ root.jQ = jQ.attr(mqBlockId, root.id);
1235
+ root.revert = function() {
1236
+ jQ.empty().unbind('.mathquill')
1237
+ .removeClass('mathquill-rendered-math mathquill-editable mathquill-textbox')
1238
+ .append(contents);
1239
+ };
1240
+
1241
+ var cursor = root.cursor = Cursor(root);
1242
+
1243
+ root.renderLatex(contents.text());
1244
+
1245
+ //textarea stuff
1246
+ var textareaSpan = root.textarea = $('<span class="textarea"><textarea></textarea></span>'),
1247
+ textarea = textareaSpan.children();
1248
+
1249
+ /******
1250
+ * TODO [Han]: Document this
1251
+ */
1252
+ var textareaSelectionTimeout;
1253
+ root.selectionChanged = function() {
1254
+ if (textareaSelectionTimeout === undefined) {
1255
+ textareaSelectionTimeout = setTimeout(setTextareaSelection);
1256
+ }
1257
+ forceIERedraw(jQ[0]);
1258
+ };
1259
+ function setTextareaSelection() {
1260
+ textareaSelectionTimeout = undefined;
1261
+ var latex = cursor.selection ? '$'+cursor.selection.latex()+'$' : '';
1262
+ textareaManager.select(latex);
1263
+ }
1264
+
1265
+ //prevent native selection except textarea
1266
+ jQ.bind('selectstart.mathquill', function(e) {
1267
+ if (e.target !== textarea[0]) e.preventDefault();
1268
+ e.stopPropagation();
1269
+ });
1270
+
1271
+ //drag-to-select event handling
1272
+ var anticursor, blink = cursor.blink;
1273
+ jQ.bind('mousedown.mathquill', function(e) {
1274
+ function mousemove(e) {
1275
+ cursor.seek($(e.target), e.pageX, e.pageY);
1276
+
1277
+ if (cursor[L] !== anticursor[L]
1278
+ || cursor.parent !== anticursor.parent) {
1279
+ cursor.selectFrom(anticursor);
1280
+ }
1281
+
1282
+ return false;
1283
+ }
1284
+
1285
+ // docmousemove is attached to the document, so that
1286
+ // selection still works when the mouse leaves the window.
1287
+ function docmousemove(e) {
1288
+ // [Han]: i delete the target because of the way seek works.
1289
+ // it will not move the mouse to the target, but will instead
1290
+ // just seek those X and Y coordinates. If there is a target,
1291
+ // it will try to move the cursor to document, which will not work.
1292
+ // cursor.seek needs to be refactored.
1293
+ delete e.target;
1294
+
1295
+ return mousemove(e);
1296
+ }
1297
+
1298
+ function mouseup(e) {
1299
+ anticursor = undefined;
1300
+ cursor.blink = blink;
1301
+ if (!cursor.selection) {
1302
+ if (editable) {
1303
+ cursor.show();
1304
+ }
1305
+ else {
1306
+ textareaSpan.detach();
1307
+ }
1308
+ }
1309
+
1310
+ // delete the mouse handlers now that we're not dragging anymore
1311
+ jQ.unbind('mousemove', mousemove);
1312
+ $(e.target.ownerDocument).unbind('mousemove', docmousemove).unbind('mouseup', mouseup);
1313
+ }
1314
+
1315
+ setTimeout(function() { textarea.focus(); });
1316
+ // preventDefault won't prevent focus on mousedown in IE<9
1317
+ // that means immediately after this mousedown, whatever was
1318
+ // mousedown-ed will receive focus
1319
+ // http://bugs.jquery.com/ticket/10345
1320
+
1321
+ cursor.blink = noop;
1322
+ cursor.seek($(e.target), e.pageX, e.pageY);
1323
+
1324
+ anticursor = Point(cursor.parent, cursor[L], cursor[R]);
1325
+
1326
+ if (!editable) jQ.prepend(textareaSpan);
1327
+
1328
+ jQ.mousemove(mousemove);
1329
+ $(e.target.ownerDocument).mousemove(docmousemove).mouseup(mouseup);
1330
+
1331
+ return false;
1332
+ });
1333
+
1334
+ if (!editable) {
1335
+ var textareaManager = manageTextarea(textarea, { container: jQ });
1336
+ jQ.bind('cut paste', false).bind('copy', setTextareaSelection)
1337
+ .prepend('<span class="selectable">$'+root.latex()+'$</span>');
1338
+ textarea.blur(function() {
1339
+ cursor.clearSelection();
1340
+ setTimeout(detach); //detaching during blur explodes in WebKit
1341
+ });
1342
+ function detach() {
1343
+ textareaSpan.detach();
1344
+ }
1345
+ return;
1346
+ }
1347
+
1348
+ var textareaManager = manageTextarea(textarea, {
1349
+ container: jQ,
1350
+ key: function(key, evt) {
1351
+ cursor.parent.bubble('onKey', key, evt);
1352
+ },
1353
+ text: function(text) {
1354
+ cursor.parent.bubble('onText', text);
1355
+ },
1356
+ cut: function(e) {
1357
+ if (cursor.selection) {
1358
+ setTimeout(function() {
1359
+ cursor.prepareEdit();
1360
+ cursor.parent.bubble('redraw');
1361
+ });
1362
+ }
1363
+
1364
+ e.stopPropagation();
1365
+ },
1366
+ paste: function(text) {
1367
+ // FIXME HACK the parser in RootTextBlock needs to be moved to
1368
+ // Cursor::writeLatex or something so this'll work with
1369
+ // MathQuill textboxes
1370
+ if (text.slice(0,1) === '$' && text.slice(-1) === '$') {
1371
+ text = text.slice(1, -1);
1372
+ }
1373
+ else {
1374
+ text = '\\text{' + text + '}';
1375
+ }
1376
+
1377
+ cursor.writeLatex(text).show();
1378
+ }
1379
+ });
1380
+
1381
+ jQ.prepend(textareaSpan);
1382
+
1383
+ //root CSS classes
1384
+ jQ.addClass('mathquill-editable');
1385
+ if (textbox)
1386
+ jQ.addClass('mathquill-textbox');
1387
+
1388
+ //focus and blur handling
1389
+ textarea.focus(function(e) {
1390
+ if (!cursor.parent)
1391
+ cursor.insAtRightEnd(root);
1392
+ cursor.parent.jQ.addClass('hasCursor');
1393
+ if (cursor.selection) {
1394
+ cursor.selection.jQ.removeClass('blur');
1395
+ setTimeout(root.selectionChanged); //re-select textarea contents after tabbing away and back
1396
+ }
1397
+ else
1398
+ cursor.show();
1399
+ e.stopPropagation();
1400
+ }).blur(function(e) {
1401
+ cursor.hide().parent.blur();
1402
+ if (cursor.selection)
1403
+ cursor.selection.jQ.addClass('blur');
1404
+ e.stopPropagation();
1405
+ });
1406
+
1407
+ jQ.bind('focus.mathquill blur.mathquill', function(e) {
1408
+ textarea.trigger(e);
1409
+ }).blur();
1410
+ }
1411
+
1412
+ var RootMathBlock = P(MathBlock, function(_, _super) {
1413
+ _.latex = function() {
1414
+ return _super.latex.call(this).replace(/(\\[a-z]+) (?![a-z])/ig,'$1');
1415
+ };
1416
+ _.text = function() {
1417
+ return this.foldChildren('', function(text, child) {
1418
+ return text + child.text();
1419
+ });
1420
+ };
1421
+ _.renderLatex = function(latex) {
1422
+ var jQ = this.jQ;
1423
+
1424
+ jQ.children().slice(1).remove();
1425
+ this.ends[L] = this.ends[R] = 0;
1426
+
1427
+ delete this.cursor.selection;
1428
+ this.cursor.insAtRightEnd(this).writeLatex(latex);
1429
+ };
1430
+ _.onKey = function(key, e) {
1431
+ switch (key) {
1432
+ case 'Ctrl-Shift-Backspace':
1433
+ case 'Ctrl-Backspace':
1434
+ while (this.cursor[L] || this.cursor.selection) {
1435
+ this.cursor.backspace();
1436
+ }
1437
+ break;
1438
+
1439
+ case 'Shift-Backspace':
1440
+ case 'Backspace':
1441
+ this.cursor.backspace();
1442
+ break;
1443
+
1444
+ // Tab or Esc -> go one block right if it exists, else escape right.
1445
+ case 'Esc':
1446
+ case 'Tab':
1447
+ case 'Spacebar':
1448
+ var parent = this.cursor.parent;
1449
+ // cursor is in root editable, continue default
1450
+ if (parent === this.cursor.root) {
1451
+ if (key === 'Spacebar') e.preventDefault();
1452
+ return;
1453
+ }
1454
+
1455
+ this.cursor.prepareMove();
1456
+ if (parent[R]) {
1457
+ // go one block right
1458
+ this.cursor.insAtLeftEnd(parent[R]);
1459
+ } else {
1460
+ // get out of the block
1461
+ this.cursor.insRightOf(parent.parent);
1462
+ }
1463
+ break;
1464
+
1465
+ // Shift-Tab -> go one block left if it exists, else escape left.
1466
+ case 'Shift-Tab':
1467
+ case 'Shift-Esc':
1468
+ case 'Shift-Spacebar':
1469
+ var parent = this.cursor.parent;
1470
+ //cursor is in root editable, continue default
1471
+ if (parent === this.cursor.root) {
1472
+ if (key === 'Shift-Spacebar') e.preventDefault();
1473
+ return;
1474
+ }
1475
+
1476
+ this.cursor.prepareMove();
1477
+ if (parent[L]) {
1478
+ // go one block left
1479
+ this.cursor.insAtRightEnd(parent[L]);
1480
+ } else {
1481
+ //get out of the block
1482
+ this.cursor.insLeftOf(parent.parent);
1483
+ }
1484
+ break;
1485
+
1486
+ // Prevent newlines from showing up
1487
+ case 'Enter': break;
1488
+
1489
+
1490
+ // End -> move to the end of the current block.
1491
+ case 'End':
1492
+ this.cursor.prepareMove().insAtRightEnd(this.cursor.parent);
1493
+ break;
1494
+
1495
+ // Ctrl-End -> move all the way to the end of the root block.
1496
+ case 'Ctrl-End':
1497
+ this.cursor.prepareMove().insAtRightEnd(this);
1498
+ break;
1499
+
1500
+ // Shift-End -> select to the end of the current block.
1501
+ case 'Shift-End':
1502
+ while (this.cursor[R]) {
1503
+ this.cursor.selectRight();
1504
+ }
1505
+ break;
1506
+
1507
+ // Ctrl-Shift-End -> select to the end of the root block.
1508
+ case 'Ctrl-Shift-End':
1509
+ while (this.cursor[R] || this.cursor.parent !== this) {
1510
+ this.cursor.selectRight();
1511
+ }
1512
+ break;
1513
+
1514
+ // Home -> move to the start of the root block or the current block.
1515
+ case 'Home':
1516
+ this.cursor.prepareMove().insAtLeftEnd(this.cursor.parent);
1517
+ break;
1518
+
1519
+ // Ctrl-Home -> move to the start of the current block.
1520
+ case 'Ctrl-Home':
1521
+ this.cursor.prepareMove().insAtLeftEnd(this);
1522
+ break;
1523
+
1524
+ // Shift-Home -> select to the start of the current block.
1525
+ case 'Shift-Home':
1526
+ while (this.cursor[L]) {
1527
+ this.cursor.selectLeft();
1528
+ }
1529
+ break;
1530
+
1531
+ // Ctrl-Shift-Home -> move to the start of the root block.
1532
+ case 'Ctrl-Shift-Home':
1533
+ while (this.cursor[L] || this.cursor.parent !== this) {
1534
+ this.cursor.selectLeft();
1535
+ }
1536
+ break;
1537
+
1538
+ case 'Left': this.cursor.moveLeft(); break;
1539
+ case 'Shift-Left': this.cursor.selectLeft(); break;
1540
+ case 'Ctrl-Left': break;
1541
+
1542
+ case 'Right': this.cursor.moveRight(); break;
1543
+ case 'Shift-Right': this.cursor.selectRight(); break;
1544
+ case 'Ctrl-Right': break;
1545
+
1546
+ case 'Up': this.cursor.moveUp(); break;
1547
+ case 'Down': this.cursor.moveDown(); break;
1548
+
1549
+ case 'Shift-Up':
1550
+ if (this.cursor[L]) {
1551
+ while (this.cursor[L]) this.cursor.selectLeft();
1552
+ } else {
1553
+ this.cursor.selectLeft();
1554
+ }
1555
+
1556
+ case 'Shift-Down':
1557
+ if (this.cursor[R]) {
1558
+ while (this.cursor[R]) this.cursor.selectRight();
1559
+ }
1560
+ else {
1561
+ this.cursor.selectRight();
1562
+ }
1563
+
1564
+ case 'Ctrl-Up': break;
1565
+ case 'Ctrl-Down': break;
1566
+
1567
+ case 'Ctrl-Shift-Del':
1568
+ case 'Ctrl-Del':
1569
+ while (this.cursor[R] || this.cursor.selection) {
1570
+ this.cursor.deleteForward();
1571
+ }
1572
+ break;
1573
+
1574
+ case 'Shift-Del':
1575
+ case 'Del':
1576
+ this.cursor.deleteForward();
1577
+ break;
1578
+
1579
+ case 'Meta-A':
1580
+ case 'Ctrl-A':
1581
+ //so not stopPropagation'd at RootMathCommand
1582
+ if (this !== this.cursor.root) return;
1583
+
1584
+ this.cursor.prepareMove().insAtRightEnd(this);
1585
+ while (this.cursor[L]) this.cursor.selectLeft();
1586
+ break;
1587
+
1588
+ default:
1589
+ return false;
1590
+ }
1591
+ e.preventDefault();
1592
+ return false;
1593
+ };
1594
+ _.onText = function(ch) {
1595
+ this.cursor.write(ch);
1596
+ return false;
1597
+ };
1598
+ });
1599
+
1600
+ var RootMathCommand = P(MathCommand, function(_, _super) {
1601
+ _.init = function(cursor) {
1602
+ _super.init.call(this, '$');
1603
+ this.cursor = cursor;
1604
+ };
1605
+ _.htmlTemplate = '<span class="mathquill-rendered-math">&0</span>';
1606
+ _.createBlocks = function() {
1607
+ this.ends[L] =
1608
+ this.ends[R] =
1609
+ RootMathBlock();
1610
+
1611
+ this.blocks = [ this.ends[L] ];
1612
+
1613
+ this.ends[L].parent = this;
1614
+
1615
+ this.ends[L].cursor = this.cursor;
1616
+ this.ends[L].write = function(cursor, ch, replacedFragment) {
1617
+ if (ch !== '$')
1618
+ MathBlock.prototype.write.call(this, cursor, ch, replacedFragment);
1619
+ else if (this.isEmpty()) {
1620
+ cursor.insRightOf(this.parent).backspace().show();
1621
+ VanillaSymbol('\\$','$').createLeftOf(cursor);
1622
+ }
1623
+ else if (!cursor[R])
1624
+ cursor.insRightOf(this.parent);
1625
+ else if (!cursor[L])
1626
+ cursor.insLeftOf(this.parent);
1627
+ else
1628
+ MathBlock.prototype.write.call(this, cursor, ch, replacedFragment);
1629
+ };
1630
+ };
1631
+ _.latex = function() {
1632
+ return '$' + this.ends[L].latex() + '$';
1633
+ };
1634
+ });
1635
+
1636
+ var RootTextBlock = P(MathBlock, function(_) {
1637
+ _.renderLatex = function(latex) {
1638
+ var self = this;
1639
+ var cursor = self.cursor;
1640
+ self.jQ.children().slice(1).remove();
1641
+ self.ends[L] = self.ends[R] = 0;
1642
+ delete cursor.selection;
1643
+ cursor.show().insAtRightEnd(self);
1644
+
1645
+ var regex = Parser.regex;
1646
+ var string = Parser.string;
1647
+ var eof = Parser.eof;
1648
+ var all = Parser.all;
1649
+
1650
+ // Parser RootMathCommand
1651
+ var mathMode = string('$').then(latexMathParser)
1652
+ // because TeX is insane, math mode doesn't necessarily
1653
+ // have to end. So we allow for the case that math mode
1654
+ // continues to the end of the stream.
1655
+ .skip(string('$').or(eof))
1656
+ .map(function(block) {
1657
+ // HACK FIXME: this shouldn't have to have access to cursor
1658
+ var rootMathCommand = RootMathCommand(cursor);
1659
+
1660
+ rootMathCommand.createBlocks();
1661
+ var rootMathBlock = rootMathCommand.ends[L];
1662
+ block.children().adopt(rootMathBlock, 0, 0);
1663
+
1664
+ return rootMathCommand;
1665
+ })
1666
+ ;
1667
+
1668
+ var escapedDollar = string('\\$').result('$');
1669
+ var textChar = escapedDollar.or(regex(/^[^$]/)).map(VanillaSymbol);
1670
+ var latexText = mathMode.or(textChar).many();
1671
+ var commands = latexText.skip(eof).or(all.result(false)).parse(latex);
1672
+
1673
+ if (commands) {
1674
+ for (var i = 0; i < commands.length; i += 1) {
1675
+ commands[i].adopt(self, self.ends[R], 0);
1676
+ }
1677
+
1678
+ var html = self.join('html');
1679
+ MathElement.jQize(html).appendTo(self.jQ);
1680
+
1681
+ this.finalizeInsert();
1682
+ }
1683
+ };
1684
+ _.onKey = function(key) {
1685
+ if (key === 'Spacebar' || key === 'Shift-Spacebar') return;
1686
+ RootMathBlock.prototype.onKey.apply(this, arguments);
1687
+ };
1688
+ _.onText = RootMathBlock.prototype.onText;
1689
+ _.write = function(cursor, ch, replacedFragment) {
1690
+ if (replacedFragment) replacedFragment.remove();
1691
+ if (ch === '$')
1692
+ RootMathCommand(cursor).createLeftOf(cursor);
1693
+ else {
1694
+ var html;
1695
+ if (ch === '<') html = '&lt;';
1696
+ else if (ch === '>') html = '&gt;';
1697
+ VanillaSymbol(ch, html).createLeftOf(cursor);
1698
+ }
1699
+ };
1700
+ });
1701
+ /***************************
1702
+ * Commands and Operators.
1703
+ **************************/
1704
+
1705
+ var CharCmds = {}, LatexCmds = {}; //single character commands, LaTeX commands
1706
+
1707
+ var scale, // = function(jQ, x, y) { ... }
1708
+ //will use a CSS 2D transform to scale the jQuery-wrapped HTML elements,
1709
+ //or the filter matrix transform fallback for IE 5.5-8, or gracefully degrade to
1710
+ //increasing the fontSize to match the vertical Y scaling factor.
1711
+
1712
+ //ideas from http://github.com/louisremi/jquery.transform.js
1713
+ //see also http://msdn.microsoft.com/en-us/library/ms533014(v=vs.85).aspx
1714
+
1715
+ forceIERedraw = noop,
1716
+ div = document.createElement('div'),
1717
+ div_style = div.style,
1718
+ transformPropNames = {
1719
+ transform:1,
1720
+ WebkitTransform:1,
1721
+ MozTransform:1,
1722
+ OTransform:1,
1723
+ msTransform:1
1724
+ },
1725
+ transformPropName;
1726
+
1727
+ for (var prop in transformPropNames) {
1728
+ if (prop in div_style) {
1729
+ transformPropName = prop;
1730
+ break;
1731
+ }
1732
+ }
1733
+
1734
+ if (transformPropName) {
1735
+ scale = function(jQ, x, y) {
1736
+ jQ.css(transformPropName, 'scale('+x+','+y+')');
1737
+ };
1738
+ }
1739
+ else if ('filter' in div_style) { //IE 6, 7, & 8 fallback, see https://github.com/laughinghan/mathquill/wiki/Transforms
1740
+ forceIERedraw = function(el){ el.className = el.className; };
1741
+ scale = function(jQ, x, y) { //NOTE: assumes y > x
1742
+ x /= (1+(y-1)/2);
1743
+ jQ.css('fontSize', y + 'em');
1744
+ if (!jQ.hasClass('matrixed-container')) {
1745
+ jQ.addClass('matrixed-container')
1746
+ .wrapInner('<span class="matrixed"></span>');
1747
+ }
1748
+ var innerjQ = jQ.children()
1749
+ .css('filter', 'progid:DXImageTransform.Microsoft'
1750
+ + '.Matrix(M11=' + x + ",SizingMethod='auto expand')"
1751
+ );
1752
+ function calculateMarginRight() {
1753
+ jQ.css('marginRight', (innerjQ.width()-1)*(x-1)/x + 'px');
1754
+ }
1755
+ calculateMarginRight();
1756
+ var intervalId = setInterval(calculateMarginRight);
1757
+ $(window).load(function() {
1758
+ clearTimeout(intervalId);
1759
+ calculateMarginRight();
1760
+ });
1761
+ };
1762
+ }
1763
+ else {
1764
+ scale = function(jQ, x, y) {
1765
+ jQ.css('fontSize', y + 'em');
1766
+ };
1767
+ }
1768
+
1769
+ var Style = P(MathCommand, function(_, _super) {
1770
+ _.init = function(ctrlSeq, tagName, attrs) {
1771
+ _super.init.call(this, ctrlSeq, '<'+tagName+' '+attrs+'>&0</'+tagName+'>');
1772
+ };
1773
+ });
1774
+
1775
+ //fonts
1776
+ LatexCmds.mathrm = bind(Style, '\\mathrm', 'span', 'class="roman font"');
1777
+ LatexCmds.mathit = bind(Style, '\\mathit', 'i', 'class="font"');
1778
+ LatexCmds.mathbf = bind(Style, '\\mathbf', 'b', 'class="font"');
1779
+ LatexCmds.mathsf = bind(Style, '\\mathsf', 'span', 'class="sans-serif font"');
1780
+ LatexCmds.mathtt = bind(Style, '\\mathtt', 'span', 'class="monospace font"');
1781
+ //text-decoration
1782
+ LatexCmds.underline = bind(Style, '\\underline', 'span', 'class="non-leaf underline"');
1783
+ LatexCmds.overline = LatexCmds.bar = bind(Style, '\\overline', 'span', 'class="non-leaf overline"');
1784
+
1785
+ var SupSub = P(MathCommand, function(_, _super) {
1786
+ _.init = function(ctrlSeq, tag, text) {
1787
+ _super.init.call(this, ctrlSeq, '<'+tag+' class="non-leaf">&0</'+tag+'>', [ text ]);
1788
+ };
1789
+ _.finalizeTree = function() {
1790
+ //TODO: use inheritance
1791
+ pray('SupSub is only _ and ^',
1792
+ this.ctrlSeq === '^' || this.ctrlSeq === '_'
1793
+ );
1794
+
1795
+ if (this.ctrlSeq === '_') {
1796
+ this.down = this.ends[L];
1797
+ this.ends[L].up = insLeftOfMeUnlessAtEnd;
1798
+ }
1799
+ else {
1800
+ this.up = this.ends[L];
1801
+ this.ends[L].down = insLeftOfMeUnlessAtEnd;
1802
+ }
1803
+ function insLeftOfMeUnlessAtEnd(cursor) {
1804
+ // cursor.insLeftOf(cmd), unless cursor at the end of block, and every
1805
+ // ancestor cmd is at the end of every ancestor block
1806
+ var cmd = this.parent, ancestorCmd = cursor;
1807
+ do {
1808
+ if (ancestorCmd[R]) {
1809
+ cursor.insLeftOf(cmd);
1810
+ return false;
1811
+ }
1812
+ ancestorCmd = ancestorCmd.parent.parent;
1813
+ } while (ancestorCmd !== cmd);
1814
+ cursor.insRightOf(cmd);
1815
+ return false;
1816
+ }
1817
+ };
1818
+ _.latex = function() {
1819
+ var latex = this.ends[L].latex();
1820
+ if (latex.length === 1)
1821
+ return this.ctrlSeq + latex;
1822
+ else
1823
+ return this.ctrlSeq + '{' + (latex || ' ') + '}';
1824
+ };
1825
+ _.redraw = function() {
1826
+ if (this[L])
1827
+ this[L].respace();
1828
+ //SupSub::respace recursively calls respace on all the following SupSubs
1829
+ //so if leftward is a SupSub, no need to call respace on this or following nodes
1830
+ if (!(this[L] instanceof SupSub)) {
1831
+ this.respace();
1832
+ //and if rightward is a SupSub, then this.respace() will have already called
1833
+ //this[R].respace()
1834
+ if (this[R] && !(this[R] instanceof SupSub))
1835
+ this[R].respace();
1836
+ }
1837
+ };
1838
+ _.respace = function() {
1839
+ if (
1840
+ this[L].ctrlSeq === '\\int ' || (
1841
+ this[L] instanceof SupSub && this[L].ctrlSeq != this.ctrlSeq
1842
+ && this[L][L] && this[L][L].ctrlSeq === '\\int '
1843
+ )
1844
+ ) {
1845
+ if (!this.limit) {
1846
+ this.limit = true;
1847
+ this.jQ.addClass('limit');
1848
+ }
1849
+ }
1850
+ else {
1851
+ if (this.limit) {
1852
+ this.limit = false;
1853
+ this.jQ.removeClass('limit');
1854
+ }
1855
+ }
1856
+
1857
+ this.respaced = this[L] instanceof SupSub && this[L].ctrlSeq != this.ctrlSeq && !this[L].respaced;
1858
+ if (this.respaced) {
1859
+ var fontSize = +this.jQ.css('fontSize').slice(0,-2),
1860
+ leftWidth = this[L].jQ.outerWidth(),
1861
+ thisWidth = this.jQ.outerWidth();
1862
+ this.jQ.css({
1863
+ left: (this.limit && this.ctrlSeq === '_' ? -.25 : 0) - leftWidth/fontSize + 'em',
1864
+ marginRight: .1 - min(thisWidth, leftWidth)/fontSize + 'em'
1865
+ //1px extra so it doesn't wrap in retarded browsers (Firefox 2, I think)
1866
+ });
1867
+ }
1868
+ else if (this.limit && this.ctrlSeq === '_') {
1869
+ this.jQ.css({
1870
+ left: '-.25em',
1871
+ marginRight: ''
1872
+ });
1873
+ }
1874
+ else {
1875
+ this.jQ.css({
1876
+ left: '',
1877
+ marginRight: ''
1878
+ });
1879
+ }
1880
+
1881
+ if (this[R] instanceof SupSub)
1882
+ this[R].respace();
1883
+
1884
+ return this;
1885
+ };
1886
+ });
1887
+
1888
+ LatexCmds.subscript =
1889
+ LatexCmds._ = bind(SupSub, '_', 'sub', '_');
1890
+
1891
+ LatexCmds.superscript =
1892
+ LatexCmds.supscript =
1893
+ LatexCmds['^'] = bind(SupSub, '^', 'sup', '**');
1894
+
1895
+ var Fraction =
1896
+ LatexCmds.frac =
1897
+ LatexCmds.dfrac =
1898
+ LatexCmds.cfrac =
1899
+ LatexCmds.fraction = P(MathCommand, function(_, _super) {
1900
+ _.ctrlSeq = '\\frac';
1901
+ _.htmlTemplate =
1902
+ '<span class="fraction non-leaf">'
1903
+ + '<span class="numerator">&0</span>'
1904
+ + '<span class="denominator">&1</span>'
1905
+ + '<span style="display:inline-block;width:0">&nbsp;</span>'
1906
+ + '</span>'
1907
+ ;
1908
+ _.textTemplate = ['(', '/', ')'];
1909
+ _.finalizeTree = function() {
1910
+ this.up = this.ends[R].up = this.ends[L];
1911
+ this.down = this.ends[L].down = this.ends[R];
1912
+ };
1913
+ });
1914
+
1915
+ var LiveFraction =
1916
+ LatexCmds.over =
1917
+ CharCmds['/'] = P(Fraction, function(_, _super) {
1918
+ _.createLeftOf = function(cursor) {
1919
+ if (!this.replacedFragment) {
1920
+ var leftward = cursor[L];
1921
+ while (leftward &&
1922
+ !(
1923
+ leftward instanceof BinaryOperator ||
1924
+ leftward instanceof TextBlock ||
1925
+ leftward instanceof BigSymbol ||
1926
+ ',;:'.split('').indexOf(leftward.ctrlSeq) > -1
1927
+ ) //lookbehind for operator
1928
+ )
1929
+ leftward = leftward[L];
1930
+
1931
+ if (leftward instanceof BigSymbol && leftward[R] instanceof SupSub) {
1932
+ leftward = leftward[R];
1933
+ if (leftward[R] instanceof SupSub && leftward[R].ctrlSeq != leftward.ctrlSeq)
1934
+ leftward = leftward[R];
1935
+ }
1936
+
1937
+ if (leftward !== cursor[L]) {
1938
+ this.replaces(MathFragment(leftward[R] || cursor.parent.ends[L], cursor[L]));
1939
+ cursor[L] = leftward;
1940
+ }
1941
+ }
1942
+ _super.createLeftOf.call(this, cursor);
1943
+ };
1944
+ });
1945
+
1946
+ var SquareRoot =
1947
+ LatexCmds.sqrt =
1948
+ LatexCmds['√'] = P(MathCommand, function(_, _super) {
1949
+ _.ctrlSeq = '\\sqrt';
1950
+ _.htmlTemplate =
1951
+ '<span class="non-leaf">'
1952
+ + '<span class="scaled sqrt-prefix">&radic;</span>'
1953
+ + '<span class="non-leaf sqrt-stem">&0</span>'
1954
+ + '</span>'
1955
+ ;
1956
+ _.textTemplate = ['sqrt(', ')'];
1957
+ _.parser = function() {
1958
+ return latexMathParser.optBlock.then(function(optBlock) {
1959
+ return latexMathParser.block.map(function(block) {
1960
+ var nthroot = NthRoot();
1961
+ nthroot.blocks = [ optBlock, block ];
1962
+ optBlock.adopt(nthroot, 0, 0);
1963
+ block.adopt(nthroot, optBlock, 0);
1964
+ return nthroot;
1965
+ });
1966
+ }).or(_super.parser.call(this));
1967
+ };
1968
+ _.redraw = function() {
1969
+ var block = this.ends[R].jQ;
1970
+ scale(block.prev(), 1, block.innerHeight()/+block.css('fontSize').slice(0,-2) - .1);
1971
+ };
1972
+ });
1973
+
1974
+ var Vec = LatexCmds.vec = P(MathCommand, function(_, _super) {
1975
+ _.ctrlSeq = '\\vec';
1976
+ _.htmlTemplate =
1977
+ '<span class="non-leaf">'
1978
+ + '<span class="vector-prefix">&rarr;</span>'
1979
+ + '<span class="vector-stem">&0</span>'
1980
+ + '</span>'
1981
+ ;
1982
+ _.textTemplate = ['vec(', ')'];
1983
+ });
1984
+
1985
+ var NthRoot =
1986
+ LatexCmds.nthroot = P(SquareRoot, function(_, _super) {
1987
+ _.htmlTemplate =
1988
+ '<sup class="nthroot non-leaf">&0</sup>'
1989
+ + '<span class="scaled">'
1990
+ + '<span class="sqrt-prefix scaled">&radic;</span>'
1991
+ + '<span class="sqrt-stem non-leaf">&1</span>'
1992
+ + '</span>'
1993
+ ;
1994
+ _.textTemplate = ['sqrt[', '](', ')'];
1995
+ _.latex = function() {
1996
+ return '\\sqrt['+this.ends[L].latex()+']{'+this.ends[R].latex()+'}';
1997
+ };
1998
+ });
1999
+
2000
+ // Round/Square/Curly/Angle Brackets (aka Parens/Brackets/Braces)
2001
+ var Bracket = P(MathCommand, function(_, _super) {
2002
+ _.init = function(open, close, ctrlSeq, end) {
2003
+ _super.init.call(this, '\\left'+ctrlSeq,
2004
+ '<span class="non-leaf">'
2005
+ + '<span class="scaled paren">'+open+'</span>'
2006
+ + '<span class="non-leaf">&0</span>'
2007
+ + '<span class="scaled paren">'+close+'</span>'
2008
+ + '</span>',
2009
+ [open, close]);
2010
+ this.end = '\\right'+end;
2011
+ };
2012
+ _.jQadd = function() {
2013
+ _super.jQadd.apply(this, arguments);
2014
+ var jQ = this.jQ;
2015
+ this.bracketjQs = jQ.children(':first').add(jQ.children(':last'));
2016
+ };
2017
+ _.latex = function() {
2018
+ return this.ctrlSeq + this.ends[L].latex() + this.end;
2019
+ };
2020
+ _.redraw = function() {
2021
+ var blockjQ = this.ends[L].jQ;
2022
+
2023
+ var height = blockjQ.outerHeight()/+blockjQ.css('fontSize').slice(0,-2);
2024
+
2025
+ scale(this.bracketjQs, min(1 + .2*(height - 1), 1.2), 1.05*height);
2026
+ };
2027
+ });
2028
+
2029
+ LatexCmds.left = P(MathCommand, function(_) {
2030
+ _.parser = function() {
2031
+ var regex = Parser.regex;
2032
+ var string = Parser.string;
2033
+ var succeed = Parser.succeed;
2034
+ var optWhitespace = Parser.optWhitespace;
2035
+
2036
+ return optWhitespace.then(regex(/^(?:[([|]|\\\{)/))
2037
+ .then(function(open) {
2038
+ if (open.charAt(0) === '\\') open = open.slice(1);
2039
+
2040
+ var cmd = CharCmds[open]();
2041
+
2042
+ return latexMathParser
2043
+ .map(function (block) {
2044
+ cmd.blocks = [ block ];
2045
+ block.adopt(cmd, 0, 0);
2046
+ })
2047
+ .then(string('\\right'))
2048
+ .skip(optWhitespace)
2049
+ .then(regex(/^(?:[\])|]|\\\})/))
2050
+ .then(function(close) {
2051
+ if (close.slice(-1) !== cmd.end.slice(-1)) {
2052
+ return Parser.fail('open doesn\'t match close');
2053
+ }
2054
+
2055
+ return succeed(cmd);
2056
+ })
2057
+ ;
2058
+ })
2059
+ ;
2060
+ };
2061
+ });
2062
+
2063
+ LatexCmds.right = P(MathCommand, function(_) {
2064
+ _.parser = function() {
2065
+ return Parser.fail('unmatched \\right');
2066
+ };
2067
+ });
2068
+
2069
+ LatexCmds.lbrace =
2070
+ CharCmds['{'] = bind(Bracket, '{', '}', '\\{', '\\}');
2071
+ LatexCmds.langle =
2072
+ LatexCmds.lang = bind(Bracket, '&lang;','&rang;','\\langle ','\\rangle ');
2073
+
2074
+ // Closing bracket matching opening bracket above
2075
+ var CloseBracket = P(Bracket, function(_, _super) {
2076
+ _.createLeftOf = function(cursor) {
2077
+ // if I'm at the end of my parent who is a matching open-paren,
2078
+ // and I am not replacing a selection fragment, don't create me,
2079
+ // just put cursor after my parent
2080
+ if (!cursor[R] && cursor.parent.parent && cursor.parent.parent.end === this.end && !this.replacedFragment)
2081
+ cursor.insRightOf(cursor.parent.parent);
2082
+ else
2083
+ _super.createLeftOf.call(this, cursor);
2084
+ };
2085
+ _.placeCursor = function(cursor) {
2086
+ this.ends[L].blur();
2087
+ cursor.insRightOf(this);
2088
+ };
2089
+ });
2090
+
2091
+ LatexCmds.rbrace =
2092
+ CharCmds['}'] = bind(CloseBracket, '{','}','\\{','\\}');
2093
+ LatexCmds.rangle =
2094
+ LatexCmds.rang = bind(CloseBracket, '&lang;','&rang;','\\langle ','\\rangle ');
2095
+
2096
+ var parenMixin = function(_, _super) {
2097
+ _.init = function(open, close) {
2098
+ _super.init.call(this, open, close, open, close);
2099
+ };
2100
+ };
2101
+
2102
+ var Paren = P(Bracket, parenMixin);
2103
+
2104
+ LatexCmds.lparen =
2105
+ CharCmds['('] = bind(Paren, '(', ')');
2106
+ LatexCmds.lbrack =
2107
+ LatexCmds.lbracket =
2108
+ CharCmds['['] = bind(Paren, '[', ']');
2109
+
2110
+ var CloseParen = P(CloseBracket, parenMixin);
2111
+
2112
+ LatexCmds.rparen =
2113
+ CharCmds[')'] = bind(CloseParen, '(', ')');
2114
+ LatexCmds.rbrack =
2115
+ LatexCmds.rbracket =
2116
+ CharCmds[']'] = bind(CloseParen, '[', ']');
2117
+
2118
+ var Pipes =
2119
+ LatexCmds.lpipe =
2120
+ LatexCmds.rpipe =
2121
+ CharCmds['|'] = P(Paren, function(_, _super) {
2122
+ _.init = function() {
2123
+ _super.init.call(this, '|', '|');
2124
+ };
2125
+
2126
+ _.createLeftOf = CloseBracket.prototype.createLeftOf;
2127
+ });
2128
+
2129
+ var TextBlock =
2130
+ CharCmds.$ =
2131
+ LatexCmds.text =
2132
+ LatexCmds.textnormal =
2133
+ LatexCmds.textrm =
2134
+ LatexCmds.textup =
2135
+ LatexCmds.textmd = P(MathCommand, function(_, _super) {
2136
+ _.ctrlSeq = '\\text';
2137
+ _.htmlTemplate = '<span class="text">&0</span>';
2138
+ _.replaces = function(replacedText) {
2139
+ if (replacedText instanceof MathFragment)
2140
+ this.replacedText = replacedText.remove().jQ.text();
2141
+ else if (typeof replacedText === 'string')
2142
+ this.replacedText = replacedText;
2143
+ };
2144
+ _.textTemplate = ['"', '"'];
2145
+ _.parser = function() {
2146
+ var self = this;
2147
+
2148
+ // TODO: correctly parse text mode
2149
+ var string = Parser.string;
2150
+ var regex = Parser.regex;
2151
+ var optWhitespace = Parser.optWhitespace;
2152
+ return optWhitespace
2153
+ .then(string('{')).then(regex(/^[^}]*/)).skip(string('}'))
2154
+ .map(function(text) {
2155
+ self.createBlocks();
2156
+ var block = self.ends[L];
2157
+ for (var i = 0; i < text.length; i += 1) {
2158
+ var ch = VanillaSymbol(text.charAt(i));
2159
+ ch.adopt(block, block.ends[R], 0);
2160
+ }
2161
+ return self;
2162
+ })
2163
+ ;
2164
+ };
2165
+ _.createBlocks = function() {
2166
+ //FIXME: another possible Law of Demeter violation, but this seems much cleaner, like it was supposed to be done this way
2167
+ this.ends[L] =
2168
+ this.ends[R] =
2169
+ InnerTextBlock();
2170
+
2171
+ this.blocks = [ this.ends[L] ];
2172
+
2173
+ this.ends[L].parent = this;
2174
+ };
2175
+ _.finalizeInsert = function() {
2176
+ //FIXME HACK blur removes the TextBlock
2177
+ this.ends[L].blur = function() { delete this.blur; return this; };
2178
+ _super.finalizeInsert.call(this);
2179
+ };
2180
+ _.createLeftOf = function(cursor) {
2181
+ _super.createLeftOf.call(this, this.cursor = cursor);
2182
+
2183
+ if (this.replacedText)
2184
+ for (var i = 0; i < this.replacedText.length; i += 1)
2185
+ this.ends[L].write(cursor, this.replacedText.charAt(i));
2186
+ };
2187
+ });
2188
+
2189
+ var InnerTextBlock = P(MathBlock, function(_, _super) {
2190
+ _.onKey = function(key, e) {
2191
+ if (key === 'Spacebar' || key === 'Shift-Spacebar') return false;
2192
+ };
2193
+ // backspace and delete at ends of block don't unwrap
2194
+ _.deleteOutOf = function(dir, cursor) {
2195
+ if (this.isEmpty()) cursor.insRightOf(this.parent);
2196
+ };
2197
+ _.write = function(cursor, ch, replacedFragment) {
2198
+ if (replacedFragment) replacedFragment.remove();
2199
+
2200
+ if (ch !== '$') {
2201
+ var html;
2202
+ if (ch === '<') html = '&lt;';
2203
+ else if (ch === '>') html = '&gt;';
2204
+ VanillaSymbol(ch, html).createLeftOf(cursor);
2205
+ }
2206
+ else if (this.isEmpty()) {
2207
+ cursor.insRightOf(this.parent).backspace();
2208
+ VanillaSymbol('\\$','$').createLeftOf(cursor);
2209
+ }
2210
+ else if (!cursor[R])
2211
+ cursor.insRightOf(this.parent);
2212
+ else if (!cursor[L])
2213
+ cursor.insLeftOf(this.parent);
2214
+ else { //split apart
2215
+ var rightward = TextBlock();
2216
+ rightward.replaces(MathFragment(cursor[R], this.ends[R]));
2217
+
2218
+ cursor.insRightOf(this.parent);
2219
+
2220
+ // FIXME HACK: pretend no prev so they don't get merged when
2221
+ // .createLeftOf() calls blur on the InnerTextBlock
2222
+ rightward.adopt = function() {
2223
+ delete this.adopt;
2224
+ this.adopt.apply(this, arguments);
2225
+ this[L] = 0;
2226
+ };
2227
+ rightward.createLeftOf(cursor);
2228
+ rightward[L] = this.parent;
2229
+
2230
+ cursor.insLeftOf(rightward);
2231
+ }
2232
+ return false;
2233
+ };
2234
+ _.blur = function() {
2235
+ this.jQ.removeClass('hasCursor');
2236
+ if (this.isEmpty()) {
2237
+ var textblock = this.parent, cursor = textblock.cursor;
2238
+ if (cursor.parent === this)
2239
+ this.jQ.addClass('empty');
2240
+ else {
2241
+ cursor.hide();
2242
+ textblock.remove();
2243
+ if (cursor[R] === textblock)
2244
+ cursor[R] = textblock[R];
2245
+ else if (cursor[L] === textblock)
2246
+ cursor[L] = textblock[L];
2247
+
2248
+ cursor.show().parent.bubble('redraw');
2249
+ }
2250
+ }
2251
+ return this;
2252
+ };
2253
+ _.focus = function() {
2254
+ _super.focus.call(this);
2255
+
2256
+ var textblock = this.parent;
2257
+ if (textblock[R].ctrlSeq === textblock.ctrlSeq) { //TODO: seems like there should be a better way to move MathElements around
2258
+ var innerblock = this,
2259
+ cursor = textblock.cursor,
2260
+ rightward = textblock[R].ends[L];
2261
+
2262
+ rightward.eachChild(function(child){
2263
+ child.parent = innerblock;
2264
+ child.jQ.appendTo(innerblock.jQ);
2265
+ });
2266
+
2267
+ if (this.ends[R])
2268
+ this.ends[R][R] = rightward.ends[L];
2269
+ else
2270
+ this.ends[L] = rightward.ends[L];
2271
+
2272
+ rightward.ends[L][L] = this.ends[R];
2273
+ this.ends[R] = rightward.ends[R];
2274
+
2275
+ rightward.parent.remove();
2276
+
2277
+ if (cursor[L])
2278
+ cursor.insRightOf(cursor[L]);
2279
+ else
2280
+ cursor.insAtLeftEnd(this);
2281
+
2282
+ cursor.parent.bubble('redraw');
2283
+ }
2284
+ else if (textblock[L].ctrlSeq === textblock.ctrlSeq) {
2285
+ var cursor = textblock.cursor;
2286
+ if (cursor[L])
2287
+ textblock[L].ends[L].focus();
2288
+ else
2289
+ cursor.insAtRightEnd(textblock[L].ends[L]);
2290
+ }
2291
+ return this;
2292
+ };
2293
+ });
2294
+
2295
+
2296
+ function makeTextBlock(latex, tagName, attrs) {
2297
+ return P(TextBlock, {
2298
+ ctrlSeq: latex,
2299
+ htmlTemplate: '<'+tagName+' '+attrs+'>&0</'+tagName+'>'
2300
+ });
2301
+ }
2302
+
2303
+ LatexCmds.em = LatexCmds.italic = LatexCmds.italics =
2304
+ LatexCmds.emph = LatexCmds.textit = LatexCmds.textsl =
2305
+ makeTextBlock('\\textit', 'i', 'class="text"');
2306
+ LatexCmds.strong = LatexCmds.bold = LatexCmds.textbf =
2307
+ makeTextBlock('\\textbf', 'b', 'class="text"');
2308
+ LatexCmds.sf = LatexCmds.textsf =
2309
+ makeTextBlock('\\textsf', 'span', 'class="sans-serif text"');
2310
+ LatexCmds.tt = LatexCmds.texttt =
2311
+ makeTextBlock('\\texttt', 'span', 'class="monospace text"');
2312
+ LatexCmds.textsc =
2313
+ makeTextBlock('\\textsc', 'span', 'style="font-variant:small-caps" class="text"');
2314
+ LatexCmds.uppercase =
2315
+ makeTextBlock('\\uppercase', 'span', 'style="text-transform:uppercase" class="text"');
2316
+ LatexCmds.lowercase =
2317
+ makeTextBlock('\\lowercase', 'span', 'style="text-transform:lowercase" class="text"');
2318
+
2319
+ // input box to type a variety of LaTeX commands beginning with a backslash
2320
+ var LatexCommandInput =
2321
+ CharCmds['\\'] = P(MathCommand, function(_, _super) {
2322
+ _.ctrlSeq = '\\';
2323
+ _.replaces = function(replacedFragment) {
2324
+ this._replacedFragment = replacedFragment.disown();
2325
+ this.isEmpty = function() { return false; };
2326
+ };
2327
+ _.htmlTemplate = '<span class="latex-command-input non-leaf">\\<span>&0</span></span>';
2328
+ _.textTemplate = ['\\'];
2329
+ _.createBlocks = function() {
2330
+ _super.createBlocks.call(this);
2331
+ this.ends[L].focus = function() {
2332
+ this.parent.jQ.addClass('hasCursor');
2333
+ if (this.isEmpty())
2334
+ this.parent.jQ.removeClass('empty');
2335
+
2336
+ return this;
2337
+ };
2338
+ this.ends[L].blur = function() {
2339
+ this.parent.jQ.removeClass('hasCursor');
2340
+ if (this.isEmpty())
2341
+ this.parent.jQ.addClass('empty');
2342
+
2343
+ return this;
2344
+ };
2345
+ };
2346
+ _.createLeftOf = function(cursor) {
2347
+ _super.createLeftOf.call(this, cursor);
2348
+
2349
+ this.cursor = cursor.insAtRightEnd(this.ends[L]);
2350
+ if (this._replacedFragment) {
2351
+ var el = this.jQ[0];
2352
+ this.jQ =
2353
+ this._replacedFragment.jQ.addClass('blur').bind(
2354
+ 'mousedown mousemove', //FIXME: is monkey-patching the mousedown and mousemove handlers the right way to do this?
2355
+ function(e) {
2356
+ $(e.target = el).trigger(e);
2357
+ return false;
2358
+ }
2359
+ ).insertBefore(this.jQ).add(this.jQ);
2360
+ }
2361
+
2362
+ this.ends[L].write = function(cursor, ch, replacedFragment) {
2363
+ if (replacedFragment) replacedFragment.remove();
2364
+
2365
+ if (ch.match(/[a-z]/i)) VanillaSymbol(ch).createLeftOf(cursor);
2366
+ else {
2367
+ this.parent.renderCommand();
2368
+ if (ch !== '\\' || !this.isEmpty()) this.parent.parent.write(cursor, ch);
2369
+ }
2370
+ };
2371
+ };
2372
+ _.latex = function() {
2373
+ return '\\' + this.ends[L].latex() + ' ';
2374
+ };
2375
+ _.onKey = function(key, e) {
2376
+ if (key === 'Tab' || key === 'Enter' || key === 'Spacebar') {
2377
+ this.renderCommand();
2378
+ e.preventDefault();
2379
+ return false;
2380
+ }
2381
+ };
2382
+ _.renderCommand = function() {
2383
+ this.jQ = this.jQ.last();
2384
+ this.remove();
2385
+ if (this[R]) {
2386
+ this.cursor.insLeftOf(this[R]);
2387
+ } else {
2388
+ this.cursor.insAtRightEnd(this.parent);
2389
+ }
2390
+
2391
+ var latex = this.ends[L].latex(), cmd;
2392
+ if (!latex) latex = 'backslash';
2393
+ this.cursor.insertCmd(latex, this._replacedFragment);
2394
+ };
2395
+ });
2396
+
2397
+ var Binomial =
2398
+ LatexCmds.binom =
2399
+ LatexCmds.binomial = P(MathCommand, function(_, _super) {
2400
+ _.ctrlSeq = '\\binom';
2401
+ _.htmlTemplate =
2402
+ '<span class="paren scaled">(</span>'
2403
+ + '<span class="non-leaf">'
2404
+ + '<span class="array non-leaf">'
2405
+ + '<span>&0</span>'
2406
+ + '<span>&1</span>'
2407
+ + '</span>'
2408
+ + '</span>'
2409
+ + '<span class="paren scaled">)</span>'
2410
+ ;
2411
+ _.textTemplate = ['choose(',',',')'];
2412
+ _.redraw = function() {
2413
+ var blockjQ = this.jQ.eq(1);
2414
+
2415
+ var height = blockjQ.outerHeight()/+blockjQ.css('fontSize').slice(0,-2);
2416
+
2417
+ var parens = this.jQ.filter('.paren');
2418
+ scale(parens, min(1 + .2*(height - 1), 1.2), 1.05*height);
2419
+ };
2420
+ });
2421
+
2422
+ var Choose =
2423
+ LatexCmds.choose = P(Binomial, function(_) {
2424
+ _.createLeftOf = LiveFraction.prototype.createLeftOf;
2425
+ });
2426
+
2427
+ var Vector =
2428
+ LatexCmds.vector = P(MathCommand, function(_, _super) {
2429
+ _.ctrlSeq = '\\vector';
2430
+ _.htmlTemplate = '<span class="array"><span>&0</span></span>';
2431
+ _.latex = function() {
2432
+ return '\\begin{matrix}' + this.foldChildren([], function(latex, child) {
2433
+ latex.push(child.latex());
2434
+ return latex;
2435
+ }).join('\\\\') + '\\end{matrix}';
2436
+ };
2437
+ _.text = function() {
2438
+ return '[' + this.foldChildren([], function(text, child) {
2439
+ text.push(child.text());
2440
+ return text;
2441
+ }).join() + ']';
2442
+ };
2443
+ _.createLeftOf = function(cursor) {
2444
+ _super.createLeftOf.call(this, this.cursor = cursor);
2445
+ };
2446
+ _.onKey = function(key, e) {
2447
+ var currentBlock = this.cursor.parent;
2448
+
2449
+ if (currentBlock.parent === this) {
2450
+ if (key === 'Enter') { //enter
2451
+ var newBlock = MathBlock();
2452
+ newBlock.parent = this;
2453
+ newBlock.jQ = $('<span></span>')
2454
+ .attr(mqBlockId, newBlock.id)
2455
+ .insertAfter(currentBlock.jQ);
2456
+ if (currentBlock[R])
2457
+ currentBlock[R][L] = newBlock;
2458
+ else
2459
+ this.ends[R] = newBlock;
2460
+
2461
+ newBlock[R] = currentBlock[R];
2462
+ currentBlock[R] = newBlock;
2463
+ newBlock[L] = currentBlock;
2464
+ this.bubble('redraw').cursor.insAtRightEnd(newBlock);
2465
+
2466
+ e.preventDefault();
2467
+ return false;
2468
+ }
2469
+ else if (key === 'Tab' && !currentBlock[R]) {
2470
+ if (currentBlock.isEmpty()) {
2471
+ if (currentBlock[L]) {
2472
+ this.cursor.insRightOf(this);
2473
+ delete currentBlock[L][R];
2474
+ this.ends[R] = currentBlock[L];
2475
+ currentBlock.jQ.remove();
2476
+ this.bubble('redraw');
2477
+
2478
+ e.preventDefault();
2479
+ return false;
2480
+ }
2481
+ else
2482
+ return;
2483
+ }
2484
+
2485
+ var newBlock = MathBlock();
2486
+ newBlock.parent = this;
2487
+ newBlock.jQ = $('<span></span>').attr(mqBlockId, newBlock.id).appendTo(this.jQ);
2488
+ this.ends[R] = newBlock;
2489
+ currentBlock[R] = newBlock;
2490
+ newBlock[L] = currentBlock;
2491
+ this.bubble('redraw').cursor.insAtRightEnd(newBlock);
2492
+
2493
+ e.preventDefault();
2494
+ return false;
2495
+ }
2496
+ else if (e.which === 8) { //backspace
2497
+ if (currentBlock.isEmpty()) {
2498
+ if (currentBlock[L]) {
2499
+ this.cursor.insAtRightEnd(currentBlock[L])
2500
+ currentBlock[L][R] = currentBlock[R];
2501
+ }
2502
+ else {
2503
+ this.cursor.insLeftOf(this);
2504
+ this.ends[L] = currentBlock[R];
2505
+ }
2506
+
2507
+ if (currentBlock[R])
2508
+ currentBlock[R][L] = currentBlock[L];
2509
+ else
2510
+ this.ends[R] = currentBlock[L];
2511
+
2512
+ currentBlock.jQ.remove();
2513
+ if (this.isEmpty())
2514
+ this.cursor.deleteForward();
2515
+ else
2516
+ this.bubble('redraw');
2517
+
2518
+ e.preventDefault();
2519
+ return false;
2520
+ }
2521
+ else if (!this.cursor[L]) {
2522
+ e.preventDefault();
2523
+ return false;
2524
+ }
2525
+ }
2526
+ }
2527
+ };
2528
+ });
2529
+
2530
+ LatexCmds.editable = P(RootMathCommand, function(_, _super) {
2531
+ _.init = function() {
2532
+ MathCommand.prototype.init.call(this, '\\editable');
2533
+ };
2534
+
2535
+ _.jQadd = function() {
2536
+ var self = this;
2537
+ // FIXME: this entire method is a giant hack to get around
2538
+ // having to call createBlocks, and createRoot expecting to
2539
+ // render the contents' LaTeX. Both need to be refactored.
2540
+ _super.jQadd.apply(self, arguments);
2541
+ var block = self.ends[L].disown();
2542
+ var blockjQ = self.jQ.children().detach();
2543
+
2544
+ self.ends[L] =
2545
+ self.ends[R] =
2546
+ RootMathBlock();
2547
+
2548
+ self.blocks = [ self.ends[L] ];
2549
+
2550
+ self.ends[L].parent = self;
2551
+
2552
+ createRoot(self.jQ, self.ends[L], false, true);
2553
+ self.cursor = self.ends[L].cursor;
2554
+
2555
+ block.children().adopt(self.ends[L], 0, 0);
2556
+ blockjQ.appendTo(self.ends[L].jQ);
2557
+
2558
+ self.ends[L].cursor.insAtRightEnd(self.ends[L]);
2559
+ };
2560
+
2561
+ _.latex = function(){ return this.ends[L].latex(); };
2562
+ _.text = function(){ return this.ends[L].text(); };
2563
+ });
2564
+ /**********************************
2565
+ * Symbols and Special Characters
2566
+ *********************************/
2567
+
2568
+ LatexCmds.f = bind(Symbol, 'f', '<var class="florin">&fnof;</var><span style="display:inline-block;width:0">&nbsp;</span>');
2569
+
2570
+ var Variable = P(Symbol, function(_, _super) {
2571
+ _.init = function(ch, html) {
2572
+ _super.init.call(this, ch, '<var>'+(html || ch)+'</var>');
2573
+ };
2574
+ _.text = function() {
2575
+ var text = this.ctrlSeq;
2576
+ if (this[L] && !(this[L] instanceof Variable)
2577
+ && !(this[L] instanceof BinaryOperator))
2578
+ text = '*' + text;
2579
+ if (this[R] && !(this[R] instanceof BinaryOperator)
2580
+ && !(this[R].ctrlSeq === '^'))
2581
+ text += '*';
2582
+ return text;
2583
+ };
2584
+ });
2585
+
2586
+ var VanillaSymbol = P(Symbol, function(_, _super) {
2587
+ _.init = function(ch, html) {
2588
+ _super.init.call(this, ch, '<span>'+(html || ch)+'</span>');
2589
+ };
2590
+ });
2591
+
2592
+ CharCmds[' '] = bind(VanillaSymbol, '\\:', ' ');
2593
+
2594
+ LatexCmds.prime = CharCmds["'"] = bind(VanillaSymbol, "'", '&prime;');
2595
+
2596
+ // does not use Symbola font
2597
+ var NonSymbolaSymbol = P(Symbol, function(_, _super) {
2598
+ _.init = function(ch, html) {
2599
+ _super.init.call(this, ch, '<span class="nonSymbola">'+(html || ch)+'</span>');
2600
+ };
2601
+ });
2602
+
2603
+ LatexCmds['@'] = NonSymbolaSymbol;
2604
+ LatexCmds['&'] = bind(NonSymbolaSymbol, '\\&', '&amp;');
2605
+ LatexCmds['%'] = bind(NonSymbolaSymbol, '\\%', '%');
2606
+
2607
+ //the following are all Greek to me, but this helped a lot: http://www.ams.org/STIX/ion/stixsig03.html
2608
+
2609
+ //lowercase Greek letter variables
2610
+ LatexCmds.alpha =
2611
+ LatexCmds.beta =
2612
+ LatexCmds.gamma =
2613
+ LatexCmds.delta =
2614
+ LatexCmds.zeta =
2615
+ LatexCmds.eta =
2616
+ LatexCmds.theta =
2617
+ LatexCmds.iota =
2618
+ LatexCmds.kappa =
2619
+ LatexCmds.mu =
2620
+ LatexCmds.nu =
2621
+ LatexCmds.xi =
2622
+ LatexCmds.rho =
2623
+ LatexCmds.sigma =
2624
+ LatexCmds.tau =
2625
+ LatexCmds.chi =
2626
+ LatexCmds.psi =
2627
+ LatexCmds.omega = P(Variable, function(_, _super) {
2628
+ _.init = function(latex) {
2629
+ _super.init.call(this,'\\'+latex+' ','&'+latex+';');
2630
+ };
2631
+ });
2632
+
2633
+ //why can't anybody FUCKING agree on these
2634
+ LatexCmds.phi = //W3C or Unicode?
2635
+ bind(Variable,'\\phi ','&#981;');
2636
+
2637
+ LatexCmds.phiv = //Elsevier and 9573-13
2638
+ LatexCmds.varphi = //AMS and LaTeX
2639
+ bind(Variable,'\\varphi ','&phi;');
2640
+
2641
+ LatexCmds.epsilon = //W3C or Unicode?
2642
+ bind(Variable,'\\epsilon ','&#1013;');
2643
+
2644
+ LatexCmds.epsiv = //Elsevier and 9573-13
2645
+ LatexCmds.varepsilon = //AMS and LaTeX
2646
+ bind(Variable,'\\varepsilon ','&epsilon;');
2647
+
2648
+ LatexCmds.piv = //W3C/Unicode and Elsevier and 9573-13
2649
+ LatexCmds.varpi = //AMS and LaTeX
2650
+ bind(Variable,'\\varpi ','&piv;');
2651
+
2652
+ LatexCmds.sigmaf = //W3C/Unicode
2653
+ LatexCmds.sigmav = //Elsevier
2654
+ LatexCmds.varsigma = //LaTeX
2655
+ bind(Variable,'\\varsigma ','&sigmaf;');
2656
+
2657
+ LatexCmds.thetav = //Elsevier and 9573-13
2658
+ LatexCmds.vartheta = //AMS and LaTeX
2659
+ LatexCmds.thetasym = //W3C/Unicode
2660
+ bind(Variable,'\\vartheta ','&thetasym;');
2661
+
2662
+ LatexCmds.upsilon = //AMS and LaTeX and W3C/Unicode
2663
+ LatexCmds.upsi = //Elsevier and 9573-13
2664
+ bind(Variable,'\\upsilon ','&upsilon;');
2665
+
2666
+ //these aren't even mentioned in the HTML character entity references
2667
+ LatexCmds.gammad = //Elsevier
2668
+ LatexCmds.Gammad = //9573-13 -- WTF, right? I dunno if this was a typo in the reference (see above)
2669
+ LatexCmds.digamma = //LaTeX
2670
+ bind(Variable,'\\digamma ','&#989;');
2671
+
2672
+ LatexCmds.kappav = //Elsevier
2673
+ LatexCmds.varkappa = //AMS and LaTeX
2674
+ bind(Variable,'\\varkappa ','&#1008;');
2675
+
2676
+ LatexCmds.rhov = //Elsevier and 9573-13
2677
+ LatexCmds.varrho = //AMS and LaTeX
2678
+ bind(Variable,'\\varrho ','&#1009;');
2679
+
2680
+ //Greek constants, look best in un-italicised Times New Roman
2681
+ LatexCmds.pi = LatexCmds['π'] = bind(NonSymbolaSymbol,'\\pi ','&pi;');
2682
+ LatexCmds.lambda = bind(NonSymbolaSymbol,'\\lambda ','&lambda;');
2683
+
2684
+ //uppercase greek letters
2685
+
2686
+ LatexCmds.Upsilon = //LaTeX
2687
+ LatexCmds.Upsi = //Elsevier and 9573-13
2688
+ LatexCmds.upsih = //W3C/Unicode "upsilon with hook"
2689
+ LatexCmds.Upsih = //'cos it makes sense to me
2690
+ bind(Symbol,'\\Upsilon ','<var style="font-family: serif">&upsih;</var>'); //Symbola's 'upsilon with a hook' is a capital Y without hooks :(
2691
+
2692
+ //other symbols with the same LaTeX command and HTML character entity reference
2693
+ LatexCmds.Gamma =
2694
+ LatexCmds.Delta =
2695
+ LatexCmds.Theta =
2696
+ LatexCmds.Lambda =
2697
+ LatexCmds.Xi =
2698
+ LatexCmds.Pi =
2699
+ LatexCmds.Sigma =
2700
+ LatexCmds.Phi =
2701
+ LatexCmds.Psi =
2702
+ LatexCmds.Omega =
2703
+ LatexCmds.forall = P(VanillaSymbol, function(_, _super) {
2704
+ _.init = function(latex) {
2705
+ _super.init.call(this,'\\'+latex+' ','&'+latex+';');
2706
+ };
2707
+ });
2708
+
2709
+ // symbols that aren't a single MathCommand, but are instead a whole
2710
+ // Fragment. Creates the Fragment from a LaTeX string
2711
+ var LatexFragment = P(MathCommand, function(_) {
2712
+ _.init = function(latex) { this.latex = latex; };
2713
+ _.createLeftOf = function(cursor) { cursor.writeLatex(this.latex); };
2714
+ _.parser = function() {
2715
+ var frag = latexMathParser.parse(this.latex).children();
2716
+ return Parser.succeed(frag);
2717
+ };
2718
+ });
2719
+
2720
+ // for what seems to me like [stupid reasons][1], Unicode provides
2721
+ // subscripted and superscripted versions of all ten Arabic numerals,
2722
+ // as well as [so-called "vulgar fractions"][2].
2723
+ // Nobody really cares about most of them, but some of them actually
2724
+ // predate Unicode, dating back to [ISO-8859-1][3], apparently also
2725
+ // known as "Latin-1", which among other things [Windows-1252][4]
2726
+ // largely coincides with, so Microsoft Word sometimes inserts them
2727
+ // and they get copy-pasted into MathQuill.
2728
+ //
2729
+ // (Irrelevant but funny story: Windows-1252 is actually a strict
2730
+ // superset of the "closely related but distinct"[3] "ISO 8859-1" --
2731
+ // see the lack of a dash after "ISO"? Completely different character
2732
+ // set, like elephants vs elephant seals, or "Zombies" vs "Zombie
2733
+ // Redneck Torture Family". What kind of idiot would get them confused.
2734
+ // People in fact got them confused so much, it was so common to
2735
+ // mislabel Windows-1252 text as ISO-8859-1, that most modern web
2736
+ // browsers and email clients treat the MIME charset of ISO-8859-1
2737
+ // as actually Windows-1252, behavior now standard in the HTML5 spec.)
2738
+ //
2739
+ // [1]: http://en.wikipedia.org/wiki/Unicode_subscripts_and_superscripts
2740
+ // [2]: http://en.wikipedia.org/wiki/Number_Forms
2741
+ // [3]: http://en.wikipedia.org/wiki/ISO/IEC_8859-1
2742
+ // [4]: http://en.wikipedia.org/wiki/Windows-1252
2743
+ LatexCmds['¹'] = bind(LatexFragment, '^1');
2744
+ LatexCmds['²'] = bind(LatexFragment, '^2');
2745
+ LatexCmds['³'] = bind(LatexFragment, '^3');
2746
+ LatexCmds['¼'] = bind(LatexFragment, '\\frac14');
2747
+ LatexCmds['½'] = bind(LatexFragment, '\\frac12');
2748
+ LatexCmds['¾'] = bind(LatexFragment, '\\frac34');
2749
+
2750
+ var BinaryOperator = P(Symbol, function(_, _super) {
2751
+ _.init = function(ctrlSeq, html, text) {
2752
+ _super.init.call(this,
2753
+ ctrlSeq, '<span class="binary-operator">'+html+'</span>', text
2754
+ );
2755
+ };
2756
+ });
2757
+
2758
+ var PlusMinus = P(BinaryOperator, function(_) {
2759
+ _.init = VanillaSymbol.prototype.init;
2760
+
2761
+ _.respace = function() {
2762
+ if (!this[L]) {
2763
+ this.jQ[0].className = '';
2764
+ }
2765
+ else if (
2766
+ this[L] instanceof BinaryOperator &&
2767
+ this[R] && !(this[R] instanceof BinaryOperator)
2768
+ ) {
2769
+ this.jQ[0].className = 'unary-operator';
2770
+ }
2771
+ else {
2772
+ this.jQ[0].className = 'binary-operator';
2773
+ }
2774
+ return this;
2775
+ };
2776
+ });
2777
+
2778
+ LatexCmds['+'] = bind(PlusMinus, '+', '+');
2779
+ //yes, these are different dashes, I think one is an en dash and the other is a hyphen
2780
+ LatexCmds['–'] = LatexCmds['-'] = bind(PlusMinus, '-', '&minus;');
2781
+ LatexCmds['±'] = LatexCmds.pm = LatexCmds.plusmn = LatexCmds.plusminus =
2782
+ bind(PlusMinus,'\\pm ','&plusmn;');
2783
+ LatexCmds.mp = LatexCmds.mnplus = LatexCmds.minusplus =
2784
+ bind(PlusMinus,'\\mp ','&#8723;');
2785
+
2786
+ CharCmds['*'] = LatexCmds.sdot = LatexCmds.cdot =
2787
+ bind(BinaryOperator, '\\cdot ', '&middot;');
2788
+ //semantically should be &sdot;, but &middot; looks better
2789
+
2790
+ LatexCmds['='] = bind(BinaryOperator, '=', '=');
2791
+ LatexCmds['<'] = bind(BinaryOperator, '<', '&lt;');
2792
+ LatexCmds['>'] = bind(BinaryOperator, '>', '&gt;');
2793
+
2794
+ LatexCmds.notin =
2795
+ LatexCmds.sim =
2796
+ LatexCmds.cong =
2797
+ LatexCmds.equiv =
2798
+ LatexCmds.oplus =
2799
+ LatexCmds.otimes = P(BinaryOperator, function(_, _super) {
2800
+ _.init = function(latex) {
2801
+ _super.init.call(this, '\\'+latex+' ', '&'+latex+';');
2802
+ };
2803
+ });
2804
+
2805
+ LatexCmds.times = bind(BinaryOperator, '\\times ', '&times;', '[x]');
2806
+
2807
+ LatexCmds['÷'] = LatexCmds.div = LatexCmds.divide = LatexCmds.divides =
2808
+ bind(BinaryOperator,'\\div ','&divide;', '[/]');
2809
+
2810
+ LatexCmds['≠'] = LatexCmds.ne = LatexCmds.neq = bind(BinaryOperator,'\\ne ','&ne;');
2811
+
2812
+ LatexCmds.ast = LatexCmds.star = LatexCmds.loast = LatexCmds.lowast =
2813
+ bind(BinaryOperator,'\\ast ','&lowast;');
2814
+ //case 'there4 = // a special exception for this one, perhaps?
2815
+ LatexCmds.therefor = LatexCmds.therefore =
2816
+ bind(BinaryOperator,'\\therefore ','&there4;');
2817
+
2818
+ LatexCmds.cuz = // l33t
2819
+ LatexCmds.because = bind(BinaryOperator,'\\because ','&#8757;');
2820
+
2821
+ LatexCmds.prop = LatexCmds.propto = bind(BinaryOperator,'\\propto ','&prop;');
2822
+
2823
+ LatexCmds['≈'] = LatexCmds.asymp = LatexCmds.approx = bind(BinaryOperator,'\\approx ','&asymp;');
2824
+
2825
+ LatexCmds.lt = bind(BinaryOperator,'<','&lt;');
2826
+
2827
+ LatexCmds.gt = bind(BinaryOperator,'>','&gt;');
2828
+
2829
+ LatexCmds['≤'] = LatexCmds.le = LatexCmds.leq = bind(BinaryOperator,'\\le ','&le;');
2830
+
2831
+ LatexCmds['≥'] = LatexCmds.ge = LatexCmds.geq = bind(BinaryOperator,'\\ge ','&ge;');
2832
+
2833
+ LatexCmds.isin = LatexCmds['in'] = bind(BinaryOperator,'\\in ','&isin;');
2834
+
2835
+ LatexCmds.ni = LatexCmds.contains = bind(BinaryOperator,'\\ni ','&ni;');
2836
+
2837
+ LatexCmds.notni = LatexCmds.niton = LatexCmds.notcontains = LatexCmds.doesnotcontain =
2838
+ bind(BinaryOperator,'\\not\\ni ','&#8716;');
2839
+
2840
+ LatexCmds.sub = LatexCmds.subset = bind(BinaryOperator,'\\subset ','&sub;');
2841
+
2842
+ LatexCmds.sup = LatexCmds.supset = LatexCmds.superset =
2843
+ bind(BinaryOperator,'\\supset ','&sup;');
2844
+
2845
+ LatexCmds.nsub = LatexCmds.notsub =
2846
+ LatexCmds.nsubset = LatexCmds.notsubset =
2847
+ bind(BinaryOperator,'\\not\\subset ','&#8836;');
2848
+
2849
+ LatexCmds.nsup = LatexCmds.notsup =
2850
+ LatexCmds.nsupset = LatexCmds.notsupset =
2851
+ LatexCmds.nsuperset = LatexCmds.notsuperset =
2852
+ bind(BinaryOperator,'\\not\\supset ','&#8837;');
2853
+
2854
+ LatexCmds.sube = LatexCmds.subeq = LatexCmds.subsete = LatexCmds.subseteq =
2855
+ bind(BinaryOperator,'\\subseteq ','&sube;');
2856
+
2857
+ LatexCmds.supe = LatexCmds.supeq =
2858
+ LatexCmds.supsete = LatexCmds.supseteq =
2859
+ LatexCmds.supersete = LatexCmds.superseteq =
2860
+ bind(BinaryOperator,'\\supseteq ','&supe;');
2861
+
2862
+ LatexCmds.nsube = LatexCmds.nsubeq =
2863
+ LatexCmds.notsube = LatexCmds.notsubeq =
2864
+ LatexCmds.nsubsete = LatexCmds.nsubseteq =
2865
+ LatexCmds.notsubsete = LatexCmds.notsubseteq =
2866
+ bind(BinaryOperator,'\\not\\subseteq ','&#8840;');
2867
+
2868
+ LatexCmds.nsupe = LatexCmds.nsupeq =
2869
+ LatexCmds.notsupe = LatexCmds.notsupeq =
2870
+ LatexCmds.nsupsete = LatexCmds.nsupseteq =
2871
+ LatexCmds.notsupsete = LatexCmds.notsupseteq =
2872
+ LatexCmds.nsupersete = LatexCmds.nsuperseteq =
2873
+ LatexCmds.notsupersete = LatexCmds.notsuperseteq =
2874
+ bind(BinaryOperator,'\\not\\supseteq ','&#8841;');
2875
+
2876
+
2877
+ //sum, product, coproduct, integral
2878
+ var BigSymbol = P(Symbol, function(_, _super) {
2879
+ _.init = function(ch, html) {
2880
+ _super.init.call(this, ch, '<big>'+html+'</big>');
2881
+ };
2882
+ });
2883
+
2884
+ LatexCmds['∑'] = LatexCmds.sum = LatexCmds.summation = bind(BigSymbol,'\\sum ','&sum;');
2885
+ LatexCmds['∏'] = LatexCmds.prod = LatexCmds.product = bind(BigSymbol,'\\prod ','&prod;');
2886
+ LatexCmds.coprod = LatexCmds.coproduct = bind(BigSymbol,'\\coprod ','&#8720;');
2887
+ LatexCmds['∫'] = LatexCmds['int'] = LatexCmds.integral = bind(BigSymbol,'\\int ','&int;');
2888
+
2889
+
2890
+
2891
+ //the canonical sets of numbers
2892
+ LatexCmds.N = LatexCmds.naturals = LatexCmds.Naturals =
2893
+ bind(VanillaSymbol,'\\mathbb{N}','&#8469;');
2894
+
2895
+ LatexCmds.P =
2896
+ LatexCmds.primes = LatexCmds.Primes =
2897
+ LatexCmds.projective = LatexCmds.Projective =
2898
+ LatexCmds.probability = LatexCmds.Probability =
2899
+ bind(VanillaSymbol,'\\mathbb{P}','&#8473;');
2900
+
2901
+ LatexCmds.Z = LatexCmds.integers = LatexCmds.Integers =
2902
+ bind(VanillaSymbol,'\\mathbb{Z}','&#8484;');
2903
+
2904
+ LatexCmds.Q = LatexCmds.rationals = LatexCmds.Rationals =
2905
+ bind(VanillaSymbol,'\\mathbb{Q}','&#8474;');
2906
+
2907
+ LatexCmds.R = LatexCmds.reals = LatexCmds.Reals =
2908
+ bind(VanillaSymbol,'\\mathbb{R}','&#8477;');
2909
+
2910
+ LatexCmds.C =
2911
+ LatexCmds.complex = LatexCmds.Complex =
2912
+ LatexCmds.complexes = LatexCmds.Complexes =
2913
+ LatexCmds.complexplane = LatexCmds.Complexplane = LatexCmds.ComplexPlane =
2914
+ bind(VanillaSymbol,'\\mathbb{C}','&#8450;');
2915
+
2916
+ LatexCmds.H = LatexCmds.Hamiltonian = LatexCmds.quaternions = LatexCmds.Quaternions =
2917
+ bind(VanillaSymbol,'\\mathbb{H}','&#8461;');
2918
+
2919
+ //spacing
2920
+ LatexCmds.quad = LatexCmds.emsp = bind(VanillaSymbol,'\\quad ',' ');
2921
+ LatexCmds.qquad = bind(VanillaSymbol,'\\qquad ',' ');
2922
+ /* spacing special characters, gonna have to implement this in LatexCommandInput::onText somehow
2923
+ case ',':
2924
+ return VanillaSymbol('\\, ',' ');
2925
+ case ':':
2926
+ return VanillaSymbol('\\: ',' ');
2927
+ case ';':
2928
+ return VanillaSymbol('\\; ',' ');
2929
+ case '!':
2930
+ return Symbol('\\! ','<span style="margin-right:-.2em"></span>');
2931
+ */
2932
+
2933
+ //binary operators
2934
+ LatexCmds.diamond = bind(VanillaSymbol, '\\diamond ', '&#9671;');
2935
+ LatexCmds.bigtriangleup = bind(VanillaSymbol, '\\bigtriangleup ', '&#9651;');
2936
+ LatexCmds.ominus = bind(VanillaSymbol, '\\ominus ', '&#8854;');
2937
+ LatexCmds.uplus = bind(VanillaSymbol, '\\uplus ', '&#8846;');
2938
+ LatexCmds.bigtriangledown = bind(VanillaSymbol, '\\bigtriangledown ', '&#9661;');
2939
+ LatexCmds.sqcap = bind(VanillaSymbol, '\\sqcap ', '&#8851;');
2940
+ LatexCmds.triangleleft = bind(VanillaSymbol, '\\triangleleft ', '&#8882;');
2941
+ LatexCmds.sqcup = bind(VanillaSymbol, '\\sqcup ', '&#8852;');
2942
+ LatexCmds.triangleright = bind(VanillaSymbol, '\\triangleright ', '&#8883;');
2943
+ LatexCmds.odot = bind(VanillaSymbol, '\\odot ', '&#8857;');
2944
+ LatexCmds.bigcirc = bind(VanillaSymbol, '\\bigcirc ', '&#9711;');
2945
+ LatexCmds.dagger = bind(VanillaSymbol, '\\dagger ', '&#0134;');
2946
+ LatexCmds.ddagger = bind(VanillaSymbol, '\\ddagger ', '&#135;');
2947
+ LatexCmds.wr = bind(VanillaSymbol, '\\wr ', '&#8768;');
2948
+ LatexCmds.amalg = bind(VanillaSymbol, '\\amalg ', '&#8720;');
2949
+
2950
+ //relationship symbols
2951
+ LatexCmds.models = bind(VanillaSymbol, '\\models ', '&#8872;');
2952
+ LatexCmds.prec = bind(VanillaSymbol, '\\prec ', '&#8826;');
2953
+ LatexCmds.succ = bind(VanillaSymbol, '\\succ ', '&#8827;');
2954
+ LatexCmds.preceq = bind(VanillaSymbol, '\\preceq ', '&#8828;');
2955
+ LatexCmds.succeq = bind(VanillaSymbol, '\\succeq ', '&#8829;');
2956
+ LatexCmds.simeq = bind(VanillaSymbol, '\\simeq ', '&#8771;');
2957
+ LatexCmds.mid = bind(VanillaSymbol, '\\mid ', '&#8739;');
2958
+ LatexCmds.ll = bind(VanillaSymbol, '\\ll ', '&#8810;');
2959
+ LatexCmds.gg = bind(VanillaSymbol, '\\gg ', '&#8811;');
2960
+ LatexCmds.parallel = bind(VanillaSymbol, '\\parallel ', '&#8741;');
2961
+ LatexCmds.bowtie = bind(VanillaSymbol, '\\bowtie ', '&#8904;');
2962
+ LatexCmds.sqsubset = bind(VanillaSymbol, '\\sqsubset ', '&#8847;');
2963
+ LatexCmds.sqsupset = bind(VanillaSymbol, '\\sqsupset ', '&#8848;');
2964
+ LatexCmds.smile = bind(VanillaSymbol, '\\smile ', '&#8995;');
2965
+ LatexCmds.sqsubseteq = bind(VanillaSymbol, '\\sqsubseteq ', '&#8849;');
2966
+ LatexCmds.sqsupseteq = bind(VanillaSymbol, '\\sqsupseteq ', '&#8850;');
2967
+ LatexCmds.doteq = bind(VanillaSymbol, '\\doteq ', '&#8784;');
2968
+ LatexCmds.frown = bind(VanillaSymbol, '\\frown ', '&#8994;');
2969
+ LatexCmds.vdash = bind(VanillaSymbol, '\\vdash ', '&#8870;');
2970
+ LatexCmds.dashv = bind(VanillaSymbol, '\\dashv ', '&#8867;');
2971
+
2972
+ //arrows
2973
+ LatexCmds.longleftarrow = bind(VanillaSymbol, '\\longleftarrow ', '&#8592;');
2974
+ LatexCmds.longrightarrow = bind(VanillaSymbol, '\\longrightarrow ', '&#8594;');
2975
+ LatexCmds.Longleftarrow = bind(VanillaSymbol, '\\Longleftarrow ', '&#8656;');
2976
+ LatexCmds.Longrightarrow = bind(VanillaSymbol, '\\Longrightarrow ', '&#8658;');
2977
+ LatexCmds.longleftrightarrow = bind(VanillaSymbol, '\\longleftrightarrow ', '&#8596;');
2978
+ LatexCmds.updownarrow = bind(VanillaSymbol, '\\updownarrow ', '&#8597;');
2979
+ LatexCmds.Longleftrightarrow = bind(VanillaSymbol, '\\Longleftrightarrow ', '&#8660;');
2980
+ LatexCmds.Updownarrow = bind(VanillaSymbol, '\\Updownarrow ', '&#8661;');
2981
+ LatexCmds.mapsto = bind(VanillaSymbol, '\\mapsto ', '&#8614;');
2982
+ LatexCmds.nearrow = bind(VanillaSymbol, '\\nearrow ', '&#8599;');
2983
+ LatexCmds.hookleftarrow = bind(VanillaSymbol, '\\hookleftarrow ', '&#8617;');
2984
+ LatexCmds.hookrightarrow = bind(VanillaSymbol, '\\hookrightarrow ', '&#8618;');
2985
+ LatexCmds.searrow = bind(VanillaSymbol, '\\searrow ', '&#8600;');
2986
+ LatexCmds.leftharpoonup = bind(VanillaSymbol, '\\leftharpoonup ', '&#8636;');
2987
+ LatexCmds.rightharpoonup = bind(VanillaSymbol, '\\rightharpoonup ', '&#8640;');
2988
+ LatexCmds.swarrow = bind(VanillaSymbol, '\\swarrow ', '&#8601;');
2989
+ LatexCmds.leftharpoondown = bind(VanillaSymbol, '\\leftharpoondown ', '&#8637;');
2990
+ LatexCmds.rightharpoondown = bind(VanillaSymbol, '\\rightharpoondown ', '&#8641;');
2991
+ LatexCmds.nwarrow = bind(VanillaSymbol, '\\nwarrow ', '&#8598;');
2992
+
2993
+ //Misc
2994
+ LatexCmds.ldots = bind(VanillaSymbol, '\\ldots ', '&#8230;');
2995
+ LatexCmds.cdots = bind(VanillaSymbol, '\\cdots ', '&#8943;');
2996
+ LatexCmds.vdots = bind(VanillaSymbol, '\\vdots ', '&#8942;');
2997
+ LatexCmds.ddots = bind(VanillaSymbol, '\\ddots ', '&#8944;');
2998
+ LatexCmds.surd = bind(VanillaSymbol, '\\surd ', '&#8730;');
2999
+ LatexCmds.triangle = bind(VanillaSymbol, '\\triangle ', '&#9653;');
3000
+ LatexCmds.ell = bind(VanillaSymbol, '\\ell ', '&#8467;');
3001
+ LatexCmds.top = bind(VanillaSymbol, '\\top ', '&#8868;');
3002
+ LatexCmds.flat = bind(VanillaSymbol, '\\flat ', '&#9837;');
3003
+ LatexCmds.natural = bind(VanillaSymbol, '\\natural ', '&#9838;');
3004
+ LatexCmds.sharp = bind(VanillaSymbol, '\\sharp ', '&#9839;');
3005
+ LatexCmds.wp = bind(VanillaSymbol, '\\wp ', '&#8472;');
3006
+ LatexCmds.bot = bind(VanillaSymbol, '\\bot ', '&#8869;');
3007
+ LatexCmds.clubsuit = bind(VanillaSymbol, '\\clubsuit ', '&#9827;');
3008
+ LatexCmds.diamondsuit = bind(VanillaSymbol, '\\diamondsuit ', '&#9826;');
3009
+ LatexCmds.heartsuit = bind(VanillaSymbol, '\\heartsuit ', '&#9825;');
3010
+ LatexCmds.spadesuit = bind(VanillaSymbol, '\\spadesuit ', '&#9824;');
3011
+
3012
+ //variable-sized
3013
+ LatexCmds.oint = bind(VanillaSymbol, '\\oint ', '&#8750;');
3014
+ LatexCmds.bigcap = bind(VanillaSymbol, '\\bigcap ', '&#8745;');
3015
+ LatexCmds.bigcup = bind(VanillaSymbol, '\\bigcup ', '&#8746;');
3016
+ LatexCmds.bigsqcup = bind(VanillaSymbol, '\\bigsqcup ', '&#8852;');
3017
+ LatexCmds.bigvee = bind(VanillaSymbol, '\\bigvee ', '&#8744;');
3018
+ LatexCmds.bigwedge = bind(VanillaSymbol, '\\bigwedge ', '&#8743;');
3019
+ LatexCmds.bigodot = bind(VanillaSymbol, '\\bigodot ', '&#8857;');
3020
+ LatexCmds.bigotimes = bind(VanillaSymbol, '\\bigotimes ', '&#8855;');
3021
+ LatexCmds.bigoplus = bind(VanillaSymbol, '\\bigoplus ', '&#8853;');
3022
+ LatexCmds.biguplus = bind(VanillaSymbol, '\\biguplus ', '&#8846;');
3023
+
3024
+ //delimiters
3025
+ LatexCmds.lfloor = bind(VanillaSymbol, '\\lfloor ', '&#8970;');
3026
+ LatexCmds.rfloor = bind(VanillaSymbol, '\\rfloor ', '&#8971;');
3027
+ LatexCmds.lceil = bind(VanillaSymbol, '\\lceil ', '&#8968;');
3028
+ LatexCmds.rceil = bind(VanillaSymbol, '\\rceil ', '&#8969;');
3029
+ LatexCmds.slash = bind(VanillaSymbol, '\\slash ', '&#47;');
3030
+ LatexCmds.opencurlybrace = bind(VanillaSymbol, '\\opencurlybrace ', '&#123;');
3031
+ LatexCmds.closecurlybrace = bind(VanillaSymbol, '\\closecurlybrace ', '&#125;');
3032
+
3033
+ //various symbols
3034
+
3035
+ LatexCmds.caret = bind(VanillaSymbol,'\\caret ','^');
3036
+ LatexCmds.underscore = bind(VanillaSymbol,'\\underscore ','_');
3037
+ LatexCmds.backslash = bind(VanillaSymbol,'\\backslash ','\\');
3038
+ LatexCmds.vert = bind(VanillaSymbol,'|');
3039
+ LatexCmds.perp = LatexCmds.perpendicular = bind(VanillaSymbol,'\\perp ','&perp;');
3040
+ LatexCmds.nabla = LatexCmds.del = bind(VanillaSymbol,'\\nabla ','&nabla;');
3041
+ LatexCmds.hbar = bind(VanillaSymbol,'\\hbar ','&#8463;');
3042
+
3043
+ LatexCmds.AA = LatexCmds.Angstrom = LatexCmds.angstrom =
3044
+ bind(VanillaSymbol,'\\text\\AA ','&#8491;');
3045
+
3046
+ LatexCmds.ring = LatexCmds.circ = LatexCmds.circle =
3047
+ bind(VanillaSymbol,'\\circ ','&#8728;');
3048
+
3049
+ LatexCmds.bull = LatexCmds.bullet = bind(VanillaSymbol,'\\bullet ','&bull;');
3050
+
3051
+ LatexCmds.setminus = LatexCmds.smallsetminus =
3052
+ bind(VanillaSymbol,'\\setminus ','&#8726;');
3053
+
3054
+ LatexCmds.not = //bind(Symbol,'\\not ','<span class="not">/</span>');
3055
+ LatexCmds['¬'] = LatexCmds.neg = bind(VanillaSymbol,'\\neg ','&not;');
3056
+
3057
+ LatexCmds['…'] = LatexCmds.dots = LatexCmds.ellip = LatexCmds.hellip =
3058
+ LatexCmds.ellipsis = LatexCmds.hellipsis =
3059
+ bind(VanillaSymbol,'\\dots ','&hellip;');
3060
+
3061
+ LatexCmds.converges =
3062
+ LatexCmds.darr = LatexCmds.dnarr = LatexCmds.dnarrow = LatexCmds.downarrow =
3063
+ bind(VanillaSymbol,'\\downarrow ','&darr;');
3064
+
3065
+ LatexCmds.dArr = LatexCmds.dnArr = LatexCmds.dnArrow = LatexCmds.Downarrow =
3066
+ bind(VanillaSymbol,'\\Downarrow ','&dArr;');
3067
+
3068
+ LatexCmds.diverges = LatexCmds.uarr = LatexCmds.uparrow =
3069
+ bind(VanillaSymbol,'\\uparrow ','&uarr;');
3070
+
3071
+ LatexCmds.uArr = LatexCmds.Uparrow = bind(VanillaSymbol,'\\Uparrow ','&uArr;');
3072
+
3073
+ LatexCmds.to = bind(BinaryOperator,'\\to ','&rarr;');
3074
+
3075
+ LatexCmds.rarr = LatexCmds.rightarrow = bind(VanillaSymbol,'\\rightarrow ','&rarr;');
3076
+
3077
+ LatexCmds.implies = bind(BinaryOperator,'\\Rightarrow ','&rArr;');
3078
+
3079
+ LatexCmds.rArr = LatexCmds.Rightarrow = bind(VanillaSymbol,'\\Rightarrow ','&rArr;');
3080
+
3081
+ LatexCmds.gets = bind(BinaryOperator,'\\gets ','&larr;');
3082
+
3083
+ LatexCmds.larr = LatexCmds.leftarrow = bind(VanillaSymbol,'\\leftarrow ','&larr;');
3084
+
3085
+ LatexCmds.impliedby = bind(BinaryOperator,'\\Leftarrow ','&lArr;');
3086
+
3087
+ LatexCmds.lArr = LatexCmds.Leftarrow = bind(VanillaSymbol,'\\Leftarrow ','&lArr;');
3088
+
3089
+ LatexCmds.harr = LatexCmds.lrarr = LatexCmds.leftrightarrow =
3090
+ bind(VanillaSymbol,'\\leftrightarrow ','&harr;');
3091
+
3092
+ LatexCmds.iff = bind(BinaryOperator,'\\Leftrightarrow ','&hArr;');
3093
+
3094
+ LatexCmds.hArr = LatexCmds.lrArr = LatexCmds.Leftrightarrow =
3095
+ bind(VanillaSymbol,'\\Leftrightarrow ','&hArr;');
3096
+
3097
+ LatexCmds.Re = LatexCmds.Real = LatexCmds.real = bind(VanillaSymbol,'\\Re ','&real;');
3098
+
3099
+ LatexCmds.Im = LatexCmds.imag =
3100
+ LatexCmds.image = LatexCmds.imagin = LatexCmds.imaginary = LatexCmds.Imaginary =
3101
+ bind(VanillaSymbol,'\\Im ','&image;');
3102
+
3103
+ LatexCmds.part = LatexCmds.partial = bind(VanillaSymbol,'\\partial ','&part;');
3104
+
3105
+ LatexCmds.inf = LatexCmds.infin = LatexCmds.infty = LatexCmds.infinity =
3106
+ bind(VanillaSymbol,'\\infty ','&infin;');
3107
+
3108
+ LatexCmds.alef = LatexCmds.alefsym = LatexCmds.aleph = LatexCmds.alephsym =
3109
+ bind(VanillaSymbol,'\\aleph ','&alefsym;');
3110
+
3111
+ LatexCmds.xist = //LOL
3112
+ LatexCmds.xists = LatexCmds.exist = LatexCmds.exists =
3113
+ bind(VanillaSymbol,'\\exists ','&exist;');
3114
+
3115
+ LatexCmds.and = LatexCmds.land = LatexCmds.wedge =
3116
+ bind(VanillaSymbol,'\\wedge ','&and;');
3117
+
3118
+ LatexCmds.or = LatexCmds.lor = LatexCmds.vee = bind(VanillaSymbol,'\\vee ','&or;');
3119
+
3120
+ LatexCmds.o = LatexCmds.O =
3121
+ LatexCmds.empty = LatexCmds.emptyset =
3122
+ LatexCmds.oslash = LatexCmds.Oslash =
3123
+ LatexCmds.nothing = LatexCmds.varnothing =
3124
+ bind(BinaryOperator,'\\varnothing ','&empty;');
3125
+
3126
+ LatexCmds.cup = LatexCmds.union = bind(BinaryOperator,'\\cup ','&cup;');
3127
+
3128
+ LatexCmds.cap = LatexCmds.intersect = LatexCmds.intersection =
3129
+ bind(BinaryOperator,'\\cap ','&cap;');
3130
+
3131
+ LatexCmds.deg = LatexCmds.degree = bind(VanillaSymbol,'^\\circ ','&deg;');
3132
+
3133
+ LatexCmds.ang = LatexCmds.angle = bind(VanillaSymbol,'\\angle ','&ang;');
3134
+
3135
+
3136
+ var NonItalicizedFunction = P(Symbol, function(_, _super) {
3137
+ _.init = function(fn) {
3138
+ _super.init.call(this, '\\'+fn+' ', '<span>'+fn+'</span>');
3139
+ };
3140
+ _.respace = function()
3141
+ {
3142
+ this.jQ[0].className =
3143
+ (this[R] instanceof SupSub || this[R] instanceof Bracket) ?
3144
+ '' : 'non-italicized-function';
3145
+ };
3146
+ });
3147
+
3148
+ LatexCmds.ln =
3149
+ LatexCmds.lg =
3150
+ LatexCmds.log =
3151
+ LatexCmds.span =
3152
+ LatexCmds.proj =
3153
+ LatexCmds.det =
3154
+ LatexCmds.dim =
3155
+ LatexCmds.min =
3156
+ LatexCmds.max =
3157
+ LatexCmds.mod =
3158
+ LatexCmds.lcm =
3159
+ LatexCmds.gcd =
3160
+ LatexCmds.gcf =
3161
+ LatexCmds.hcf =
3162
+ LatexCmds.lim = NonItalicizedFunction;
3163
+
3164
+ (function() {
3165
+ var trig = ['sin', 'cos', 'tan', 'sec', 'cosec', 'csc', 'cotan', 'cot'];
3166
+ for (var i in trig) {
3167
+ LatexCmds[trig[i]] =
3168
+ LatexCmds[trig[i]+'h'] =
3169
+ LatexCmds['a'+trig[i]] = LatexCmds['arc'+trig[i]] =
3170
+ LatexCmds['a'+trig[i]+'h'] = LatexCmds['arc'+trig[i]+'h'] =
3171
+ NonItalicizedFunction;
3172
+ }
3173
+ }());
3174
+
3175
+ // Parser MathCommand
3176
+ var latexMathParser = (function() {
3177
+ function commandToBlock(cmd) {
3178
+ var block = MathBlock();
3179
+ cmd.adopt(block, 0, 0);
3180
+ return block;
3181
+ }
3182
+ function joinBlocks(blocks) {
3183
+ var firstBlock = blocks[0] || MathBlock();
3184
+
3185
+ for (var i = 1; i < blocks.length; i += 1) {
3186
+ blocks[i].children().adopt(firstBlock, firstBlock.ends[R], 0);
3187
+ }
3188
+
3189
+ return firstBlock;
3190
+ }
3191
+
3192
+ var string = Parser.string;
3193
+ var regex = Parser.regex;
3194
+ var letter = Parser.letter;
3195
+ var any = Parser.any;
3196
+ var optWhitespace = Parser.optWhitespace;
3197
+ var succeed = Parser.succeed;
3198
+ var fail = Parser.fail;
3199
+
3200
+ // Parsers yielding MathCommands
3201
+ var variable = letter.map(Variable);
3202
+ var symbol = regex(/^[^${}\\_^]/).map(VanillaSymbol);
3203
+
3204
+ var controlSequence =
3205
+ regex(/^[^\\a-eg-zA-Z]/) // hotfix #164; match MathBlock::write
3206
+ .or(string('\\').then(
3207
+ regex(/^[a-z]+/i)
3208
+ .or(regex(/^\s+/).result(' '))
3209
+ .or(any)
3210
+ )).then(function(ctrlSeq) {
3211
+ var cmdKlass = LatexCmds[ctrlSeq];
3212
+
3213
+ if (cmdKlass) {
3214
+ return cmdKlass(ctrlSeq).parser();
3215
+ }
3216
+ else {
3217
+ return fail('unknown command: \\'+ctrlSeq);
3218
+ }
3219
+ })
3220
+ ;
3221
+
3222
+ var command =
3223
+ controlSequence
3224
+ .or(variable)
3225
+ .or(symbol)
3226
+ ;
3227
+
3228
+ // Parsers yielding MathBlocks
3229
+ var mathGroup = string('{').then(function() { return mathSequence; }).skip(string('}'));
3230
+ var mathBlock = optWhitespace.then(mathGroup.or(command.map(commandToBlock)));
3231
+ var mathSequence = mathBlock.many().map(joinBlocks).skip(optWhitespace);
3232
+
3233
+ var optMathBlock =
3234
+ string('[').then(
3235
+ mathBlock.then(function(block) {
3236
+ return block.join('latex') !== ']' ? succeed(block) : fail();
3237
+ })
3238
+ .many().map(joinBlocks).skip(optWhitespace)
3239
+ ).skip(string(']'))
3240
+ ;
3241
+
3242
+ var latexMath = mathSequence;
3243
+
3244
+ latexMath.block = mathBlock;
3245
+ latexMath.optBlock = optMathBlock;
3246
+ return latexMath;
3247
+ })();
3248
+ /********************************************
3249
+ * Cursor and Selection "singleton" classes
3250
+ *******************************************/
3251
+
3252
+ /* The main thing that manipulates the Math DOM. Makes sure to manipulate the
3253
+ HTML DOM to match. */
3254
+
3255
+ /* Sort of singletons, since there should only be one per editable math
3256
+ textbox, but any one HTML document can contain many such textboxes, so any one
3257
+ JS environment could actually contain many instances. */
3258
+
3259
+ //A fake cursor in the fake textbox that the math is rendered in.
3260
+ var Cursor = P(Point, function(_) {
3261
+ _.init = function(root) {
3262
+ this.parent = this.root = root;
3263
+ var jQ = this.jQ = this._jQ = $('<span class="cursor">&zwj;</span>');
3264
+
3265
+ //closured for setInterval
3266
+ this.blink = function(){ jQ.toggleClass('blink'); };
3267
+
3268
+ this.upDownCache = {};
3269
+ };
3270
+
3271
+ _.show = function() {
3272
+ this.jQ = this._jQ.removeClass('blink');
3273
+ if ('intervalId' in this) //already was shown, just restart interval
3274
+ clearInterval(this.intervalId);
3275
+ else { //was hidden and detached, insert this.jQ back into HTML DOM
3276
+ if (this[R]) {
3277
+ if (this.selection && this.selection.ends[L][L] === this[L])
3278
+ this.jQ.insertBefore(this.selection.jQ);
3279
+ else
3280
+ this.jQ.insertBefore(this[R].jQ.first());
3281
+ }
3282
+ else
3283
+ this.jQ.appendTo(this.parent.jQ);
3284
+ this.parent.focus();
3285
+ }
3286
+ this.intervalId = setInterval(this.blink, 500);
3287
+ return this;
3288
+ };
3289
+ _.hide = function() {
3290
+ if ('intervalId' in this)
3291
+ clearInterval(this.intervalId);
3292
+ delete this.intervalId;
3293
+ this.jQ.detach();
3294
+ this.jQ = $();
3295
+ return this;
3296
+ };
3297
+
3298
+ _.withDirInsertAt = function(dir, parent, withDir, oppDir) {
3299
+ var oldParent = this.parent;
3300
+ this.parent = parent;
3301
+ this[dir] = withDir;
3302
+ this[-dir] = oppDir;
3303
+ oldParent.blur();
3304
+ };
3305
+ _.insDirOf = function(dir, el) {
3306
+ prayDirection(dir);
3307
+ this.withDirInsertAt(dir, el.parent, el[dir], el);
3308
+ this.parent.jQ.addClass('hasCursor');
3309
+ this.jQ.insDirOf(dir, el.jQ);
3310
+ return this;
3311
+ };
3312
+ _.insLeftOf = function(el) { return this.insDirOf(L, el); };
3313
+ _.insRightOf = function(el) { return this.insDirOf(R, el); };
3314
+
3315
+ _.insAtDirEnd = function(dir, el) {
3316
+ prayDirection(dir);
3317
+ this.withDirInsertAt(dir, el, 0, el.ends[dir]);
3318
+
3319
+ // never insert before textarea
3320
+ if (dir === L && el.textarea) {
3321
+ this.jQ.insDirOf(-dir, el.textarea);
3322
+ }
3323
+ else {
3324
+ this.jQ.insAtDirEnd(dir, el.jQ);
3325
+ }
3326
+
3327
+ el.focus();
3328
+
3329
+ return this;
3330
+ };
3331
+ _.insAtLeftEnd = function(el) { return this.insAtDirEnd(L, el); };
3332
+ _.insAtRightEnd = function(el) { return this.insAtDirEnd(R, el); };
3333
+
3334
+ _.hopDir = function(dir) {
3335
+ prayDirection(dir);
3336
+
3337
+ this.jQ.insDirOf(dir, this[dir].jQ);
3338
+ this[-dir] = this[dir];
3339
+ this[dir] = this[dir][dir];
3340
+ return this;
3341
+ };
3342
+ _.hopLeft = function() { return this.hopDir(L); };
3343
+ _.hopRight = function() { return this.hopDir(R); };
3344
+
3345
+ _.moveDirWithin = function(dir, block) {
3346
+ prayDirection(dir);
3347
+
3348
+ if (this[dir]) {
3349
+ if (this[dir].ends[-dir]) this.insAtDirEnd(-dir, this[dir].ends[-dir]);
3350
+ else this.hopDir(dir);
3351
+ }
3352
+ else {
3353
+ // we're at the beginning/end of the containing block, so do nothing
3354
+ if (this.parent === block) return;
3355
+
3356
+ if (this.parent[dir]) this.insAtDirEnd(-dir, this.parent[dir]);
3357
+ else this.insDirOf(dir, this.parent.parent);
3358
+ }
3359
+ };
3360
+ _.moveLeftWithin = function(block) {
3361
+ return this.moveDirWithin(L, block);
3362
+ };
3363
+ _.moveRightWithin = function(block) {
3364
+ return this.moveDirWithin(R, block);
3365
+ };
3366
+ _.moveDir = function(dir) {
3367
+ prayDirection(dir);
3368
+
3369
+ clearUpDownCache(this);
3370
+
3371
+ if (this.selection) {
3372
+ this.insDirOf(dir, this.selection.ends[dir]).clearSelection();
3373
+ }
3374
+ else {
3375
+ this.moveDirWithin(dir, this.root);
3376
+ }
3377
+
3378
+ return this.show();
3379
+ };
3380
+ _.moveLeft = function() { return this.moveDir(L); };
3381
+ _.moveRight = function() { return this.moveDir(R); };
3382
+
3383
+ /**
3384
+ * moveUp and moveDown have almost identical algorithms:
3385
+ * - first check left and right, if so insAtLeft/RightEnd of them
3386
+ * - else check the parent's 'up'/'down' property - if it's a function,
3387
+ * call it with the cursor as the sole argument and use the return value.
3388
+ *
3389
+ * Given undefined, will bubble up to the next ancestor block.
3390
+ * Given false, will stop bubbling.
3391
+ * Given a MathBlock,
3392
+ * + moveUp will insAtRightEnd of it
3393
+ * + moveDown will insAtLeftEnd of it
3394
+ *
3395
+ */
3396
+ _.moveUp = function() { return moveUpDown(this, 'up'); };
3397
+ _.moveDown = function() { return moveUpDown(this, 'down'); };
3398
+ function moveUpDown(self, dir) {
3399
+ if (self[R][dir]) self.insAtLeftEnd(self[R][dir]);
3400
+ else if (self[L][dir]) self.insAtRightEnd(self[L][dir]);
3401
+ else {
3402
+ var ancestorBlock = self.parent;
3403
+ do {
3404
+ var prop = ancestorBlock[dir];
3405
+ if (prop) {
3406
+ if (typeof prop === 'function') prop = ancestorBlock[dir](self);
3407
+ if (prop === false || prop instanceof MathBlock) {
3408
+ self.upDownCache[ancestorBlock.id] = Point(self.parent, self[L], self[R]);
3409
+
3410
+ if (prop instanceof MathBlock) {
3411
+ var cached = self.upDownCache[prop.id];
3412
+
3413
+ if (cached) {
3414
+ if (cached[R]) {
3415
+ self.insLeftOf(cached[R]);
3416
+ } else {
3417
+ self.insAtRightEnd(cached.parent);
3418
+ }
3419
+ } else {
3420
+ var pageX = offset(self).left;
3421
+ self.insAtRightEnd(prop);
3422
+ self.seekHoriz(pageX, prop);
3423
+ }
3424
+ }
3425
+ break;
3426
+ }
3427
+ }
3428
+ ancestorBlock = ancestorBlock.parent.parent;
3429
+ } while (ancestorBlock);
3430
+ }
3431
+
3432
+ return self.clearSelection().show();
3433
+ }
3434
+
3435
+ _.seek = function(target, pageX, pageY) {
3436
+ clearUpDownCache(this);
3437
+ var cmd, block, cursor = this.clearSelection().show();
3438
+ if (target.hasClass('empty')) {
3439
+ cursor.insAtLeftEnd(MathElement[target.attr(mqBlockId)]);
3440
+ return cursor;
3441
+ }
3442
+
3443
+ cmd = MathElement[target.attr(mqCmdId)];
3444
+ if (cmd instanceof Symbol) { //insert at whichever side is closer
3445
+ if (target.outerWidth() > 2*(pageX - target.offset().left))
3446
+ cursor.insLeftOf(cmd);
3447
+ else
3448
+ cursor.insRightOf(cmd);
3449
+
3450
+ return cursor;
3451
+ }
3452
+ if (!cmd) {
3453
+ block = MathElement[target.attr(mqBlockId)];
3454
+ if (!block) { //if no MathQuill data, try parent, if still no, just start from the root
3455
+ target = target.parent();
3456
+ cmd = MathElement[target.attr(mqCmdId)];
3457
+ if (!cmd) {
3458
+ block = MathElement[target.attr(mqBlockId)];
3459
+ if (!block) block = cursor.root;
3460
+ }
3461
+ }
3462
+ }
3463
+
3464
+ if (cmd)
3465
+ cursor.insRightOf(cmd);
3466
+ else
3467
+ cursor.insAtRightEnd(block);
3468
+
3469
+ return cursor.seekHoriz(pageX, cursor.root);
3470
+ };
3471
+ _.seekHoriz = function(pageX, block) {
3472
+ //move cursor to position closest to click
3473
+ var cursor = this;
3474
+ var dist = offset(cursor).left - pageX;
3475
+ var leftDist;
3476
+
3477
+ do {
3478
+ cursor.moveLeftWithin(block);
3479
+ leftDist = dist;
3480
+ dist = offset(cursor).left - pageX;
3481
+ }
3482
+ while (dist > 0 && (cursor[L] || cursor.parent !== block));
3483
+
3484
+ if (-dist > leftDist) cursor.moveRightWithin(block);
3485
+
3486
+ return cursor;
3487
+ };
3488
+ function offset(self) {
3489
+ //in Opera 11.62, .getBoundingClientRect() and hence jQuery::offset()
3490
+ //returns all 0's on inline elements with negative margin-right (like
3491
+ //the cursor) at the end of their parent, so temporarily remove the
3492
+ //negative margin-right when calling jQuery::offset()
3493
+ //Opera bug DSK-360043
3494
+ //http://bugs.jquery.com/ticket/11523
3495
+ //https://github.com/jquery/jquery/pull/717
3496
+ var offset = self.jQ.removeClass('cursor').offset();
3497
+ self.jQ.addClass('cursor');
3498
+ return offset;
3499
+ }
3500
+ _.writeLatex = function(latex) {
3501
+ var self = this;
3502
+ clearUpDownCache(self);
3503
+ self.show().deleteSelection();
3504
+
3505
+ var all = Parser.all;
3506
+ var eof = Parser.eof;
3507
+
3508
+ var block = latexMathParser.skip(eof).or(all.result(false)).parse(latex);
3509
+
3510
+ if (block) {
3511
+ block.children().adopt(self.parent, self[L], self[R]);
3512
+ MathElement.jQize(block.join('html')).insertBefore(self.jQ);
3513
+ self[L] = block.ends[R];
3514
+ block.finalizeInsert();
3515
+ self.parent.bubble('redraw');
3516
+ }
3517
+
3518
+ return this.hide();
3519
+ };
3520
+ _.write = function(ch) {
3521
+ var seln = this.prepareWrite();
3522
+ return this.insertCh(ch, seln);
3523
+ };
3524
+ _.insertCh = function(ch, replacedFragment) {
3525
+ this.parent.write(this, ch, replacedFragment);
3526
+ return this;
3527
+ };
3528
+ _.insertCmd = function(latexCmd, replacedFragment) {
3529
+ var cmd = LatexCmds[latexCmd];
3530
+ if (cmd) {
3531
+ cmd = cmd(latexCmd);
3532
+ if (replacedFragment) cmd.replaces(replacedFragment);
3533
+ cmd.createLeftOf(this);
3534
+ }
3535
+ else {
3536
+ cmd = TextBlock();
3537
+ cmd.replaces(latexCmd);
3538
+ cmd.ends[L].focus = function(){ delete this.focus; return this; };
3539
+ cmd.createLeftOf(this);
3540
+ this.insRightOf(cmd);
3541
+ if (replacedFragment)
3542
+ replacedFragment.remove();
3543
+ }
3544
+ return this;
3545
+ };
3546
+ _.unwrapGramp = function() {
3547
+ var gramp = this.parent.parent;
3548
+ var greatgramp = gramp.parent;
3549
+ var rightward = gramp[R];
3550
+ var cursor = this;
3551
+
3552
+ var leftward = gramp[L];
3553
+ gramp.disown().eachChild(function(uncle) {
3554
+ if (uncle.isEmpty()) return;
3555
+
3556
+ uncle.children()
3557
+ .adopt(greatgramp, leftward, rightward)
3558
+ .each(function(cousin) {
3559
+ cousin.jQ.insertBefore(gramp.jQ.first());
3560
+ })
3561
+ ;
3562
+
3563
+ leftward = uncle.ends[R];
3564
+ });
3565
+
3566
+ if (!this[R]) { //then find something to be rightward to insLeftOf
3567
+ if (this[L])
3568
+ this[R] = this[L][R];
3569
+ else {
3570
+ while (!this[R]) {
3571
+ this.parent = this.parent[R];
3572
+ if (this.parent)
3573
+ this[R] = this.parent.ends[L];
3574
+ else {
3575
+ this[R] = gramp[R];
3576
+ this.parent = greatgramp;
3577
+ break;
3578
+ }
3579
+ }
3580
+ }
3581
+ }
3582
+ if (this[R])
3583
+ this.insLeftOf(this[R]);
3584
+ else
3585
+ this.insAtRightEnd(greatgramp);
3586
+
3587
+ gramp.jQ.remove();
3588
+
3589
+ if (gramp[L])
3590
+ gramp[L].respace();
3591
+ if (gramp[R])
3592
+ gramp[R].respace();
3593
+ };
3594
+ _.deleteDir = function(dir) {
3595
+ prayDirection(dir);
3596
+ clearUpDownCache(this);
3597
+ this.show();
3598
+
3599
+ if (this.deleteSelection()); // pass
3600
+ else if (this[dir]) {
3601
+ if (this[dir].isEmpty())
3602
+ this[dir] = this[dir].remove()[dir];
3603
+ else
3604
+ this.selectDir(dir);
3605
+ }
3606
+ else if (this.parent !== this.root) {
3607
+ if (this.parent.parent.isEmpty())
3608
+ return this.insDirOf(-dir, this.parent.parent).deleteDir(dir);
3609
+ else
3610
+ this.unwrapGramp();
3611
+ }
3612
+
3613
+ if (this[L])
3614
+ this[L].respace();
3615
+ if (this[R])
3616
+ this[R].respace();
3617
+ this.parent.bubble('redraw');
3618
+
3619
+ return this;
3620
+ };
3621
+ _.backspace = function() { return this.deleteDir(L); };
3622
+ _.deleteForward = function() { return this.deleteDir(R); };
3623
+ _.selectFrom = function(anticursor) {
3624
+ //find ancestors of each with common parent
3625
+ var oneA = this, otherA = anticursor; //one ancestor, the other ancestor
3626
+ loopThroughAncestors: while (true) {
3627
+ for (var oneI = this; oneI !== oneA.parent.parent; oneI = oneI.parent.parent) //one intermediate, the other intermediate
3628
+ if (oneI.parent === otherA.parent) {
3629
+ left = oneI;
3630
+ right = otherA;
3631
+ break loopThroughAncestors;
3632
+ }
3633
+
3634
+ for (var otherI = anticursor; otherI !== otherA.parent.parent; otherI = otherI.parent.parent)
3635
+ if (oneA.parent === otherI.parent) {
3636
+ left = oneA;
3637
+ right = otherI;
3638
+ break loopThroughAncestors;
3639
+ }
3640
+
3641
+ if (oneA.parent.parent)
3642
+ oneA = oneA.parent.parent;
3643
+ if (otherA.parent.parent)
3644
+ otherA = otherA.parent.parent;
3645
+ }
3646
+ //figure out which is leftward and which is rightward
3647
+ var left, right, leftRight;
3648
+ if (left[R] !== right) {
3649
+ for (var rightward = left; rightward; rightward = rightward[R]) {
3650
+ if (rightward === right[L]) {
3651
+ leftRight = true;
3652
+ break;
3653
+ }
3654
+ }
3655
+ if (!leftRight) {
3656
+ leftRight = right;
3657
+ right = left;
3658
+ left = leftRight;
3659
+ }
3660
+ }
3661
+ this.hide().selection = Selection(left[L][R] || left.parent.ends[L], right[R][L] || right.parent.ends[R]);
3662
+ this.insRightOf(right[R][L] || right.parent.ends[R]);
3663
+ this.root.selectionChanged();
3664
+ };
3665
+ _.selectDir = function(dir) {
3666
+ prayDirection(dir);
3667
+ clearUpDownCache(this);
3668
+
3669
+ if (this.selection) {
3670
+ // if cursor is at the (dir) edge of selection
3671
+ if (this.selection.ends[dir] === this[-dir]) {
3672
+ // then extend (dir) if possible
3673
+ if (this[dir]) this.hopDir(dir).selection.extendDir(dir);
3674
+ // else level up if possible
3675
+ else if (this.parent !== this.root) {
3676
+ this.insDirOf(dir, this.parent.parent).selection.levelUp();
3677
+ }
3678
+ }
3679
+ // else cursor is at the (-dir) edge of selection, retract if possible
3680
+ else {
3681
+ this.hopDir(dir);
3682
+
3683
+ // clear the selection if we only have one thing selected
3684
+ if (this.selection.ends[dir] === this.selection.ends[-dir]) {
3685
+ this.clearSelection().show();
3686
+ return;
3687
+ }
3688
+
3689
+ this.selection.retractDir(dir);
3690
+ }
3691
+ }
3692
+ // no selection, create one
3693
+ else {
3694
+ if (this[dir]) this.hopDir(dir);
3695
+ // else edge of a block
3696
+ else {
3697
+ if (this.parent === this.root) return;
3698
+
3699
+ this.insDirOf(dir, this.parent.parent);
3700
+ }
3701
+
3702
+ this.hide().selection = Selection(this[-dir]);
3703
+ }
3704
+
3705
+ this.root.selectionChanged();
3706
+ };
3707
+ _.selectLeft = function() { return this.selectDir(L); };
3708
+ _.selectRight = function() { return this.selectDir(R); };
3709
+
3710
+ function clearUpDownCache(self) {
3711
+ self.upDownCache = {};
3712
+ }
3713
+
3714
+ _.prepareMove = function() {
3715
+ clearUpDownCache(this);
3716
+ return this.show().clearSelection();
3717
+ };
3718
+ _.prepareEdit = function() {
3719
+ clearUpDownCache(this);
3720
+ return this.show().deleteSelection();
3721
+ };
3722
+ _.prepareWrite = function() {
3723
+ clearUpDownCache(this);
3724
+ return this.show().replaceSelection();
3725
+ };
3726
+
3727
+ _.clearSelection = function() {
3728
+ if (this.selection) {
3729
+ this.selection.clear();
3730
+ delete this.selection;
3731
+ this.root.selectionChanged();
3732
+ }
3733
+ return this;
3734
+ };
3735
+ _.deleteSelection = function() {
3736
+ if (!this.selection) return false;
3737
+
3738
+ this[L] = this.selection.ends[L][L];
3739
+ this[R] = this.selection.ends[R][R];
3740
+ this.selection.remove();
3741
+ this.root.selectionChanged();
3742
+ return delete this.selection;
3743
+ };
3744
+ _.replaceSelection = function() {
3745
+ var seln = this.selection;
3746
+ if (seln) {
3747
+ this[L] = seln.ends[L][L];
3748
+ this[R] = seln.ends[R][R];
3749
+ delete this.selection;
3750
+ }
3751
+ return seln;
3752
+ };
3753
+ });
3754
+
3755
+ var Selection = P(MathFragment, function(_, _super) {
3756
+ _.init = function() {
3757
+ var frag = this;
3758
+ _super.init.apply(frag, arguments);
3759
+
3760
+ frag.jQwrap(frag.jQ);
3761
+ };
3762
+ _.jQwrap = function(children) {
3763
+ this.jQ = children.wrapAll('<span class="selection"></span>').parent();
3764
+ //can't do wrapAll(this.jQ = $(...)) because wrapAll will clone it
3765
+ };
3766
+ _.adopt = function() {
3767
+ this.jQ.replaceWith(this.jQ = this.jQ.children());
3768
+ return _super.adopt.apply(this, arguments);
3769
+ };
3770
+ _.clear = function() {
3771
+ this.jQ.replaceWith(this.jQ.children());
3772
+ return this;
3773
+ };
3774
+ _.levelUp = function() {
3775
+ var seln = this,
3776
+ gramp = seln.ends[L] = seln.ends[R] = seln.ends[R].parent.parent;
3777
+ seln.clear().jQwrap(gramp.jQ);
3778
+ return seln;
3779
+ };
3780
+ _.extendDir = function(dir) {
3781
+ prayDirection(dir);
3782
+ this.ends[dir] = this.ends[dir][dir];
3783
+ this.ends[dir].jQ.insAtDirEnd(dir, this.jQ);
3784
+ return this;
3785
+ };
3786
+ _.extendLeft = function() { return this.extendDir(L); };
3787
+ _.extendRight = function() { return this.extendDir(R); };
3788
+
3789
+ _.retractDir = function(dir) {
3790
+ prayDirection(dir);
3791
+ this.ends[-dir].jQ.insDirOf(-dir, this.jQ);
3792
+ this.ends[-dir] = this.ends[-dir][dir];
3793
+ };
3794
+ _.retractRight = function() { return this.retractDir(R); };
3795
+ _.retractLeft = function() { return this.retractDir(L); };
3796
+ });
3797
+ /*********************************************************
3798
+ * The actual jQuery plugin and document ready handlers.
3799
+ ********************************************************/
3800
+
3801
+ //The publicy exposed method of jQuery.prototype, available (and meant to be
3802
+ //called) on jQuery-wrapped HTML DOM elements.
3803
+ jQuery.fn.mathquill = function(cmd, latex) {
3804
+ switch (cmd) {
3805
+ case 'redraw':
3806
+ return this.each(function() {
3807
+ var blockId = $(this).attr(mqBlockId),
3808
+ rootBlock = blockId && MathElement[blockId];
3809
+ if (rootBlock) {
3810
+ (function postOrderRedraw(el) {
3811
+ el.eachChild(postOrderRedraw);
3812
+ if (el.redraw) el.redraw();
3813
+ }(rootBlock));
3814
+ }
3815
+ });
3816
+ case 'revert':
3817
+ return this.each(function() {
3818
+ var blockId = $(this).attr(mqBlockId),
3819
+ block = blockId && MathElement[blockId];
3820
+ if (block && block.revert)
3821
+ block.revert();
3822
+ });
3823
+ case 'latex':
3824
+ if (arguments.length > 1) {
3825
+ return this.each(function() {
3826
+ var blockId = $(this).attr(mqBlockId),
3827
+ block = blockId && MathElement[blockId];
3828
+ if (block)
3829
+ block.renderLatex(latex);
3830
+ });
3831
+ }
3832
+
3833
+ var blockId = $(this).attr(mqBlockId),
3834
+ block = blockId && MathElement[blockId];
3835
+ return block && block.latex();
3836
+ case 'text':
3837
+ var blockId = $(this).attr(mqBlockId),
3838
+ block = blockId && MathElement[blockId];
3839
+ return block && block.text();
3840
+ case 'html':
3841
+ return this.html().replace(/ ?hasCursor|hasCursor /, '')
3842
+ .replace(/ class=(""|(?= |>))/g, '')
3843
+ .replace(/<span class="?cursor( blink)?"?><\/span>/i, '')
3844
+ .replace(/<span class="?textarea"?><textarea><\/textarea><\/span>/i, '');
3845
+ case 'write':
3846
+ if (arguments.length > 1)
3847
+ return this.each(function() {
3848
+ var blockId = $(this).attr(mqBlockId),
3849
+ block = blockId && MathElement[blockId],
3850
+ cursor = block && block.cursor;
3851
+
3852
+ if (cursor)
3853
+ cursor.writeLatex(latex).parent.blur();
3854
+ });
3855
+ case 'cmd':
3856
+ if (arguments.length > 1)
3857
+ return this.each(function() {
3858
+ var blockId = $(this).attr(mqBlockId),
3859
+ block = blockId && MathElement[blockId],
3860
+ cursor = block && block.cursor;
3861
+
3862
+ if (cursor) {
3863
+ var seln = cursor.prepareWrite();
3864
+ if (/^\\[a-z]+$/i.test(latex)) cursor.insertCmd(latex.slice(1), seln);
3865
+ else cursor.insertCh(latex, seln);
3866
+ cursor.hide().parent.blur();
3867
+ }
3868
+ });
3869
+ default:
3870
+ var textbox = cmd === 'textbox',
3871
+ editable = textbox || cmd === 'editable',
3872
+ RootBlock = textbox ? RootTextBlock : RootMathBlock;
3873
+ return this.each(function() {
3874
+ createRoot($(this), RootBlock(), textbox, editable);
3875
+ });
3876
+ }
3877
+ };
3878
+
3879
+ //on document ready, mathquill-ify all `<tag class="mathquill-*">latex</tag>`
3880
+ //elements according to their CSS class.
3881
+ jQuery(function() {
3882
+ jQuery('.mathquill-editable:not(.mathquill-rendered-math)').mathquill('editable');
3883
+ jQuery('.mathquill-textbox:not(.mathquill-rendered-math)').mathquill('textbox');
3884
+ jQuery('.mathquill-embedded-latex').mathquill();
3885
+ });
3886
+
3887
+
3888
+ }());