umeditor-rails 1.0.0

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