baidu_umeditor_rails 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/app/assets/javascripts/umeditor.js +2 -0
- data/baidu_umeditor_rails.gemspec +23 -0
- data/lib/baidu_umeditor_rails/asset_installer.rb +64 -0
- data/lib/baidu_umeditor_rails/asset_manifest.rb +120 -0
- data/lib/baidu_umeditor_rails/engine.rb +20 -0
- data/lib/baidu_umeditor_rails/version.rb +4 -0
- data/lib/baidu_umeditor_rails.rb +3 -0
- data/lib/tasks/baidu_umeditor_rails_tasks.rake +15 -0
- data/vendor/assets/javascripts/umeditor/dialogs/emotion/emotion.css +87 -0
- data/vendor/assets/javascripts/umeditor/dialogs/emotion/emotion.js +272 -0
- data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/0.gif +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/bface.gif +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/cface.gif +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/fface.gif +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/jxface2.gif +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/neweditor-tab-bg.png +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/tface.gif +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/wface.gif +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/emotion/images/yface.gif +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/formula/formula.css +32 -0
- data/vendor/assets/javascripts/umeditor/dialogs/formula/formula.html +212 -0
- data/vendor/assets/javascripts/umeditor/dialogs/formula/formula.js +124 -0
- data/vendor/assets/javascripts/umeditor/dialogs/formula/images/formula.png +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/image/image.css +42 -0
- data/vendor/assets/javascripts/umeditor/dialogs/image/image.js +445 -0
- data/vendor/assets/javascripts/umeditor/dialogs/image/images/close.png +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/image/images/upload1.png +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/image/images/upload2.png +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/link/link.js +73 -0
- data/vendor/assets/javascripts/umeditor/dialogs/map/map.html +148 -0
- data/vendor/assets/javascripts/umeditor/dialogs/map/map.js +263 -0
- data/vendor/assets/javascripts/umeditor/dialogs/video/images/center_focus.jpg +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/video/images/left_focus.jpg +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/video/images/none_focus.jpg +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/video/images/right_focus.jpg +0 -0
- data/vendor/assets/javascripts/umeditor/dialogs/video/video.css +59 -0
- data/vendor/assets/javascripts/umeditor/dialogs/video/video.js +282 -0
- data/vendor/assets/javascripts/umeditor/index.html +277 -0
- data/vendor/assets/javascripts/umeditor/lang/en/en.js +150 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/addimage.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/alldeletebtnhoverskin.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/alldeletebtnupskin.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/background.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/button.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/copy.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/deletedisable.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/deleteenable.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/imglabel.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/listbackground.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/localimage.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/music.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/rotateleftdisable.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/rotateleftenable.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/rotaterightdisable.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/rotaterightenable.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/en/images/upload.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/zh-cn/images/copy.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/zh-cn/images/imglabel.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/zh-cn/images/localimage.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/zh-cn/images/music.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/zh-cn/images/upload.png +0 -0
- data/vendor/assets/javascripts/umeditor/lang/zh-cn/zh-cn.js +150 -0
- data/vendor/assets/javascripts/umeditor/themes/default/css/umeditor.css +773 -0
- data/vendor/assets/javascripts/umeditor/themes/default/css/umeditor.min.css +8 -0
- data/vendor/assets/javascripts/umeditor/themes/default/images/caret.png +0 -0
- data/vendor/assets/javascripts/umeditor/themes/default/images/close.png +0 -0
- data/vendor/assets/javascripts/umeditor/themes/default/images/icons.gif +0 -0
- data/vendor/assets/javascripts/umeditor/themes/default/images/icons.png +0 -0
- data/vendor/assets/javascripts/umeditor/themes/default/images/ok.gif +0 -0
- data/vendor/assets/javascripts/umeditor/themes/default/images/pop-bg.png +0 -0
- data/vendor/assets/javascripts/umeditor/themes/default/images/spacer.gif +0 -0
- data/vendor/assets/javascripts/umeditor/themes/default/images/videologo.gif +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/jquery.min.js +6 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/Symbola.eot +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/Symbola.otf +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/Symbola.svg +5102 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/Symbola.ttf +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/Symbola.woff +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/STIXFontLicense2010.txt +103 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneral-webfont.eot +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneral-webfont.svg +3318 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneral-webfont.ttf +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneral-webfont.woff +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbol-webfont.eot +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbol-webfont.svg +1738 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbol-webfont.ttf +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbol-webfont.woff +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbolita-webfont.eot +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbolita-webfont.svg +1137 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbolita-webfont.ttf +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralbolita-webfont.woff +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralitalic-webfont.eot +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralitalic-webfont.svg +1089 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralitalic-webfont.ttf +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/font/stixgeneral-bundle/stixgeneralitalic-webfont.woff +0 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/mathquill.css +357 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/mathquill.js +3888 -0
- data/vendor/assets/javascripts/umeditor/third-party/mathquill/mathquill.min.js +2 -0
- data/vendor/assets/javascripts/umeditor/umeditor.config.js +249 -0
- data/vendor/assets/javascripts/umeditor/umeditor.js +10923 -0
- data/vendor/assets/javascripts/umeditor/umeditor.min.js +264 -0
- metadata +179 -0
Binary file
|
Binary file
|
@@ -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 '&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 = '<';
|
1696
|
+
else if (ch === '>') html = '>';
|
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"> </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">√</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">→</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">√</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, '⟨','⟩','\\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, '⟨','⟩','\\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 = '<';
|
2203
|
+
else if (ch === '>') html = '>';
|
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">ƒ</var><span style="display:inline-block;width:0"> </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, "'", '′');
|
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, '\\&', '&');
|
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 ','ϕ');
|
2636
|
+
|
2637
|
+
LatexCmds.phiv = //Elsevier and 9573-13
|
2638
|
+
LatexCmds.varphi = //AMS and LaTeX
|
2639
|
+
bind(Variable,'\\varphi ','φ');
|
2640
|
+
|
2641
|
+
LatexCmds.epsilon = //W3C or Unicode?
|
2642
|
+
bind(Variable,'\\epsilon ','ϵ');
|
2643
|
+
|
2644
|
+
LatexCmds.epsiv = //Elsevier and 9573-13
|
2645
|
+
LatexCmds.varepsilon = //AMS and LaTeX
|
2646
|
+
bind(Variable,'\\varepsilon ','ε');
|
2647
|
+
|
2648
|
+
LatexCmds.piv = //W3C/Unicode and Elsevier and 9573-13
|
2649
|
+
LatexCmds.varpi = //AMS and LaTeX
|
2650
|
+
bind(Variable,'\\varpi ','ϖ');
|
2651
|
+
|
2652
|
+
LatexCmds.sigmaf = //W3C/Unicode
|
2653
|
+
LatexCmds.sigmav = //Elsevier
|
2654
|
+
LatexCmds.varsigma = //LaTeX
|
2655
|
+
bind(Variable,'\\varsigma ','ς');
|
2656
|
+
|
2657
|
+
LatexCmds.thetav = //Elsevier and 9573-13
|
2658
|
+
LatexCmds.vartheta = //AMS and LaTeX
|
2659
|
+
LatexCmds.thetasym = //W3C/Unicode
|
2660
|
+
bind(Variable,'\\vartheta ','ϑ');
|
2661
|
+
|
2662
|
+
LatexCmds.upsilon = //AMS and LaTeX and W3C/Unicode
|
2663
|
+
LatexCmds.upsi = //Elsevier and 9573-13
|
2664
|
+
bind(Variable,'\\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 ','ϝ');
|
2671
|
+
|
2672
|
+
LatexCmds.kappav = //Elsevier
|
2673
|
+
LatexCmds.varkappa = //AMS and LaTeX
|
2674
|
+
bind(Variable,'\\varkappa ','ϰ');
|
2675
|
+
|
2676
|
+
LatexCmds.rhov = //Elsevier and 9573-13
|
2677
|
+
LatexCmds.varrho = //AMS and LaTeX
|
2678
|
+
bind(Variable,'\\varrho ','ϱ');
|
2679
|
+
|
2680
|
+
//Greek constants, look best in un-italicised Times New Roman
|
2681
|
+
LatexCmds.pi = LatexCmds['π'] = bind(NonSymbolaSymbol,'\\pi ','π');
|
2682
|
+
LatexCmds.lambda = bind(NonSymbolaSymbol,'\\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">ϒ</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, '-', '−');
|
2781
|
+
LatexCmds['±'] = LatexCmds.pm = LatexCmds.plusmn = LatexCmds.plusminus =
|
2782
|
+
bind(PlusMinus,'\\pm ','±');
|
2783
|
+
LatexCmds.mp = LatexCmds.mnplus = LatexCmds.minusplus =
|
2784
|
+
bind(PlusMinus,'\\mp ','∓');
|
2785
|
+
|
2786
|
+
CharCmds['*'] = LatexCmds.sdot = LatexCmds.cdot =
|
2787
|
+
bind(BinaryOperator, '\\cdot ', '·');
|
2788
|
+
//semantically should be ⋅, but · looks better
|
2789
|
+
|
2790
|
+
LatexCmds['='] = bind(BinaryOperator, '=', '=');
|
2791
|
+
LatexCmds['<'] = bind(BinaryOperator, '<', '<');
|
2792
|
+
LatexCmds['>'] = bind(BinaryOperator, '>', '>');
|
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 ', '×', '[x]');
|
2806
|
+
|
2807
|
+
LatexCmds['÷'] = LatexCmds.div = LatexCmds.divide = LatexCmds.divides =
|
2808
|
+
bind(BinaryOperator,'\\div ','÷', '[/]');
|
2809
|
+
|
2810
|
+
LatexCmds['≠'] = LatexCmds.ne = LatexCmds.neq = bind(BinaryOperator,'\\ne ','≠');
|
2811
|
+
|
2812
|
+
LatexCmds.ast = LatexCmds.star = LatexCmds.loast = LatexCmds.lowast =
|
2813
|
+
bind(BinaryOperator,'\\ast ','∗');
|
2814
|
+
//case 'there4 = // a special exception for this one, perhaps?
|
2815
|
+
LatexCmds.therefor = LatexCmds.therefore =
|
2816
|
+
bind(BinaryOperator,'\\therefore ','∴');
|
2817
|
+
|
2818
|
+
LatexCmds.cuz = // l33t
|
2819
|
+
LatexCmds.because = bind(BinaryOperator,'\\because ','∵');
|
2820
|
+
|
2821
|
+
LatexCmds.prop = LatexCmds.propto = bind(BinaryOperator,'\\propto ','∝');
|
2822
|
+
|
2823
|
+
LatexCmds['≈'] = LatexCmds.asymp = LatexCmds.approx = bind(BinaryOperator,'\\approx ','≈');
|
2824
|
+
|
2825
|
+
LatexCmds.lt = bind(BinaryOperator,'<','<');
|
2826
|
+
|
2827
|
+
LatexCmds.gt = bind(BinaryOperator,'>','>');
|
2828
|
+
|
2829
|
+
LatexCmds['≤'] = LatexCmds.le = LatexCmds.leq = bind(BinaryOperator,'\\le ','≤');
|
2830
|
+
|
2831
|
+
LatexCmds['≥'] = LatexCmds.ge = LatexCmds.geq = bind(BinaryOperator,'\\ge ','≥');
|
2832
|
+
|
2833
|
+
LatexCmds.isin = LatexCmds['in'] = bind(BinaryOperator,'\\in ','∈');
|
2834
|
+
|
2835
|
+
LatexCmds.ni = LatexCmds.contains = bind(BinaryOperator,'\\ni ','∋');
|
2836
|
+
|
2837
|
+
LatexCmds.notni = LatexCmds.niton = LatexCmds.notcontains = LatexCmds.doesnotcontain =
|
2838
|
+
bind(BinaryOperator,'\\not\\ni ','∌');
|
2839
|
+
|
2840
|
+
LatexCmds.sub = LatexCmds.subset = bind(BinaryOperator,'\\subset ','⊂');
|
2841
|
+
|
2842
|
+
LatexCmds.sup = LatexCmds.supset = LatexCmds.superset =
|
2843
|
+
bind(BinaryOperator,'\\supset ','⊃');
|
2844
|
+
|
2845
|
+
LatexCmds.nsub = LatexCmds.notsub =
|
2846
|
+
LatexCmds.nsubset = LatexCmds.notsubset =
|
2847
|
+
bind(BinaryOperator,'\\not\\subset ','⊄');
|
2848
|
+
|
2849
|
+
LatexCmds.nsup = LatexCmds.notsup =
|
2850
|
+
LatexCmds.nsupset = LatexCmds.notsupset =
|
2851
|
+
LatexCmds.nsuperset = LatexCmds.notsuperset =
|
2852
|
+
bind(BinaryOperator,'\\not\\supset ','⊅');
|
2853
|
+
|
2854
|
+
LatexCmds.sube = LatexCmds.subeq = LatexCmds.subsete = LatexCmds.subseteq =
|
2855
|
+
bind(BinaryOperator,'\\subseteq ','⊆');
|
2856
|
+
|
2857
|
+
LatexCmds.supe = LatexCmds.supeq =
|
2858
|
+
LatexCmds.supsete = LatexCmds.supseteq =
|
2859
|
+
LatexCmds.supersete = LatexCmds.superseteq =
|
2860
|
+
bind(BinaryOperator,'\\supseteq ','⊇');
|
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 ','⊈');
|
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 ','⊉');
|
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 ','∑');
|
2885
|
+
LatexCmds['∏'] = LatexCmds.prod = LatexCmds.product = bind(BigSymbol,'\\prod ','∏');
|
2886
|
+
LatexCmds.coprod = LatexCmds.coproduct = bind(BigSymbol,'\\coprod ','∐');
|
2887
|
+
LatexCmds['∫'] = LatexCmds['int'] = LatexCmds.integral = bind(BigSymbol,'\\int ','∫');
|
2888
|
+
|
2889
|
+
|
2890
|
+
|
2891
|
+
//the canonical sets of numbers
|
2892
|
+
LatexCmds.N = LatexCmds.naturals = LatexCmds.Naturals =
|
2893
|
+
bind(VanillaSymbol,'\\mathbb{N}','ℕ');
|
2894
|
+
|
2895
|
+
LatexCmds.P =
|
2896
|
+
LatexCmds.primes = LatexCmds.Primes =
|
2897
|
+
LatexCmds.projective = LatexCmds.Projective =
|
2898
|
+
LatexCmds.probability = LatexCmds.Probability =
|
2899
|
+
bind(VanillaSymbol,'\\mathbb{P}','ℙ');
|
2900
|
+
|
2901
|
+
LatexCmds.Z = LatexCmds.integers = LatexCmds.Integers =
|
2902
|
+
bind(VanillaSymbol,'\\mathbb{Z}','ℤ');
|
2903
|
+
|
2904
|
+
LatexCmds.Q = LatexCmds.rationals = LatexCmds.Rationals =
|
2905
|
+
bind(VanillaSymbol,'\\mathbb{Q}','ℚ');
|
2906
|
+
|
2907
|
+
LatexCmds.R = LatexCmds.reals = LatexCmds.Reals =
|
2908
|
+
bind(VanillaSymbol,'\\mathbb{R}','ℝ');
|
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}','ℂ');
|
2915
|
+
|
2916
|
+
LatexCmds.H = LatexCmds.Hamiltonian = LatexCmds.quaternions = LatexCmds.Quaternions =
|
2917
|
+
bind(VanillaSymbol,'\\mathbb{H}','ℍ');
|
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 ', '◇');
|
2935
|
+
LatexCmds.bigtriangleup = bind(VanillaSymbol, '\\bigtriangleup ', '△');
|
2936
|
+
LatexCmds.ominus = bind(VanillaSymbol, '\\ominus ', '⊖');
|
2937
|
+
LatexCmds.uplus = bind(VanillaSymbol, '\\uplus ', '⊎');
|
2938
|
+
LatexCmds.bigtriangledown = bind(VanillaSymbol, '\\bigtriangledown ', '▽');
|
2939
|
+
LatexCmds.sqcap = bind(VanillaSymbol, '\\sqcap ', '⊓');
|
2940
|
+
LatexCmds.triangleleft = bind(VanillaSymbol, '\\triangleleft ', '⊲');
|
2941
|
+
LatexCmds.sqcup = bind(VanillaSymbol, '\\sqcup ', '⊔');
|
2942
|
+
LatexCmds.triangleright = bind(VanillaSymbol, '\\triangleright ', '⊳');
|
2943
|
+
LatexCmds.odot = bind(VanillaSymbol, '\\odot ', '⊙');
|
2944
|
+
LatexCmds.bigcirc = bind(VanillaSymbol, '\\bigcirc ', '◯');
|
2945
|
+
LatexCmds.dagger = bind(VanillaSymbol, '\\dagger ', '†');
|
2946
|
+
LatexCmds.ddagger = bind(VanillaSymbol, '\\ddagger ', '‡');
|
2947
|
+
LatexCmds.wr = bind(VanillaSymbol, '\\wr ', '≀');
|
2948
|
+
LatexCmds.amalg = bind(VanillaSymbol, '\\amalg ', '∐');
|
2949
|
+
|
2950
|
+
//relationship symbols
|
2951
|
+
LatexCmds.models = bind(VanillaSymbol, '\\models ', '⊨');
|
2952
|
+
LatexCmds.prec = bind(VanillaSymbol, '\\prec ', '≺');
|
2953
|
+
LatexCmds.succ = bind(VanillaSymbol, '\\succ ', '≻');
|
2954
|
+
LatexCmds.preceq = bind(VanillaSymbol, '\\preceq ', '≼');
|
2955
|
+
LatexCmds.succeq = bind(VanillaSymbol, '\\succeq ', '≽');
|
2956
|
+
LatexCmds.simeq = bind(VanillaSymbol, '\\simeq ', '≃');
|
2957
|
+
LatexCmds.mid = bind(VanillaSymbol, '\\mid ', '∣');
|
2958
|
+
LatexCmds.ll = bind(VanillaSymbol, '\\ll ', '≪');
|
2959
|
+
LatexCmds.gg = bind(VanillaSymbol, '\\gg ', '≫');
|
2960
|
+
LatexCmds.parallel = bind(VanillaSymbol, '\\parallel ', '∥');
|
2961
|
+
LatexCmds.bowtie = bind(VanillaSymbol, '\\bowtie ', '⋈');
|
2962
|
+
LatexCmds.sqsubset = bind(VanillaSymbol, '\\sqsubset ', '⊏');
|
2963
|
+
LatexCmds.sqsupset = bind(VanillaSymbol, '\\sqsupset ', '⊐');
|
2964
|
+
LatexCmds.smile = bind(VanillaSymbol, '\\smile ', '⌣');
|
2965
|
+
LatexCmds.sqsubseteq = bind(VanillaSymbol, '\\sqsubseteq ', '⊑');
|
2966
|
+
LatexCmds.sqsupseteq = bind(VanillaSymbol, '\\sqsupseteq ', '⊒');
|
2967
|
+
LatexCmds.doteq = bind(VanillaSymbol, '\\doteq ', '≐');
|
2968
|
+
LatexCmds.frown = bind(VanillaSymbol, '\\frown ', '⌢');
|
2969
|
+
LatexCmds.vdash = bind(VanillaSymbol, '\\vdash ', '⊦');
|
2970
|
+
LatexCmds.dashv = bind(VanillaSymbol, '\\dashv ', '⊣');
|
2971
|
+
|
2972
|
+
//arrows
|
2973
|
+
LatexCmds.longleftarrow = bind(VanillaSymbol, '\\longleftarrow ', '←');
|
2974
|
+
LatexCmds.longrightarrow = bind(VanillaSymbol, '\\longrightarrow ', '→');
|
2975
|
+
LatexCmds.Longleftarrow = bind(VanillaSymbol, '\\Longleftarrow ', '⇐');
|
2976
|
+
LatexCmds.Longrightarrow = bind(VanillaSymbol, '\\Longrightarrow ', '⇒');
|
2977
|
+
LatexCmds.longleftrightarrow = bind(VanillaSymbol, '\\longleftrightarrow ', '↔');
|
2978
|
+
LatexCmds.updownarrow = bind(VanillaSymbol, '\\updownarrow ', '↕');
|
2979
|
+
LatexCmds.Longleftrightarrow = bind(VanillaSymbol, '\\Longleftrightarrow ', '⇔');
|
2980
|
+
LatexCmds.Updownarrow = bind(VanillaSymbol, '\\Updownarrow ', '⇕');
|
2981
|
+
LatexCmds.mapsto = bind(VanillaSymbol, '\\mapsto ', '↦');
|
2982
|
+
LatexCmds.nearrow = bind(VanillaSymbol, '\\nearrow ', '↗');
|
2983
|
+
LatexCmds.hookleftarrow = bind(VanillaSymbol, '\\hookleftarrow ', '↩');
|
2984
|
+
LatexCmds.hookrightarrow = bind(VanillaSymbol, '\\hookrightarrow ', '↪');
|
2985
|
+
LatexCmds.searrow = bind(VanillaSymbol, '\\searrow ', '↘');
|
2986
|
+
LatexCmds.leftharpoonup = bind(VanillaSymbol, '\\leftharpoonup ', '↼');
|
2987
|
+
LatexCmds.rightharpoonup = bind(VanillaSymbol, '\\rightharpoonup ', '⇀');
|
2988
|
+
LatexCmds.swarrow = bind(VanillaSymbol, '\\swarrow ', '↙');
|
2989
|
+
LatexCmds.leftharpoondown = bind(VanillaSymbol, '\\leftharpoondown ', '↽');
|
2990
|
+
LatexCmds.rightharpoondown = bind(VanillaSymbol, '\\rightharpoondown ', '⇁');
|
2991
|
+
LatexCmds.nwarrow = bind(VanillaSymbol, '\\nwarrow ', '↖');
|
2992
|
+
|
2993
|
+
//Misc
|
2994
|
+
LatexCmds.ldots = bind(VanillaSymbol, '\\ldots ', '…');
|
2995
|
+
LatexCmds.cdots = bind(VanillaSymbol, '\\cdots ', '⋯');
|
2996
|
+
LatexCmds.vdots = bind(VanillaSymbol, '\\vdots ', '⋮');
|
2997
|
+
LatexCmds.ddots = bind(VanillaSymbol, '\\ddots ', '⋰');
|
2998
|
+
LatexCmds.surd = bind(VanillaSymbol, '\\surd ', '√');
|
2999
|
+
LatexCmds.triangle = bind(VanillaSymbol, '\\triangle ', '▵');
|
3000
|
+
LatexCmds.ell = bind(VanillaSymbol, '\\ell ', 'ℓ');
|
3001
|
+
LatexCmds.top = bind(VanillaSymbol, '\\top ', '⊤');
|
3002
|
+
LatexCmds.flat = bind(VanillaSymbol, '\\flat ', '♭');
|
3003
|
+
LatexCmds.natural = bind(VanillaSymbol, '\\natural ', '♮');
|
3004
|
+
LatexCmds.sharp = bind(VanillaSymbol, '\\sharp ', '♯');
|
3005
|
+
LatexCmds.wp = bind(VanillaSymbol, '\\wp ', '℘');
|
3006
|
+
LatexCmds.bot = bind(VanillaSymbol, '\\bot ', '⊥');
|
3007
|
+
LatexCmds.clubsuit = bind(VanillaSymbol, '\\clubsuit ', '♣');
|
3008
|
+
LatexCmds.diamondsuit = bind(VanillaSymbol, '\\diamondsuit ', '♢');
|
3009
|
+
LatexCmds.heartsuit = bind(VanillaSymbol, '\\heartsuit ', '♡');
|
3010
|
+
LatexCmds.spadesuit = bind(VanillaSymbol, '\\spadesuit ', '♠');
|
3011
|
+
|
3012
|
+
//variable-sized
|
3013
|
+
LatexCmds.oint = bind(VanillaSymbol, '\\oint ', '∮');
|
3014
|
+
LatexCmds.bigcap = bind(VanillaSymbol, '\\bigcap ', '∩');
|
3015
|
+
LatexCmds.bigcup = bind(VanillaSymbol, '\\bigcup ', '∪');
|
3016
|
+
LatexCmds.bigsqcup = bind(VanillaSymbol, '\\bigsqcup ', '⊔');
|
3017
|
+
LatexCmds.bigvee = bind(VanillaSymbol, '\\bigvee ', '∨');
|
3018
|
+
LatexCmds.bigwedge = bind(VanillaSymbol, '\\bigwedge ', '∧');
|
3019
|
+
LatexCmds.bigodot = bind(VanillaSymbol, '\\bigodot ', '⊙');
|
3020
|
+
LatexCmds.bigotimes = bind(VanillaSymbol, '\\bigotimes ', '⊗');
|
3021
|
+
LatexCmds.bigoplus = bind(VanillaSymbol, '\\bigoplus ', '⊕');
|
3022
|
+
LatexCmds.biguplus = bind(VanillaSymbol, '\\biguplus ', '⊎');
|
3023
|
+
|
3024
|
+
//delimiters
|
3025
|
+
LatexCmds.lfloor = bind(VanillaSymbol, '\\lfloor ', '⌊');
|
3026
|
+
LatexCmds.rfloor = bind(VanillaSymbol, '\\rfloor ', '⌋');
|
3027
|
+
LatexCmds.lceil = bind(VanillaSymbol, '\\lceil ', '⌈');
|
3028
|
+
LatexCmds.rceil = bind(VanillaSymbol, '\\rceil ', '⌉');
|
3029
|
+
LatexCmds.slash = bind(VanillaSymbol, '\\slash ', '/');
|
3030
|
+
LatexCmds.opencurlybrace = bind(VanillaSymbol, '\\opencurlybrace ', '{');
|
3031
|
+
LatexCmds.closecurlybrace = bind(VanillaSymbol, '\\closecurlybrace ', '}');
|
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 ','⊥');
|
3040
|
+
LatexCmds.nabla = LatexCmds.del = bind(VanillaSymbol,'\\nabla ','∇');
|
3041
|
+
LatexCmds.hbar = bind(VanillaSymbol,'\\hbar ','ℏ');
|
3042
|
+
|
3043
|
+
LatexCmds.AA = LatexCmds.Angstrom = LatexCmds.angstrom =
|
3044
|
+
bind(VanillaSymbol,'\\text\\AA ','Å');
|
3045
|
+
|
3046
|
+
LatexCmds.ring = LatexCmds.circ = LatexCmds.circle =
|
3047
|
+
bind(VanillaSymbol,'\\circ ','∘');
|
3048
|
+
|
3049
|
+
LatexCmds.bull = LatexCmds.bullet = bind(VanillaSymbol,'\\bullet ','•');
|
3050
|
+
|
3051
|
+
LatexCmds.setminus = LatexCmds.smallsetminus =
|
3052
|
+
bind(VanillaSymbol,'\\setminus ','∖');
|
3053
|
+
|
3054
|
+
LatexCmds.not = //bind(Symbol,'\\not ','<span class="not">/</span>');
|
3055
|
+
LatexCmds['¬'] = LatexCmds.neg = bind(VanillaSymbol,'\\neg ','¬');
|
3056
|
+
|
3057
|
+
LatexCmds['…'] = LatexCmds.dots = LatexCmds.ellip = LatexCmds.hellip =
|
3058
|
+
LatexCmds.ellipsis = LatexCmds.hellipsis =
|
3059
|
+
bind(VanillaSymbol,'\\dots ','…');
|
3060
|
+
|
3061
|
+
LatexCmds.converges =
|
3062
|
+
LatexCmds.darr = LatexCmds.dnarr = LatexCmds.dnarrow = LatexCmds.downarrow =
|
3063
|
+
bind(VanillaSymbol,'\\downarrow ','↓');
|
3064
|
+
|
3065
|
+
LatexCmds.dArr = LatexCmds.dnArr = LatexCmds.dnArrow = LatexCmds.Downarrow =
|
3066
|
+
bind(VanillaSymbol,'\\Downarrow ','⇓');
|
3067
|
+
|
3068
|
+
LatexCmds.diverges = LatexCmds.uarr = LatexCmds.uparrow =
|
3069
|
+
bind(VanillaSymbol,'\\uparrow ','↑');
|
3070
|
+
|
3071
|
+
LatexCmds.uArr = LatexCmds.Uparrow = bind(VanillaSymbol,'\\Uparrow ','⇑');
|
3072
|
+
|
3073
|
+
LatexCmds.to = bind(BinaryOperator,'\\to ','→');
|
3074
|
+
|
3075
|
+
LatexCmds.rarr = LatexCmds.rightarrow = bind(VanillaSymbol,'\\rightarrow ','→');
|
3076
|
+
|
3077
|
+
LatexCmds.implies = bind(BinaryOperator,'\\Rightarrow ','⇒');
|
3078
|
+
|
3079
|
+
LatexCmds.rArr = LatexCmds.Rightarrow = bind(VanillaSymbol,'\\Rightarrow ','⇒');
|
3080
|
+
|
3081
|
+
LatexCmds.gets = bind(BinaryOperator,'\\gets ','←');
|
3082
|
+
|
3083
|
+
LatexCmds.larr = LatexCmds.leftarrow = bind(VanillaSymbol,'\\leftarrow ','←');
|
3084
|
+
|
3085
|
+
LatexCmds.impliedby = bind(BinaryOperator,'\\Leftarrow ','⇐');
|
3086
|
+
|
3087
|
+
LatexCmds.lArr = LatexCmds.Leftarrow = bind(VanillaSymbol,'\\Leftarrow ','⇐');
|
3088
|
+
|
3089
|
+
LatexCmds.harr = LatexCmds.lrarr = LatexCmds.leftrightarrow =
|
3090
|
+
bind(VanillaSymbol,'\\leftrightarrow ','↔');
|
3091
|
+
|
3092
|
+
LatexCmds.iff = bind(BinaryOperator,'\\Leftrightarrow ','⇔');
|
3093
|
+
|
3094
|
+
LatexCmds.hArr = LatexCmds.lrArr = LatexCmds.Leftrightarrow =
|
3095
|
+
bind(VanillaSymbol,'\\Leftrightarrow ','⇔');
|
3096
|
+
|
3097
|
+
LatexCmds.Re = LatexCmds.Real = LatexCmds.real = bind(VanillaSymbol,'\\Re ','ℜ');
|
3098
|
+
|
3099
|
+
LatexCmds.Im = LatexCmds.imag =
|
3100
|
+
LatexCmds.image = LatexCmds.imagin = LatexCmds.imaginary = LatexCmds.Imaginary =
|
3101
|
+
bind(VanillaSymbol,'\\Im ','ℑ');
|
3102
|
+
|
3103
|
+
LatexCmds.part = LatexCmds.partial = bind(VanillaSymbol,'\\partial ','∂');
|
3104
|
+
|
3105
|
+
LatexCmds.inf = LatexCmds.infin = LatexCmds.infty = LatexCmds.infinity =
|
3106
|
+
bind(VanillaSymbol,'\\infty ','∞');
|
3107
|
+
|
3108
|
+
LatexCmds.alef = LatexCmds.alefsym = LatexCmds.aleph = LatexCmds.alephsym =
|
3109
|
+
bind(VanillaSymbol,'\\aleph ','ℵ');
|
3110
|
+
|
3111
|
+
LatexCmds.xist = //LOL
|
3112
|
+
LatexCmds.xists = LatexCmds.exist = LatexCmds.exists =
|
3113
|
+
bind(VanillaSymbol,'\\exists ','∃');
|
3114
|
+
|
3115
|
+
LatexCmds.and = LatexCmds.land = LatexCmds.wedge =
|
3116
|
+
bind(VanillaSymbol,'\\wedge ','∧');
|
3117
|
+
|
3118
|
+
LatexCmds.or = LatexCmds.lor = LatexCmds.vee = bind(VanillaSymbol,'\\vee ','∨');
|
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 ','∅');
|
3125
|
+
|
3126
|
+
LatexCmds.cup = LatexCmds.union = bind(BinaryOperator,'\\cup ','∪');
|
3127
|
+
|
3128
|
+
LatexCmds.cap = LatexCmds.intersect = LatexCmds.intersection =
|
3129
|
+
bind(BinaryOperator,'\\cap ','∩');
|
3130
|
+
|
3131
|
+
LatexCmds.deg = LatexCmds.degree = bind(VanillaSymbol,'^\\circ ','°');
|
3132
|
+
|
3133
|
+
LatexCmds.ang = LatexCmds.angle = bind(VanillaSymbol,'\\angle ','∠');
|
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">‍</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
|
+
}());
|