| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888 | 
							- /**
 
-  * Copyleft 2010-2011 Jay and Han (laughinghan@gmail.com)
 
-  *   under the GNU Lesser General Public License
 
-  *     http://www.gnu.org/licenses/lgpl.html
 
-  * Project Website: http://mathquill.com
 
-  */
 
- (function() {
 
- var jQuery = window.jQuery,
 
-   undefined,
 
-   _, //temp variable of prototypes
 
-   mqCmdId = 'mathquill-command-id',
 
-   mqBlockId = 'mathquill-block-id',
 
-   min = Math.min,
 
-   max = Math.max;
 
- var __slice = [].slice;
 
- function noop() {}
 
- /**
 
-  * sugar to make defining lots of commands easier.
 
-  * TODO: rethink this.
 
-  */
 
- function bind(cons /*, args... */) {
 
-   var args = __slice.call(arguments, 1);
 
-   return function() {
 
-     return cons.apply(this, args);
 
-   };
 
- }
 
- /**
 
-  * a development-only debug method.  This definition and all
 
-  * calls to `pray` will be stripped from the minified
 
-  * build of mathquill.
 
-  *
 
-  * This function must be called by name to be removed
 
-  * at compile time.  Do not define another function
 
-  * with the same name, and only call this function by
 
-  * name.
 
-  */
 
- function pray(message, cond) {
 
-   if (!cond) throw new Error('prayer failed: '+message);
 
- }
 
- var P = (function(prototype, ownProperty, undefined) {
 
-   // helper functions that also help minification
 
-   function isObject(o) { return typeof o === 'object'; }
 
-   function isFunction(f) { return typeof f === 'function'; }
 
-   // a function that gets reused to make uninitialized objects
 
-   function BareConstructor() {}
 
-   function P(_superclass /* = Object */, definition) {
 
-     // handle the case where no superclass is given
 
-     if (definition === undefined) {
 
-       definition = _superclass;
 
-       _superclass = Object;
 
-     }
 
-     // C is the class to be returned.
 
-     //
 
-     // It delegates to instantiating an instance of `Bare`, so that it
 
-     // will always return a new instance regardless of the calling
 
-     // context.
 
-     //
 
-     //  TODO: the Chrome inspector shows all created objects as `C`
 
-     //        rather than `Object`.  Setting the .name property seems to
 
-     //        have no effect.  Is there a way to override this behavior?
 
-     function C() {
 
-       var self = new Bare;
 
-       if (isFunction(self.init)) self.init.apply(self, arguments);
 
-       return self;
 
-     }
 
-     // C.Bare is a class with a noop constructor.  Its prototype is the
 
-     // same as C, so that instances of C.Bare are also instances of C.
 
-     // New objects can be allocated without initialization by calling
 
-     // `new MyClass.Bare`.
 
-     function Bare() {}
 
-     C.Bare = Bare;
 
-     // Set up the prototype of the new class.
 
-     var _super = BareConstructor[prototype] = _superclass[prototype];
 
-     var proto = Bare[prototype] = C[prototype] = new BareConstructor;
 
-     // other variables, as a minifier optimization
 
-     var extensions;
 
-     // set the constructor property on the prototype, for convenience
 
-     proto.constructor = C;
 
-     C.mixin = function(def) {
 
-       Bare[prototype] = C[prototype] = P(C, def)[prototype];
 
-       return C;
 
-     }
 
-     return (C.open = function(def) {
 
-       extensions = {};
 
-       if (isFunction(def)) {
 
-         // call the defining function with all the arguments you need
 
-         // extensions captures the return value.
 
-         extensions = def.call(C, proto, _super, C, _superclass);
 
-       }
 
-       else if (isObject(def)) {
 
-         // if you passed an object instead, we'll take it
 
-         extensions = def;
 
-       }
 
-       // ...and extend it
 
-       if (isObject(extensions)) {
 
-         for (var ext in extensions) {
 
-           if (ownProperty.call(extensions, ext)) {
 
-             proto[ext] = extensions[ext];
 
-           }
 
-         }
 
-       }
 
-       // if there's no init, we assume we're inheriting a non-pjs class, so
 
-       // we default to applying the superclass's constructor.
 
-       if (!isFunction(proto.init)) {
 
-         proto.init = _superclass;
 
-       }
 
-       return C;
 
-     })(definition);
 
-   }
 
-   // ship it
 
-   return P;
 
-   // as a minifier optimization, we've closured in a few helper functions
 
-   // and the string 'prototype' (C[p] is much shorter than C.prototype)
 
- })('prototype', ({}).hasOwnProperty);
 
- /*************************************************
 
-  * Textarea Manager
 
-  *
 
-  * An abstraction layer wrapping the textarea in
 
-  * an object with methods to manipulate and listen
 
-  * to events on, that hides all the nasty cross-
 
-  * browser incompatibilities behind a uniform API.
 
-  *
 
-  * Design goal: This is a *HARD* internal
 
-  * abstraction barrier. Cross-browser
 
-  * inconsistencies are not allowed to leak through
 
-  * and be dealt with by event handlers. All future
 
-  * cross-browser issues that arise must be dealt
 
-  * with here, and if necessary, the API updated.
 
-  *
 
-  * Organization:
 
-  * - key values map and stringify()
 
-  * - manageTextarea()
 
-  *    + defer() and flush()
 
-  *    + event handler logic
 
-  *    + attach event handlers and export methods
 
-  ************************************************/
 
- var manageTextarea = (function() {
 
-   // The following [key values][1] map was compiled from the
 
-   // [DOM3 Events appendix section on key codes][2] and
 
-   // [a widely cited report on cross-browser tests of key codes][3],
 
-   // except for 10: 'Enter', which I've empirically observed in Safari on iOS
 
-   // and doesn't appear to conflict with any other known key codes.
 
-   //
 
-   // [1]: http://www.w3.org/TR/2012/WD-DOM-Level-3-Events-20120614/#keys-keyvalues
 
-   // [2]: http://www.w3.org/TR/2012/WD-DOM-Level-3-Events-20120614/#fixed-virtual-key-codes
 
-   // [3]: http://unixpapa.com/js/key.html
 
-   var KEY_VALUES = {
 
-     8: 'Backspace',
 
-     9: 'Tab',
 
-     10: 'Enter', // for Safari on iOS
 
-     13: 'Enter',
 
-     16: 'Shift',
 
-     17: 'Control',
 
-     18: 'Alt',
 
-     20: 'CapsLock',
 
-     27: 'Esc',
 
-     32: 'Spacebar',
 
-     33: 'PageUp',
 
-     34: 'PageDown',
 
-     35: 'End',
 
-     36: 'Home',
 
-     37: 'Left',
 
-     38: 'Up',
 
-     39: 'Right',
 
-     40: 'Down',
 
-     45: 'Insert',
 
-     46: 'Del',
 
-     144: 'NumLock'
 
-   };
 
-   // To the extent possible, create a normalized string representation
 
-   // of the key combo (i.e., key code and modifier keys).
 
-   function stringify(evt) {
 
-     var which = evt.which || evt.keyCode;
 
-     var keyVal = KEY_VALUES[which];
 
-     var key;
 
-     var modifiers = [];
 
-     if (evt.ctrlKey) modifiers.push('Ctrl');
 
-     if (evt.originalEvent && evt.originalEvent.metaKey) modifiers.push('Meta');
 
-     if (evt.altKey) modifiers.push('Alt');
 
-     if (evt.shiftKey) modifiers.push('Shift');
 
-     key = keyVal || String.fromCharCode(which);
 
-     if (!modifiers.length && !keyVal) return key;
 
-     modifiers.push(key);
 
-     return modifiers.join('-');
 
-   }
 
-   // create a textarea manager that calls callbacks at useful times
 
-   // and exports useful public methods
 
-   return function manageTextarea(el, opts) {
 
-     var keydown = null;
 
-     var keypress = null;
 
-     if (!opts) opts = {};
 
-     var textCallback = opts.text || noop;
 
-     var keyCallback = opts.key || noop;
 
-     var pasteCallback = opts.paste || noop;
 
-     var onCut = opts.cut || noop;
 
-     var textarea = jQuery(el);
 
-     var target = jQuery(opts.container || textarea);
 
-     // checkTextareaFor() is called after keypress or paste events to
 
-     // say "Hey, I think something was just typed" or "pasted" (resp.),
 
-     // so that at all subsequent opportune times (next event or timeout),
 
-     // will check for expected typed or pasted text.
 
-     // Need to check repeatedly because #135: in Safari 5.1 (at least),
 
-     // after selecting something and then typing, the textarea is
 
-     // incorrectly reported as selected during the input event (but not
 
-     // subsequently).
 
-     var checkTextarea = noop, timeoutId;
 
-     function checkTextareaFor(checker) {
 
-       checkTextarea = checker;
 
-       clearTimeout(timeoutId);
 
-       timeoutId = setTimeout(checker);
 
-     }
 
-     target.bind('keydown keypress input keyup focusout paste', function() { checkTextarea(); });
 
-     // -*- public methods -*- //
 
-     function select(text) {
 
-       // check textarea at least once/one last time before munging (so
 
-       // no race condition if selection happens after keypress/paste but
 
-       // before checkTextarea), then never again ('cos it's been munged)
 
-       checkTextarea();
 
-       checkTextarea = noop;
 
-       clearTimeout(timeoutId);
 
-       textarea.val(text);
 
-       if (text) textarea[0].select();
 
-     }
 
-     // -*- helper subroutines -*- //
 
-     // Determine whether there's a selection in the textarea.
 
-     // This will always return false in IE < 9, which don't support
 
-     // HTMLTextareaElement::selection{Start,End}.
 
-     function hasSelection() {
 
-       var dom = textarea[0];
 
-       if (!('selectionStart' in dom)) return false;
 
-       return dom.selectionStart !== dom.selectionEnd;
 
-     }
 
-     function popText(callback) {
 
-       var text = textarea.val();
 
-       textarea.val('');
 
-       if (text) callback(text);
 
-     }
 
-     function handleKey() {
 
-       keyCallback(stringify(keydown), keydown);
 
-     }
 
-     // -*- event handlers -*- //
 
-     function onKeydown(e) {
 
-       keydown = e;
 
-       keypress = null;
 
-       handleKey();
 
-     }
 
-     function onKeypress(e) {
 
-       // call the key handler for repeated keypresses.
 
-       // This excludes keypresses that happen directly
 
-       // after keydown.  In that case, there will be
 
-       // no previous keypress, so we skip it here
 
-       if (keydown && keypress) handleKey();
 
-       keypress = e;
 
-       checkTextareaFor(typedText);
 
-     }
 
-     function typedText() {
 
-       // If there is a selection, the contents of the textarea couldn't
 
-       // possibly have just been typed in.
 
-       // This happens in browsers like Firefox and Opera that fire
 
-       // keypress for keystrokes that are not text entry and leave the
 
-       // selection in the textarea alone, such as Ctrl-C.
 
-       // Note: we assume that browsers that don't support hasSelection()
 
-       // also never fire keypress on keystrokes that are not text entry.
 
-       // This seems reasonably safe because:
 
-       // - all modern browsers including IE 9+ support hasSelection(),
 
-       //   making it extremely unlikely any browser besides IE < 9 won't
 
-       // - as far as we know IE < 9 never fires keypress on keystrokes
 
-       //   that aren't text entry, which is only as reliable as our
 
-       //   tests are comprehensive, but the IE < 9 way to do
 
-       //   hasSelection() is poorly documented and is also only as
 
-       //   reliable as our tests are comprehensive
 
-       // If anything like #40 or #71 is reported in IE < 9, see
 
-       // b1318e5349160b665003e36d4eedd64101ceacd8
 
-       if (hasSelection()) return;
 
-       popText(textCallback);
 
-     }
 
-     function onBlur() { keydown = keypress = null; }
 
-     function onPaste(e) {
 
-       // browsers are dumb.
 
-       //
 
-       // In Linux, middle-click pasting causes onPaste to be called,
 
-       // when the textarea is not necessarily focused.  We focus it
 
-       // here to ensure that the pasted text actually ends up in the
 
-       // textarea.
 
-       //
 
-       // It's pretty nifty that by changing focus in this handler,
 
-       // we can change the target of the default action.  (This works
 
-       // on keydown too, FWIW).
 
-       //
 
-       // And by nifty, we mean dumb (but useful sometimes).
 
-       textarea.focus();
 
-       checkTextareaFor(pastedText);
 
-     }
 
-     function pastedText() {
 
-       popText(pasteCallback);
 
-     }
 
-     // -*- attach event handlers -*- //
 
-     target.bind({
 
-       keydown: onKeydown,
 
-       keypress: onKeypress,
 
-       focusout: onBlur,
 
-       cut: onCut,
 
-       paste: onPaste
 
-     });
 
-     // -*- export public methods -*- //
 
-     return {
 
-       select: select
 
-     };
 
-   };
 
- }());
 
- var Parser = P(function(_, _super, Parser) {
 
-   // The Parser object is a wrapper for a parser function.
 
-   // Externally, you use one to parse a string by calling
 
-   //   var result = SomeParser.parse('Me Me Me! Parse Me!');
 
-   // You should never call the constructor, rather you should
 
-   // construct your Parser from the base parsers and the
 
-   // parser combinator methods.
 
-   function parseError(stream, message) {
 
-     if (stream) {
 
-       stream = "'"+stream+"'";
 
-     }
 
-     else {
 
-       stream = 'EOF';
 
-     }
 
-     throw 'Parse Error: '+message+' at '+stream;
 
-   }
 
-   _.init = function(body) { this._ = body; };
 
-   _.parse = function(stream) {
 
-     return this.skip(eof)._(stream, success, parseError);
 
-     function success(stream, result) { return result; }
 
-   };
 
-   // -*- primitive combinators -*- //
 
-   _.or = function(alternative) {
 
-     pray('or is passed a parser', alternative instanceof Parser);
 
-     var self = this;
 
-     return Parser(function(stream, onSuccess, onFailure) {
 
-       return self._(stream, onSuccess, failure);
 
-       function failure(newStream) {
 
-         return alternative._(stream, onSuccess, onFailure);
 
-       }
 
-     });
 
-   };
 
-   _.then = function(next) {
 
-     var self = this;
 
-     return Parser(function(stream, onSuccess, onFailure) {
 
-       return self._(stream, success, onFailure);
 
-       function success(newStream, result) {
 
-         var nextParser = (next instanceof Parser ? next : next(result));
 
-         pray('a parser is returned', nextParser instanceof Parser);
 
-         return nextParser._(newStream, onSuccess, onFailure);
 
-       }
 
-     });
 
-   };
 
-   // -*- optimized iterative combinators -*- //
 
-   _.many = function() {
 
-     var self = this;
 
-     return Parser(function(stream, onSuccess, onFailure) {
 
-       var xs = [];
 
-       while (self._(stream, success, failure));
 
-       return onSuccess(stream, xs);
 
-       function success(newStream, x) {
 
-         stream = newStream;
 
-         xs.push(x);
 
-         return true;
 
-       }
 
-       function failure() {
 
-         return false;
 
-       }
 
-     });
 
-   };
 
-   _.times = function(min, max) {
 
-     if (arguments.length < 2) max = min;
 
-     var self = this;
 
-     return Parser(function(stream, onSuccess, onFailure) {
 
-       var xs = [];
 
-       var result = true;
 
-       var failure;
 
-       for (var i = 0; i < min; i += 1) {
 
-         result = self._(stream, success, firstFailure);
 
-         if (!result) return onFailure(stream, failure);
 
-       }
 
-       for (; i < max && result; i += 1) {
 
-         result = self._(stream, success, secondFailure);
 
-       }
 
-       return onSuccess(stream, xs);
 
-       function success(newStream, x) {
 
-         xs.push(x);
 
-         stream = newStream;
 
-         return true;
 
-       }
 
-       function firstFailure(newStream, msg) {
 
-         failure = msg;
 
-         stream = newStream;
 
-         return false;
 
-       }
 
-       function secondFailure(newStream, msg) {
 
-         return false;
 
-       }
 
-     });
 
-   };
 
-   // -*- higher-level combinators -*- //
 
-   _.result = function(res) { return this.then(succeed(res)); };
 
-   _.atMost = function(n) { return this.times(0, n); };
 
-   _.atLeast = function(n) {
 
-     var self = this;
 
-     return self.times(n).then(function(start) {
 
-       return self.many().map(function(end) {
 
-         return start.concat(end);
 
-       });
 
-     });
 
-   };
 
-   _.map = function(fn) {
 
-     return this.then(function(result) { return succeed(fn(result)); });
 
-   };
 
-   _.skip = function(two) {
 
-     return this.then(function(result) { return two.result(result); });
 
-   };
 
-   // -*- primitive parsers -*- //
 
-   var string = this.string = function(str) {
 
-     var len = str.length;
 
-     var expected = "expected '"+str+"'";
 
-     return Parser(function(stream, onSuccess, onFailure) {
 
-       var head = stream.slice(0, len);
 
-       if (head === str) {
 
-         return onSuccess(stream.slice(len), head);
 
-       }
 
-       else {
 
-         return onFailure(stream, expected);
 
-       }
 
-     });
 
-   };
 
-   var regex = this.regex = function(re) {
 
-     pray('regexp parser is anchored', re.toString().charAt(1) === '^');
 
-     var expected = 'expected '+re;
 
-     return Parser(function(stream, onSuccess, onFailure) {
 
-       var match = re.exec(stream);
 
-       if (match) {
 
-         var result = match[0];
 
-         return onSuccess(stream.slice(result.length), result);
 
-       }
 
-       else {
 
-         return onFailure(stream, expected);
 
-       }
 
-     });
 
-   };
 
-   var succeed = Parser.succeed = function(result) {
 
-     return Parser(function(stream, onSuccess) {
 
-       return onSuccess(stream, result);
 
-     });
 
-   };
 
-   var fail = Parser.fail = function(msg) {
 
-     return Parser(function(stream, _, onFailure) {
 
-       return onFailure(stream, msg);
 
-     });
 
-   };
 
-   var letter = Parser.letter = regex(/^[a-z]/i);
 
-   var letters = Parser.letters = regex(/^[a-z]*/i);
 
-   var digit = Parser.digit = regex(/^[0-9]/);
 
-   var digits = Parser.digits = regex(/^[0-9]*/);
 
-   var whitespace = Parser.whitespace = regex(/^\s+/);
 
-   var optWhitespace = Parser.optWhitespace = regex(/^\s*/);
 
-   var any = Parser.any = Parser(function(stream, onSuccess, onFailure) {
 
-     if (!stream) return onFailure(stream, 'expected any character');
 
-     return onSuccess(stream.slice(1), stream.charAt(0));
 
-   });
 
-   var all = Parser.all = Parser(function(stream, onSuccess, onFailure) {
 
-     return onSuccess('', stream);
 
-   });
 
-   var eof = Parser.eof = Parser(function(stream, onSuccess, onFailure) {
 
-     if (stream) return onFailure(stream, 'expected EOF');
 
-     return onSuccess(stream, stream);
 
-   });
 
- });
 
- /*************************************************
 
-  * Base classes of the MathQuill virtual DOM tree
 
-  *
 
-  * Only doing tree node manipulation via these
 
-  * adopt/ disown methods guarantees well-formedness
 
-  * of the tree.
 
-  ************************************************/
 
- // L = 'left'
 
- // R = 'right'
 
- //
 
- // the contract is that they can be used as object properties
 
- // and (-L) === R, and (-R) === L.
 
- var L = -1;
 
- var R = 1;
 
- function prayDirection(dir) {
 
-   pray('a direction was passed', dir === L || dir === R);
 
- }
 
- /**
 
-  * Tiny extension of jQuery adding directionalized DOM manipulation methods.
 
-  *
 
-  * Funny how Pjs v3 almost just works with `jQuery.fn.init`.
 
-  *
 
-  * jQuery features that don't work on $:
 
-  *   - jQuery.*, like jQuery.ajax, obviously (Pjs doesn't and shouldn't
 
-  *                                            copy constructor properties)
 
-  *
 
-  *   - jQuery(function), the shortcut for `jQuery(document).ready(function)`,
 
-  *     because `jQuery.fn.init` is idiosyncratic and Pjs doing, essentially,
 
-  *     `jQuery.fn.init.apply(this, arguments)` isn't quite right, you need:
 
-  *
 
-  *       _.init = function(s, c) { jQuery.fn.init.call(this, s, c, $(document)); };
 
-  *
 
-  *     if you actually give a shit (really, don't bother),
 
-  *     see https://github.com/jquery/jquery/blob/1.7.2/src/core.js#L889
 
-  *
 
-  *   - jQuery(selector), because jQuery translates that to
 
-  *     `jQuery(document).find(selector)`, but Pjs doesn't (should it?) let
 
-  *     you override the result of a constructor call
 
-  *       + note that because of the jQuery(document) shortcut-ness, there's also
 
-  *         the 3rd-argument-needs-to-be-`$(document)` thing above, but the fix
 
-  *         for that (as can be seen above) is really easy. This problem requires
 
-  *         a way more intrusive fix
 
-  *
 
-  * And that's it! Everything else just magically works because jQuery internally
 
-  * uses `this.constructor()` everywhere (hence calling `$`), but never ever does
 
-  * `this.constructor.find` or anything like that, always doing `jQuery.find`.
 
-  */
 
- var $ = P(jQuery, function(_) {
 
-   _.insDirOf = function(dir, el) {
 
-     return dir === L ?
 
-       this.insertBefore(el.first()) : this.insertAfter(el.last());
 
-   };
 
-   _.insAtDirEnd = function(dir, el) {
 
-     return dir === L ? this.prependTo(el) : this.appendTo(el);
 
-   };
 
- });
 
- var Point = P(function(_) {
 
-   _.parent = 0;
 
-   _[L] = 0;
 
-   _[R] = 0;
 
-   _.init = function(parent, leftward, rightward) {
 
-     this.parent = parent;
 
-     this[L] = leftward;
 
-     this[R] = rightward;
 
-   };
 
- });
 
- /**
 
-  * MathQuill virtual-DOM tree-node abstract base class
 
-  */
 
- var Node = P(function(_) {
 
-   _[L] = 0;
 
-   _[R] = 0
 
-   _.parent = 0;
 
-   _.init = function() {
 
-     this.ends = {};
 
-     this.ends[L] = 0;
 
-     this.ends[R] = 0;
 
-   };
 
-   _.children = function() {
 
-     return Fragment(this.ends[L], this.ends[R]);
 
-   };
 
-   _.eachChild = function(fn) {
 
-     return this.children().each(fn);
 
-   };
 
-   _.foldChildren = function(fold, fn) {
 
-     return this.children().fold(fold, fn);
 
-   };
 
-   _.adopt = function(parent, leftward, rightward) {
 
-     Fragment(this, this).adopt(parent, leftward, rightward);
 
-     return this;
 
-   };
 
-   _.disown = function() {
 
-     Fragment(this, this).disown();
 
-     return this;
 
-   };
 
- });
 
- /**
 
-  * An entity outside the virtual tree with one-way pointers (so it's only a
 
-  * "view" of part of the tree, not an actual node/entity in the tree) that
 
-  * delimits a doubly-linked list of sibling nodes.
 
-  * It's like a fanfic love-child between HTML DOM DocumentFragment and the Range
 
-  * classes: like DocumentFragment, its contents must be sibling nodes
 
-  * (unlike Range, whose contents are arbitrary contiguous pieces of subtrees),
 
-  * but like Range, it has only one-way pointers to its contents, its contents
 
-  * have no reference to it and in fact may still be in the visible tree (unlike
 
-  * DocumentFragment, whose contents must be detached from the visible tree
 
-  * and have their 'parent' pointers set to the DocumentFragment).
 
-  */
 
- var Fragment = P(function(_) {
 
-   _.init = function(leftEnd, rightEnd) {
 
-     pray('no half-empty fragments', !leftEnd === !rightEnd);
 
-     this.ends = {};
 
-     if (!leftEnd) return;
 
-     pray('left end node is passed to Fragment', leftEnd instanceof Node);
 
-     pray('right end node is passed to Fragment', rightEnd instanceof Node);
 
-     pray('leftEnd and rightEnd have the same parent',
 
-          leftEnd.parent === rightEnd.parent);
 
-     this.ends[L] = leftEnd;
 
-     this.ends[R] = rightEnd;
 
-   };
 
-   function prayWellFormed(parent, leftward, rightward) {
 
-     pray('a parent is always present', parent);
 
-     pray('leftward is properly set up', (function() {
 
-       // either it's empty and `rightward` is the left end child (possibly empty)
 
-       if (!leftward) return parent.ends[L] === rightward;
 
-       // or it's there and its [R] and .parent are properly set up
 
-       return leftward[R] === rightward && leftward.parent === parent;
 
-     })());
 
-     pray('rightward is properly set up', (function() {
 
-       // either it's empty and `leftward` is the right end child (possibly empty)
 
-       if (!rightward) return parent.ends[R] === leftward;
 
-       // or it's there and its [L] and .parent are properly set up
 
-       return rightward[L] === leftward && rightward.parent === parent;
 
-     })());
 
-   }
 
-   _.adopt = function(parent, leftward, rightward) {
 
-     prayWellFormed(parent, leftward, rightward);
 
-     var self = this;
 
-     self.disowned = false;
 
-     var leftEnd = self.ends[L];
 
-     if (!leftEnd) return this;
 
-     var rightEnd = self.ends[R];
 
-     if (leftward) {
 
-       // NB: this is handled in the ::each() block
 
-       // leftward[R] = leftEnd
 
-     } else {
 
-       parent.ends[L] = leftEnd;
 
-     }
 
-     if (rightward) {
 
-       rightward[L] = rightEnd;
 
-     } else {
 
-       parent.ends[R] = rightEnd;
 
-     }
 
-     self.ends[R][R] = rightward;
 
-     self.each(function(el) {
 
-       el[L] = leftward;
 
-       el.parent = parent;
 
-       if (leftward) leftward[R] = el;
 
-       leftward = el;
 
-     });
 
-     return self;
 
-   };
 
-   _.disown = function() {
 
-     var self = this;
 
-     var leftEnd = self.ends[L];
 
-     // guard for empty and already-disowned fragments
 
-     if (!leftEnd || self.disowned) return self;
 
-     self.disowned = true;
 
-     var rightEnd = self.ends[R]
 
-     var parent = leftEnd.parent;
 
-     prayWellFormed(parent, leftEnd[L], leftEnd);
 
-     prayWellFormed(parent, rightEnd, rightEnd[R]);
 
-     if (leftEnd[L]) {
 
-       leftEnd[L][R] = rightEnd[R];
 
-     } else {
 
-       parent.ends[L] = rightEnd[R];
 
-     }
 
-     if (rightEnd[R]) {
 
-       rightEnd[R][L] = leftEnd[L];
 
-     } else {
 
-       parent.ends[R] = leftEnd[L];
 
-     }
 
-     return self;
 
-   };
 
-   _.each = function(fn) {
 
-     var self = this;
 
-     var el = self.ends[L];
 
-     if (!el) return self;
 
-     for (;el !== self.ends[R][R]; el = el[R]) {
 
-       if (fn.call(self, el) === false) break;
 
-     }
 
-     return self;
 
-   };
 
-   _.fold = function(fold, fn) {
 
-     this.each(function(el) {
 
-       fold = fn.call(this, fold, el);
 
-     });
 
-     return fold;
 
-   };
 
- });
 
- /*************************************************
 
-  * Abstract classes of math blocks and commands.
 
-  ************************************************/
 
- var uuid = (function() {
 
-   var id = 0;
 
-   return function() { return id += 1; };
 
- })();
 
- /**
 
-  * Math tree node base class.
 
-  * Some math-tree-specific extensions to Node.
 
-  * Both MathBlock's and MathCommand's descend from it.
 
-  */
 
- var MathElement = P(Node, function(_, _super) {
 
-   _.init = function(obj) {
 
-     _super.init.call(this);
 
-     this.id = uuid();
 
-     MathElement[this.id] = this;
 
-   };
 
-   _.toString = function() {
 
-     return '[MathElement '+this.id+']';
 
-   };
 
-   _.bubble = function(event /*, args... */) {
 
-     var args = __slice.call(arguments, 1);
 
-     for (var ancestor = this; ancestor; ancestor = ancestor.parent) {
 
-       var res = ancestor[event] && ancestor[event].apply(ancestor, args);
 
-       if (res === false) break;
 
-     }
 
-     return this;
 
-   };
 
-   _.postOrder = function(fn /*, args... */) {
 
-     var args = __slice.call(arguments, 1);
 
-     if (typeof fn === 'string') {
 
-       var methodName = fn;
 
-       fn = function(el) {
 
-         if (methodName in el) el[methodName].apply(el, args);
 
-       };
 
-     }
 
-     (function recurse(desc) {
 
-       desc.eachChild(recurse);
 
-       fn(desc);
 
-     })(this);
 
-   };
 
-   _.jQ = $();
 
-   _.jQadd = function(jQ) { this.jQ = this.jQ.add(jQ); };
 
-   this.jQize = function(html) {
 
-     // Sets the .jQ of the entire math subtree rooted at this command.
 
-     // Expects .createBlocks() to have been called already, since it
 
-     // calls .html().
 
-     var jQ = $(html);
 
-     jQ.find('*').andSelf().each(function() {
 
-       var jQ = $(this),
 
-         cmdId = jQ.attr('mathquill-command-id'),
 
-         blockId = jQ.attr('mathquill-block-id');
 
-       if (cmdId) MathElement[cmdId].jQadd(jQ);
 
-       if (blockId) MathElement[blockId].jQadd(jQ);
 
-     });
 
-     return jQ;
 
-   };
 
-   _.finalizeInsert = function() {
 
-     var self = this;
 
-     self.postOrder('finalizeTree');
 
-     // note: this order is important.
 
-     // empty elements need the empty box provided by blur to
 
-     // be present in order for their dimensions to be measured
 
-     // correctly in redraw.
 
-     self.postOrder('blur');
 
-     // adjust context-sensitive spacing
 
-     self.postOrder('respace');
 
-     if (self[R].respace) self[R].respace();
 
-     if (self[L].respace) self[L].respace();
 
-     self.postOrder('redraw');
 
-     self.bubble('redraw');
 
-   };
 
- });
 
- /**
 
-  * Commands and operators, like subscripts, exponents, or fractions.
 
-  * Descendant commands are organized into blocks.
 
-  */
 
- var MathCommand = P(MathElement, function(_, _super) {
 
-   _.init = function(ctrlSeq, htmlTemplate, textTemplate) {
 
-     var cmd = this;
 
-     _super.init.call(cmd);
 
-     if (!cmd.ctrlSeq) cmd.ctrlSeq = ctrlSeq;
 
-     if (htmlTemplate) cmd.htmlTemplate = htmlTemplate;
 
-     if (textTemplate) cmd.textTemplate = textTemplate;
 
-   };
 
-   // obvious methods
 
-   _.replaces = function(replacedFragment) {
 
-     replacedFragment.disown();
 
-     this.replacedFragment = replacedFragment;
 
-   };
 
-   _.isEmpty = function() {
 
-     return this.foldChildren(true, function(isEmpty, child) {
 
-       return isEmpty && child.isEmpty();
 
-     });
 
-   };
 
-   _.parser = function() {
 
-     var block = latexMathParser.block;
 
-     var self = this;
 
-     return block.times(self.numBlocks()).map(function(blocks) {
 
-       self.blocks = blocks;
 
-       for (var i = 0; i < blocks.length; i += 1) {
 
-         blocks[i].adopt(self, self.ends[R], 0);
 
-       }
 
-       return self;
 
-     });
 
-   };
 
-   // createLeftOf(cursor) and the methods it calls
 
-   _.createLeftOf = function(cursor) {
 
-     var cmd = this;
 
-     var replacedFragment = cmd.replacedFragment;
 
-     cmd.createBlocks();
 
-     MathElement.jQize(cmd.html());
 
-     if (replacedFragment) {
 
-       replacedFragment.adopt(cmd.ends[L], 0, 0);
 
-       replacedFragment.jQ.appendTo(cmd.ends[L].jQ);
 
-     }
 
-     cursor.jQ.before(cmd.jQ);
 
-     cursor[L] = cmd.adopt(cursor.parent, cursor[L], cursor[R]);
 
-     cmd.finalizeInsert(cursor);
 
-     cmd.placeCursor(cursor);
 
-   };
 
-   _.createBlocks = function() {
 
-     var cmd = this,
 
-       numBlocks = cmd.numBlocks(),
 
-       blocks = cmd.blocks = Array(numBlocks);
 
-     for (var i = 0; i < numBlocks; i += 1) {
 
-       var newBlock = blocks[i] = MathBlock();
 
-       newBlock.adopt(cmd, cmd.ends[R], 0);
 
-     }
 
-   };
 
-   _.respace = noop; //placeholder for context-sensitive spacing
 
-   _.placeCursor = function(cursor) {
 
-     //insert the cursor at the right end of the first empty child, searching
 
-     //left-to-right, or if none empty, the right end child
 
-     cursor.insAtRightEnd(this.foldChildren(this.ends[L], function(leftward, child) {
 
-       return leftward.isEmpty() ? leftward : child;
 
-     }));
 
-   };
 
-   // remove()
 
-   _.remove = function() {
 
-     this.disown();
 
-     this.jQ.remove();
 
-     this.postOrder(function(el) { delete MathElement[el.id]; });
 
-     return this;
 
-   };
 
-   // methods involved in creating and cross-linking with HTML DOM nodes
 
-   /*
 
-     They all expect an .htmlTemplate like
 
-       '<span>&0</span>'
 
-     or
 
-       '<span><span>&0</span><span>&1</span></span>'
 
-     See html.test.js for more examples.
 
-     Requirements:
 
-     - For each block of the command, there must be exactly one "block content
 
-       marker" of the form '&<number>' where <number> is the 0-based index of the
 
-       block. (Like the LaTeX \newcommand syntax, but with a 0-based rather than
 
-       1-based index, because JavaScript because C because Dijkstra.)
 
-     - The block content marker must be the sole contents of the containing
 
-       element, there can't even be surrounding whitespace, or else we can't
 
-       guarantee sticking to within the bounds of the block content marker when
 
-       mucking with the HTML DOM.
 
-     - The HTML not only must be well-formed HTML (of course), but also must
 
-       conform to the XHTML requirements on tags, specifically all tags must
 
-       either be self-closing (like '<br/>') or come in matching pairs.
 
-       Close tags are never optional.
 
-     Note that &<number> isn't well-formed HTML; if you wanted a literal '&123',
 
-     your HTML template would have to have '&123'.
 
-   */
 
-   _.numBlocks = function() {
 
-     var matches = this.htmlTemplate.match(/&\d+/g);
 
-     return matches ? matches.length : 0;
 
-   };
 
-   _.html = function() {
 
-     // Render the entire math subtree rooted at this command, as HTML.
 
-     // Expects .createBlocks() to have been called already, since it uses the
 
-     // .blocks array of child blocks.
 
-     //
 
-     // See html.test.js for example templates and intended outputs.
 
-     //
 
-     // Given an .htmlTemplate as described above,
 
-     // - insert the mathquill-command-id attribute into all top-level tags,
 
-     //   which will be used to set this.jQ in .jQize().
 
-     //   This is straightforward:
 
-     //     * tokenize into tags and non-tags
 
-     //     * loop through top-level tokens:
 
-     //         * add #cmdId attribute macro to top-level self-closing tags
 
-     //         * else add #cmdId attribute macro to top-level open tags
 
-     //             * skip the matching top-level close tag and all tag pairs
 
-     //               in between
 
-     // - for each block content marker,
 
-     //     + replace it with the contents of the corresponding block,
 
-     //       rendered as HTML
 
-     //     + insert the mathquill-block-id attribute into the containing tag
 
-     //   This is even easier, a quick regex replace, since block tags cannot
 
-     //   contain anything besides the block content marker.
 
-     //
 
-     // Two notes:
 
-     // - The outermost loop through top-level tokens should never encounter any
 
-     //   top-level close tags, because we should have first encountered a
 
-     //   matching top-level open tag, all inner tags should have appeared in
 
-     //   matching pairs and been skipped, and then we should have skipped the
 
-     //   close tag in question.
 
-     // - All open tags should have matching close tags, which means our inner
 
-     //   loop should always encounter a close tag and drop nesting to 0. If
 
-     //   a close tag is missing, the loop will continue until i >= tokens.length
 
-     //   and token becomes undefined. This will not infinite loop, even in
 
-     //   production without pray(), because it will then TypeError on .slice().
 
-     var cmd = this;
 
-     var blocks = cmd.blocks;
 
-     var cmdId = ' mathquill-command-id=' + cmd.id;
 
-     var tokens = cmd.htmlTemplate.match(/<[^<>]+>|[^<>]+/g);
 
-     pray('no unmatched angle brackets', tokens.join('') === this.htmlTemplate);
 
-     // add cmdId to all top-level tags
 
-     for (var i = 0, token = tokens[0]; token; i += 1, token = tokens[i]) {
 
-       // top-level self-closing tags
 
-       if (token.slice(-2) === '/>') {
 
-         tokens[i] = token.slice(0,-2) + cmdId + '/>';
 
-       }
 
-       // top-level open tags
 
-       else if (token.charAt(0) === '<') {
 
-         pray('not an unmatched top-level close tag', token.charAt(1) !== '/');
 
-         tokens[i] = token.slice(0,-1) + cmdId + '>';
 
-         // skip matching top-level close tag and all tag pairs in between
 
-         var nesting = 1;
 
-         do {
 
-           i += 1, token = tokens[i];
 
-           pray('no missing close tags', token);
 
-           // close tags
 
-           if (token.slice(0,2) === '</') {
 
-             nesting -= 1;
 
-           }
 
-           // non-self-closing open tags
 
-           else if (token.charAt(0) === '<' && token.slice(-2) !== '/>') {
 
-             nesting += 1;
 
-           }
 
-         } while (nesting > 0);
 
-       }
 
-     }
 
-     return tokens.join('').replace(/>&(\d+)/g, function($0, $1) {
 
-       return ' mathquill-block-id=' + blocks[$1].id + '>' + blocks[$1].join('html');
 
-     });
 
-   };
 
-   // methods to export a string representation of the math tree
 
-   _.latex = function() {
 
-     return this.foldChildren(this.ctrlSeq, function(latex, child) {
 
-       return latex + '{' + (child.latex() || ' ') + '}';
 
-     });
 
-   };
 
-   _.textTemplate = [''];
 
-   _.text = function() {
 
-     var cmd = this, i = 0;
 
-     return cmd.foldChildren(cmd.textTemplate[i], function(text, child) {
 
-       i += 1;
 
-       var child_text = child.text();
 
-       if (text && cmd.textTemplate[i] === '('
 
-           && child_text[0] === '(' && child_text.slice(-1) === ')')
 
-         return text + child_text.slice(1, -1) + cmd.textTemplate[i];
 
-       return text + child.text() + (cmd.textTemplate[i] || '');
 
-     });
 
-   };
 
- });
 
- /**
 
-  * Lightweight command without blocks or children.
 
-  */
 
- var Symbol = P(MathCommand, function(_, _super) {
 
-   _.init = function(ctrlSeq, html, text) {
 
-     if (!text) text = ctrlSeq && ctrlSeq.length > 1 ? ctrlSeq.slice(1) : ctrlSeq;
 
-     _super.init.call(this, ctrlSeq, html, [ text ]);
 
-   };
 
-   _.parser = function() { return Parser.succeed(this); };
 
-   _.numBlocks = function() { return 0; };
 
-   _.replaces = function(replacedFragment) {
 
-     replacedFragment.remove();
 
-   };
 
-   _.createBlocks = noop;
 
-   _.latex = function(){ return this.ctrlSeq; };
 
-   _.text = function(){ return this.textTemplate; };
 
-   _.placeCursor = noop;
 
-   _.isEmpty = function(){ return true; };
 
- });
 
- /**
 
-  * Children and parent of MathCommand's. Basically partitions all the
 
-  * symbols and operators that descend (in the Math DOM tree) from
 
-  * ancestor operators.
 
-  */
 
- var MathBlock = P(MathElement, function(_) {
 
-   _.join = function(methodName) {
 
-     return this.foldChildren('', function(fold, child) {
 
-       return fold + child[methodName]();
 
-     });
 
-   };
 
-   _.latex = function() { return this.join('latex'); };
 
-   _.text = function() {
 
-     return this.ends[L] === this.ends[R] ?
 
-       this.ends[L].text() :
 
-       '(' + this.join('text') + ')'
 
-     ;
 
-   };
 
-   _.isEmpty = function() {
 
-     return this.ends[L] === 0 && this.ends[R] === 0;
 
-   };
 
-   _.write = function(cursor, ch, replacedFragment) {
 
-     var cmd;
 
-     if (ch.match(/^[a-eg-zA-Z]$/)) //exclude f because want florin
 
-       cmd = Variable(ch);
 
-     else if (cmd = CharCmds[ch] || LatexCmds[ch])
 
-       cmd = cmd(ch);
 
-     else
 
-       cmd = VanillaSymbol(ch);
 
-     if (replacedFragment) cmd.replaces(replacedFragment);
 
-     cmd.createLeftOf(cursor);
 
-   };
 
-   _.focus = function() {
 
-     this.jQ.addClass('hasCursor');
 
-     this.jQ.removeClass('empty');
 
-     return this;
 
-   };
 
-   _.blur = function() {
 
-     this.jQ.removeClass('hasCursor');
 
-     if (this.isEmpty())
 
-       this.jQ.addClass('empty');
 
-     return this;
 
-   };
 
- });
 
- /**
 
-  * Math tree fragment base class.
 
-  * Some math-tree-specific extensions to Fragment.
 
-  */
 
- var MathFragment = P(Fragment, function(_, _super) {
 
-   _.init = function(leftEnd, rightEnd) {
 
-     // just select one thing if only one argument
 
-     _super.init.call(this, leftEnd, rightEnd || leftEnd);
 
-     this.jQ = this.fold($(), function(jQ, child){ return child.jQ.add(jQ); });
 
-   };
 
-   _.latex = function() {
 
-     return this.fold('', function(latex, el){ return latex + el.latex(); });
 
-   };
 
-   _.remove = function() {
 
-     this.jQ.remove();
 
-     this.each(function(el) {
 
-       el.postOrder(function(desc) {
 
-         delete MathElement[desc.id];
 
-       });
 
-     });
 
-     return this.disown();
 
-   };
 
- });
 
- /*********************************************
 
-  * Root math elements with event delegation.
 
-  ********************************************/
 
- function createRoot(jQ, root, textbox, editable) {
 
-   var contents = jQ.contents().detach();
 
-   if (!textbox) {
 
-     jQ.addClass('mathquill-rendered-math');
 
-   }
 
-   root.jQ = jQ.attr(mqBlockId, root.id);
 
-   root.revert = function() {
 
-     jQ.empty().unbind('.mathquill')
 
-       .removeClass('mathquill-rendered-math mathquill-editable mathquill-textbox')
 
-       .append(contents);
 
-   };
 
-   var cursor = root.cursor = Cursor(root);
 
-   root.renderLatex(contents.text());
 
-   //textarea stuff
 
-   var textareaSpan = root.textarea = $('<span class="textarea"><textarea></textarea></span>'),
 
-     textarea = textareaSpan.children();
 
-   /******
 
-    * TODO [Han]: Document this
 
-    */
 
-   var textareaSelectionTimeout;
 
-   root.selectionChanged = function() {
 
-     if (textareaSelectionTimeout === undefined) {
 
-       textareaSelectionTimeout = setTimeout(setTextareaSelection);
 
-     }
 
-     forceIERedraw(jQ[0]);
 
-   };
 
-   function setTextareaSelection() {
 
-     textareaSelectionTimeout = undefined;
 
-     var latex = cursor.selection ? '$'+cursor.selection.latex()+'$' : '';
 
-     textareaManager.select(latex);
 
-   }
 
-   //prevent native selection except textarea
 
-   jQ.bind('selectstart.mathquill', function(e) {
 
-     if (e.target !== textarea[0]) e.preventDefault();
 
-     e.stopPropagation();
 
-   });
 
-   //drag-to-select event handling
 
-   var anticursor, blink = cursor.blink;
 
-   jQ.bind('mousedown.mathquill', function(e) {
 
-     function mousemove(e) {
 
-       cursor.seek($(e.target), e.pageX, e.pageY);
 
-       if (cursor[L] !== anticursor[L]
 
-           || cursor.parent !== anticursor.parent) {
 
-         cursor.selectFrom(anticursor);
 
-       }
 
-       return false;
 
-     }
 
-     // docmousemove is attached to the document, so that
 
-     // selection still works when the mouse leaves the window.
 
-     function docmousemove(e) {
 
-       // [Han]: i delete the target because of the way seek works.
 
-       // it will not move the mouse to the target, but will instead
 
-       // just seek those X and Y coordinates.  If there is a target,
 
-       // it will try to move the cursor to document, which will not work.
 
-       // cursor.seek needs to be refactored.
 
-       delete e.target;
 
-       return mousemove(e);
 
-     }
 
-     function mouseup(e) {
 
-       anticursor = undefined;
 
-       cursor.blink = blink;
 
-       if (!cursor.selection) {
 
-         if (editable) {
 
-           cursor.show();
 
-         }
 
-         else {
 
-           textareaSpan.detach();
 
-         }
 
-       }
 
-       // delete the mouse handlers now that we're not dragging anymore
 
-       jQ.unbind('mousemove', mousemove);
 
-       $(e.target.ownerDocument).unbind('mousemove', docmousemove).unbind('mouseup', mouseup);
 
-     }
 
-     setTimeout(function() { textarea.focus(); });
 
-       // preventDefault won't prevent focus on mousedown in IE<9
 
-       // that means immediately after this mousedown, whatever was
 
-       // mousedown-ed will receive focus
 
-       // http://bugs.jquery.com/ticket/10345
 
-     cursor.blink = noop;
 
-     cursor.seek($(e.target), e.pageX, e.pageY);
 
-     anticursor = Point(cursor.parent, cursor[L], cursor[R]);
 
-     if (!editable) jQ.prepend(textareaSpan);
 
-     jQ.mousemove(mousemove);
 
-     $(e.target.ownerDocument).mousemove(docmousemove).mouseup(mouseup);
 
-     return false;
 
-   });
 
-   if (!editable) {
 
-     var textareaManager = manageTextarea(textarea, { container: jQ });
 
-     jQ.bind('cut paste', false).bind('copy', setTextareaSelection)
 
-       .prepend('<span class="selectable">$'+root.latex()+'$</span>');
 
-     textarea.blur(function() {
 
-       cursor.clearSelection();
 
-       setTimeout(detach); //detaching during blur explodes in WebKit
 
-     });
 
-     function detach() {
 
-       textareaSpan.detach();
 
-     }
 
-     return;
 
-   }
 
-   var textareaManager = manageTextarea(textarea, {
 
-     container: jQ,
 
-     key: function(key, evt) {
 
-       cursor.parent.bubble('onKey', key, evt);
 
-     },
 
-     text: function(text) {
 
-       cursor.parent.bubble('onText', text);
 
-     },
 
-     cut: function(e) {
 
-       if (cursor.selection) {
 
-         setTimeout(function() {
 
-           cursor.prepareEdit();
 
-           cursor.parent.bubble('redraw');
 
-         });
 
-       }
 
-       e.stopPropagation();
 
-     },
 
-     paste: function(text) {
 
-       // FIXME HACK the parser in RootTextBlock needs to be moved to
 
-       // Cursor::writeLatex or something so this'll work with
 
-       // MathQuill textboxes
 
-       if (text.slice(0,1) === '$' && text.slice(-1) === '$') {
 
-         text = text.slice(1, -1);
 
-       }
 
-       else {
 
-         text = '\\text{' + text + '}';
 
-       }
 
-       cursor.writeLatex(text).show();
 
-     }
 
-   });
 
-   jQ.prepend(textareaSpan);
 
-   //root CSS classes
 
-   jQ.addClass('mathquill-editable');
 
-   if (textbox)
 
-     jQ.addClass('mathquill-textbox');
 
-   //focus and blur handling
 
-   textarea.focus(function(e) {
 
-     if (!cursor.parent)
 
-       cursor.insAtRightEnd(root);
 
-     cursor.parent.jQ.addClass('hasCursor');
 
-     if (cursor.selection) {
 
-       cursor.selection.jQ.removeClass('blur');
 
-       setTimeout(root.selectionChanged); //re-select textarea contents after tabbing away and back
 
-     }
 
-     else
 
-       cursor.show();
 
-     e.stopPropagation();
 
-   }).blur(function(e) {
 
-     cursor.hide().parent.blur();
 
-     if (cursor.selection)
 
-       cursor.selection.jQ.addClass('blur');
 
-     e.stopPropagation();
 
-   });
 
-   jQ.bind('focus.mathquill blur.mathquill', function(e) {
 
-     textarea.trigger(e);
 
-   }).blur();
 
- }
 
- var RootMathBlock = P(MathBlock, function(_, _super) {
 
-   _.latex = function() {
 
-     return _super.latex.call(this).replace(/(\\[a-z]+) (?![a-z])/ig,'$1');
 
-   };
 
-   _.text = function() {
 
-     return this.foldChildren('', function(text, child) {
 
-       return text + child.text();
 
-     });
 
-   };
 
-   _.renderLatex = function(latex) {
 
-     var jQ = this.jQ;
 
-     jQ.children().slice(1).remove();
 
-     this.ends[L] = this.ends[R] = 0;
 
-     delete this.cursor.selection;
 
-     this.cursor.insAtRightEnd(this).writeLatex(latex);
 
-   };
 
-   _.onKey = function(key, e) {
 
-     switch (key) {
 
-     case 'Ctrl-Shift-Backspace':
 
-     case 'Ctrl-Backspace':
 
-       while (this.cursor[L] || this.cursor.selection) {
 
-         this.cursor.backspace();
 
-       }
 
-       break;
 
-     case 'Shift-Backspace':
 
-     case 'Backspace':
 
-       this.cursor.backspace();
 
-       break;
 
-     // Tab or Esc -> go one block right if it exists, else escape right.
 
-     case 'Esc':
 
-     case 'Tab':
 
-     case 'Spacebar':
 
-       var parent = this.cursor.parent;
 
-       // cursor is in root editable, continue default
 
-       if (parent === this.cursor.root) {
 
-         if (key === 'Spacebar') e.preventDefault();
 
-         return;
 
-       }
 
-       this.cursor.prepareMove();
 
-       if (parent[R]) {
 
-         // go one block right
 
-         this.cursor.insAtLeftEnd(parent[R]);
 
-       } else {
 
-         // get out of the block
 
-         this.cursor.insRightOf(parent.parent);
 
-       }
 
-       break;
 
-     // Shift-Tab -> go one block left if it exists, else escape left.
 
-     case 'Shift-Tab':
 
-     case 'Shift-Esc':
 
-     case 'Shift-Spacebar':
 
-       var parent = this.cursor.parent;
 
-       //cursor is in root editable, continue default
 
-       if (parent === this.cursor.root) {
 
-         if (key === 'Shift-Spacebar') e.preventDefault();
 
-         return;
 
-       }
 
-       this.cursor.prepareMove();
 
-       if (parent[L]) {
 
-         // go one block left
 
-         this.cursor.insAtRightEnd(parent[L]);
 
-       } else {
 
-         //get out of the block
 
-         this.cursor.insLeftOf(parent.parent);
 
-       }
 
-       break;
 
-     // Prevent newlines from showing up
 
-     case 'Enter': break;
 
-     // End -> move to the end of the current block.
 
-     case 'End':
 
-       this.cursor.prepareMove().insAtRightEnd(this.cursor.parent);
 
-       break;
 
-     // Ctrl-End -> move all the way to the end of the root block.
 
-     case 'Ctrl-End':
 
-       this.cursor.prepareMove().insAtRightEnd(this);
 
-       break;
 
-     // Shift-End -> select to the end of the current block.
 
-     case 'Shift-End':
 
-       while (this.cursor[R]) {
 
-         this.cursor.selectRight();
 
-       }
 
-       break;
 
-     // Ctrl-Shift-End -> select to the end of the root block.
 
-     case 'Ctrl-Shift-End':
 
-       while (this.cursor[R] || this.cursor.parent !== this) {
 
-         this.cursor.selectRight();
 
-       }
 
-       break;
 
-     // Home -> move to the start of the root block or the current block.
 
-     case 'Home':
 
-       this.cursor.prepareMove().insAtLeftEnd(this.cursor.parent);
 
-       break;
 
-     // Ctrl-Home -> move to the start of the current block.
 
-     case 'Ctrl-Home':
 
-       this.cursor.prepareMove().insAtLeftEnd(this);
 
-       break;
 
-     // Shift-Home -> select to the start of the current block.
 
-     case 'Shift-Home':
 
-       while (this.cursor[L]) {
 
-         this.cursor.selectLeft();
 
-       }
 
-       break;
 
-     // Ctrl-Shift-Home -> move to the start of the root block.
 
-     case 'Ctrl-Shift-Home':
 
-       while (this.cursor[L] || this.cursor.parent !== this) {
 
-         this.cursor.selectLeft();
 
-       }
 
-       break;
 
-     case 'Left': this.cursor.moveLeft(); break;
 
-     case 'Shift-Left': this.cursor.selectLeft(); break;
 
-     case 'Ctrl-Left': break;
 
-     case 'Right': this.cursor.moveRight(); break;
 
-     case 'Shift-Right': this.cursor.selectRight(); break;
 
-     case 'Ctrl-Right': break;
 
-     case 'Up': this.cursor.moveUp(); break;
 
-     case 'Down': this.cursor.moveDown(); break;
 
-     case 'Shift-Up':
 
-       if (this.cursor[L]) {
 
-         while (this.cursor[L]) this.cursor.selectLeft();
 
-       } else {
 
-         this.cursor.selectLeft();
 
-       }
 
-     case 'Shift-Down':
 
-       if (this.cursor[R]) {
 
-         while (this.cursor[R]) this.cursor.selectRight();
 
-       }
 
-       else {
 
-         this.cursor.selectRight();
 
-       }
 
-     case 'Ctrl-Up': break;
 
-     case 'Ctrl-Down': break;
 
-     case 'Ctrl-Shift-Del':
 
-     case 'Ctrl-Del':
 
-       while (this.cursor[R] || this.cursor.selection) {
 
-         this.cursor.deleteForward();
 
-       }
 
-       break;
 
-     case 'Shift-Del':
 
-     case 'Del':
 
-       this.cursor.deleteForward();
 
-       break;
 
-     case 'Meta-A':
 
-     case 'Ctrl-A':
 
-       //so not stopPropagation'd at RootMathCommand
 
-       if (this !== this.cursor.root) return;
 
-       this.cursor.prepareMove().insAtRightEnd(this);
 
-       while (this.cursor[L]) this.cursor.selectLeft();
 
-       break;
 
-     default:
 
-       return false;
 
-     }
 
-     e.preventDefault();
 
-     return false;
 
-   };
 
-   _.onText = function(ch) {
 
-     this.cursor.write(ch);
 
-     return false;
 
-   };
 
- });
 
- var RootMathCommand = P(MathCommand, function(_, _super) {
 
-   _.init = function(cursor) {
 
-     _super.init.call(this, '$');
 
-     this.cursor = cursor;
 
-   };
 
-   _.htmlTemplate = '<span class="mathquill-rendered-math">&0</span>';
 
-   _.createBlocks = function() {
 
-     this.ends[L] =
 
-     this.ends[R] =
 
-       RootMathBlock();
 
-     this.blocks = [ this.ends[L] ];
 
-     this.ends[L].parent = this;
 
-     this.ends[L].cursor = this.cursor;
 
-     this.ends[L].write = function(cursor, ch, replacedFragment) {
 
-       if (ch !== '$')
 
-         MathBlock.prototype.write.call(this, cursor, ch, replacedFragment);
 
-       else if (this.isEmpty()) {
 
-         cursor.insRightOf(this.parent).backspace().show();
 
-         VanillaSymbol('\\$','$').createLeftOf(cursor);
 
-       }
 
-       else if (!cursor[R])
 
-         cursor.insRightOf(this.parent);
 
-       else if (!cursor[L])
 
-         cursor.insLeftOf(this.parent);
 
-       else
 
-         MathBlock.prototype.write.call(this, cursor, ch, replacedFragment);
 
-     };
 
-   };
 
-   _.latex = function() {
 
-     return '$' + this.ends[L].latex() + '$';
 
-   };
 
- });
 
- var RootTextBlock = P(MathBlock, function(_) {
 
-   _.renderLatex = function(latex) {
 
-     var self = this;
 
-     var cursor = self.cursor;
 
-     self.jQ.children().slice(1).remove();
 
-     self.ends[L] = self.ends[R] = 0;
 
-     delete cursor.selection;
 
-     cursor.show().insAtRightEnd(self);
 
-     var regex = Parser.regex;
 
-     var string = Parser.string;
 
-     var eof = Parser.eof;
 
-     var all = Parser.all;
 
-     // Parser RootMathCommand
 
-     var mathMode = string('$').then(latexMathParser)
 
-       // because TeX is insane, math mode doesn't necessarily
 
-       // have to end.  So we allow for the case that math mode
 
-       // continues to the end of the stream.
 
-       .skip(string('$').or(eof))
 
-       .map(function(block) {
 
-         // HACK FIXME: this shouldn't have to have access to cursor
 
-         var rootMathCommand = RootMathCommand(cursor);
 
-         rootMathCommand.createBlocks();
 
-         var rootMathBlock = rootMathCommand.ends[L];
 
-         block.children().adopt(rootMathBlock, 0, 0);
 
-         return rootMathCommand;
 
-       })
 
-     ;
 
-     var escapedDollar = string('\\$').result('$');
 
-     var textChar = escapedDollar.or(regex(/^[^$]/)).map(VanillaSymbol);
 
-     var latexText = mathMode.or(textChar).many();
 
-     var commands = latexText.skip(eof).or(all.result(false)).parse(latex);
 
-     if (commands) {
 
-       for (var i = 0; i < commands.length; i += 1) {
 
-         commands[i].adopt(self, self.ends[R], 0);
 
-       }
 
-       var html = self.join('html');
 
-       MathElement.jQize(html).appendTo(self.jQ);
 
-       this.finalizeInsert();
 
-     }
 
-   };
 
-   _.onKey = function(key) {
 
-     if (key === 'Spacebar' || key === 'Shift-Spacebar') return;
 
-     RootMathBlock.prototype.onKey.apply(this, arguments);
 
-   };
 
-   _.onText = RootMathBlock.prototype.onText;
 
-   _.write = function(cursor, ch, replacedFragment) {
 
-     if (replacedFragment) replacedFragment.remove();
 
-     if (ch === '$')
 
-       RootMathCommand(cursor).createLeftOf(cursor);
 
-     else {
 
-       var html;
 
-       if (ch === '<') html = '<';
 
-       else if (ch === '>') html = '>';
 
-       VanillaSymbol(ch, html).createLeftOf(cursor);
 
-     }
 
-   };
 
- });
 
- /***************************
 
-  * Commands and Operators.
 
-  **************************/
 
- var CharCmds = {}, LatexCmds = {}; //single character commands, LaTeX commands
 
- var scale, // = function(jQ, x, y) { ... }
 
- //will use a CSS 2D transform to scale the jQuery-wrapped HTML elements,
 
- //or the filter matrix transform fallback for IE 5.5-8, or gracefully degrade to
 
- //increasing the fontSize to match the vertical Y scaling factor.
 
- //ideas from http://github.com/louisremi/jquery.transform.js
 
- //see also http://msdn.microsoft.com/en-us/library/ms533014(v=vs.85).aspx
 
-   forceIERedraw = noop,
 
-   div = document.createElement('div'),
 
-   div_style = div.style,
 
-   transformPropNames = {
 
-     transform:1,
 
-     WebkitTransform:1,
 
-     MozTransform:1,
 
-     OTransform:1,
 
-     msTransform:1
 
-   },
 
-   transformPropName;
 
- for (var prop in transformPropNames) {
 
-   if (prop in div_style) {
 
-     transformPropName = prop;
 
-     break;
 
-   }
 
- }
 
- if (transformPropName) {
 
-   scale = function(jQ, x, y) {
 
-     jQ.css(transformPropName, 'scale('+x+','+y+')');
 
-   };
 
- }
 
- else if ('filter' in div_style) { //IE 6, 7, & 8 fallback, see https://github.com/laughinghan/mathquill/wiki/Transforms
 
-   forceIERedraw = function(el){ el.className = el.className; };
 
-   scale = function(jQ, x, y) { //NOTE: assumes y > x
 
-     x /= (1+(y-1)/2);
 
-     jQ.css('fontSize', y + 'em');
 
-     if (!jQ.hasClass('matrixed-container')) {
 
-       jQ.addClass('matrixed-container')
 
-       .wrapInner('<span class="matrixed"></span>');
 
-     }
 
-     var innerjQ = jQ.children()
 
-     .css('filter', 'progid:DXImageTransform.Microsoft'
 
-         + '.Matrix(M11=' + x + ",SizingMethod='auto expand')"
 
-     );
 
-     function calculateMarginRight() {
 
-       jQ.css('marginRight', (innerjQ.width()-1)*(x-1)/x + 'px');
 
-     }
 
-     calculateMarginRight();
 
-     var intervalId = setInterval(calculateMarginRight);
 
-     $(window).load(function() {
 
-       clearTimeout(intervalId);
 
-       calculateMarginRight();
 
-     });
 
-   };
 
- }
 
- else {
 
-   scale = function(jQ, x, y) {
 
-     jQ.css('fontSize', y + 'em');
 
-   };
 
- }
 
- var Style = P(MathCommand, function(_, _super) {
 
-   _.init = function(ctrlSeq, tagName, attrs) {
 
-     _super.init.call(this, ctrlSeq, '<'+tagName+' '+attrs+'>&0</'+tagName+'>');
 
-   };
 
- });
 
- //fonts
 
- LatexCmds.mathrm = bind(Style, '\\mathrm', 'span', 'class="roman font"');
 
- LatexCmds.mathit = bind(Style, '\\mathit', 'i', 'class="font"');
 
- LatexCmds.mathbf = bind(Style, '\\mathbf', 'b', 'class="font"');
 
- LatexCmds.mathsf = bind(Style, '\\mathsf', 'span', 'class="sans-serif font"');
 
- LatexCmds.mathtt = bind(Style, '\\mathtt', 'span', 'class="monospace font"');
 
- //text-decoration
 
- LatexCmds.underline = bind(Style, '\\underline', 'span', 'class="non-leaf underline"');
 
- LatexCmds.overline = LatexCmds.bar = bind(Style, '\\overline', 'span', 'class="non-leaf overline"');
 
- var SupSub = P(MathCommand, function(_, _super) {
 
-   _.init = function(ctrlSeq, tag, text) {
 
-     _super.init.call(this, ctrlSeq, '<'+tag+' class="non-leaf">&0</'+tag+'>', [ text ]);
 
-   };
 
-   _.finalizeTree = function() {
 
-     //TODO: use inheritance
 
-     pray('SupSub is only _ and ^',
 
-       this.ctrlSeq === '^' || this.ctrlSeq === '_'
 
-     );
 
-     if (this.ctrlSeq === '_') {
 
-       this.down = this.ends[L];
 
-       this.ends[L].up = insLeftOfMeUnlessAtEnd;
 
-     }
 
-     else {
 
-       this.up = this.ends[L];
 
-       this.ends[L].down = insLeftOfMeUnlessAtEnd;
 
-     }
 
-     function insLeftOfMeUnlessAtEnd(cursor) {
 
-       // cursor.insLeftOf(cmd), unless cursor at the end of block, and every
 
-       // ancestor cmd is at the end of every ancestor block
 
-       var cmd = this.parent, ancestorCmd = cursor;
 
-       do {
 
-         if (ancestorCmd[R]) {
 
-           cursor.insLeftOf(cmd);
 
-           return false;
 
-         }
 
-         ancestorCmd = ancestorCmd.parent.parent;
 
-       } while (ancestorCmd !== cmd);
 
-       cursor.insRightOf(cmd);
 
-       return false;
 
-     }
 
-   };
 
-   _.latex = function() {
 
-     var latex = this.ends[L].latex();
 
-     if (latex.length === 1)
 
-       return this.ctrlSeq + latex;
 
-     else
 
-       return this.ctrlSeq + '{' + (latex || ' ') + '}';
 
-   };
 
-   _.redraw = function() {
 
-     if (this[L])
 
-       this[L].respace();
 
-     //SupSub::respace recursively calls respace on all the following SupSubs
 
-     //so if leftward is a SupSub, no need to call respace on this or following nodes
 
-     if (!(this[L] instanceof SupSub)) {
 
-       this.respace();
 
-       //and if rightward is a SupSub, then this.respace() will have already called
 
-       //this[R].respace()
 
-       if (this[R] && !(this[R] instanceof SupSub))
 
-         this[R].respace();
 
-     }
 
-   };
 
-   _.respace = function() {
 
-     if (
 
-       this[L].ctrlSeq === '\\int ' || (
 
-         this[L] instanceof SupSub && this[L].ctrlSeq != this.ctrlSeq
 
-         && this[L][L] && this[L][L].ctrlSeq === '\\int '
 
-       )
 
-     ) {
 
-       if (!this.limit) {
 
-         this.limit = true;
 
-         this.jQ.addClass('limit');
 
-       }
 
-     }
 
-     else {
 
-       if (this.limit) {
 
-         this.limit = false;
 
-         this.jQ.removeClass('limit');
 
-       }
 
-     }
 
-     this.respaced = this[L] instanceof SupSub && this[L].ctrlSeq != this.ctrlSeq && !this[L].respaced;
 
-     if (this.respaced) {
 
-       var fontSize = +this.jQ.css('fontSize').slice(0,-2),
 
-         leftWidth = this[L].jQ.outerWidth(),
 
-         thisWidth = this.jQ.outerWidth();
 
-       this.jQ.css({
 
-         left: (this.limit && this.ctrlSeq === '_' ? -.25 : 0) - leftWidth/fontSize + 'em',
 
-         marginRight: .1 - min(thisWidth, leftWidth)/fontSize + 'em'
 
-           //1px extra so it doesn't wrap in retarded browsers (Firefox 2, I think)
 
-       });
 
-     }
 
-     else if (this.limit && this.ctrlSeq === '_') {
 
-       this.jQ.css({
 
-         left: '-.25em',
 
-         marginRight: ''
 
-       });
 
-     }
 
-     else {
 
-       this.jQ.css({
 
-         left: '',
 
-         marginRight: ''
 
-       });
 
-     }
 
-     if (this[R] instanceof SupSub)
 
-       this[R].respace();
 
-     return this;
 
-   };
 
- });
 
- LatexCmds.subscript =
 
- LatexCmds._ = bind(SupSub, '_', 'sub', '_');
 
- LatexCmds.superscript =
 
- LatexCmds.supscript =
 
- LatexCmds['^'] = bind(SupSub, '^', 'sup', '**');
 
- var Fraction =
 
- LatexCmds.frac =
 
- LatexCmds.dfrac =
 
- LatexCmds.cfrac =
 
- LatexCmds.fraction = P(MathCommand, function(_, _super) {
 
-   _.ctrlSeq = '\\frac';
 
-   _.htmlTemplate =
 
-       '<span class="fraction non-leaf">'
 
-     +   '<span class="numerator">&0</span>'
 
-     +   '<span class="denominator">&1</span>'
 
-     +   '<span style="display:inline-block;width:0"> </span>'
 
-     + '</span>'
 
-   ;
 
-   _.textTemplate = ['(', '/', ')'];
 
-   _.finalizeTree = function() {
 
-     this.up = this.ends[R].up = this.ends[L];
 
-     this.down = this.ends[L].down = this.ends[R];
 
-   };
 
- });
 
- var LiveFraction =
 
- LatexCmds.over =
 
- CharCmds['/'] = P(Fraction, function(_, _super) {
 
-   _.createLeftOf = function(cursor) {
 
-     if (!this.replacedFragment) {
 
-       var leftward = cursor[L];
 
-       while (leftward &&
 
-         !(
 
-           leftward instanceof BinaryOperator ||
 
-           leftward instanceof TextBlock ||
 
-           leftward instanceof BigSymbol ||
 
-           ',;:'.split('').indexOf(leftward.ctrlSeq) > -1
 
-         ) //lookbehind for operator
 
-       )
 
-         leftward = leftward[L];
 
-       if (leftward instanceof BigSymbol && leftward[R] instanceof SupSub) {
 
-         leftward = leftward[R];
 
-         if (leftward[R] instanceof SupSub && leftward[R].ctrlSeq != leftward.ctrlSeq)
 
-           leftward = leftward[R];
 
-       }
 
-       if (leftward !== cursor[L]) {
 
-         this.replaces(MathFragment(leftward[R] || cursor.parent.ends[L], cursor[L]));
 
-         cursor[L] = leftward;
 
-       }
 
-     }
 
-     _super.createLeftOf.call(this, cursor);
 
-   };
 
- });
 
- var SquareRoot =
 
- LatexCmds.sqrt =
 
- LatexCmds['√'] = P(MathCommand, function(_, _super) {
 
-   _.ctrlSeq = '\\sqrt';
 
-   _.htmlTemplate =
 
-       '<span class="non-leaf">'
 
-     +   '<span class="scaled sqrt-prefix">√</span>'
 
-     +   '<span class="non-leaf sqrt-stem">&0</span>'
 
-     + '</span>'
 
-   ;
 
-   _.textTemplate = ['sqrt(', ')'];
 
-   _.parser = function() {
 
-     return latexMathParser.optBlock.then(function(optBlock) {
 
-       return latexMathParser.block.map(function(block) {
 
-         var nthroot = NthRoot();
 
-         nthroot.blocks = [ optBlock, block ];
 
-         optBlock.adopt(nthroot, 0, 0);
 
-         block.adopt(nthroot, optBlock, 0);
 
-         return nthroot;
 
-       });
 
-     }).or(_super.parser.call(this));
 
-   };
 
-   _.redraw = function() {
 
-     var block = this.ends[R].jQ;
 
-     scale(block.prev(), 1, block.innerHeight()/+block.css('fontSize').slice(0,-2) - .1);
 
-   };
 
- });
 
- var Vec = LatexCmds.vec = P(MathCommand, function(_, _super) {
 
-   _.ctrlSeq = '\\vec';
 
-   _.htmlTemplate =
 
-       '<span class="non-leaf">'
 
-     +   '<span class="vector-prefix">→</span>'
 
-     +   '<span class="vector-stem">&0</span>'
 
-     + '</span>'
 
-   ;
 
-   _.textTemplate = ['vec(', ')'];
 
- });
 
- var NthRoot =
 
- LatexCmds.nthroot = P(SquareRoot, function(_, _super) {
 
-   _.htmlTemplate =
 
-       '<sup class="nthroot non-leaf">&0</sup>'
 
-     + '<span class="scaled">'
 
-     +   '<span class="sqrt-prefix scaled">√</span>'
 
-     +   '<span class="sqrt-stem non-leaf">&1</span>'
 
-     + '</span>'
 
-   ;
 
-   _.textTemplate = ['sqrt[', '](', ')'];
 
-   _.latex = function() {
 
-     return '\\sqrt['+this.ends[L].latex()+']{'+this.ends[R].latex()+'}';
 
-   };
 
- });
 
- // Round/Square/Curly/Angle Brackets (aka Parens/Brackets/Braces)
 
- var Bracket = P(MathCommand, function(_, _super) {
 
-   _.init = function(open, close, ctrlSeq, end) {
 
-     _super.init.call(this, '\\left'+ctrlSeq,
 
-         '<span class="non-leaf">'
 
-       +   '<span class="scaled paren">'+open+'</span>'
 
-       +   '<span class="non-leaf">&0</span>'
 
-       +   '<span class="scaled paren">'+close+'</span>'
 
-       + '</span>',
 
-       [open, close]);
 
-     this.end = '\\right'+end;
 
-   };
 
-   _.jQadd = function() {
 
-     _super.jQadd.apply(this, arguments);
 
-     var jQ = this.jQ;
 
-     this.bracketjQs = jQ.children(':first').add(jQ.children(':last'));
 
-   };
 
-   _.latex = function() {
 
-     return this.ctrlSeq + this.ends[L].latex() + this.end;
 
-   };
 
-   _.redraw = function() {
 
-     var blockjQ = this.ends[L].jQ;
 
-     var height = blockjQ.outerHeight()/+blockjQ.css('fontSize').slice(0,-2);
 
-     scale(this.bracketjQs, min(1 + .2*(height - 1), 1.2), 1.05*height);
 
-   };
 
- });
 
- LatexCmds.left = P(MathCommand, function(_) {
 
-   _.parser = function() {
 
-     var regex = Parser.regex;
 
-     var string = Parser.string;
 
-     var succeed = Parser.succeed;
 
-     var optWhitespace = Parser.optWhitespace;
 
-     return optWhitespace.then(regex(/^(?:[([|]|\\\{)/))
 
-       .then(function(open) {
 
-         if (open.charAt(0) === '\\') open = open.slice(1);
 
-         var cmd = CharCmds[open]();
 
-         return latexMathParser
 
-           .map(function (block) {
 
-             cmd.blocks = [ block ];
 
-             block.adopt(cmd, 0, 0);
 
-           })
 
-           .then(string('\\right'))
 
-           .skip(optWhitespace)
 
-           .then(regex(/^(?:[\])|]|\\\})/))
 
-           .then(function(close) {
 
-             if (close.slice(-1) !== cmd.end.slice(-1)) {
 
-               return Parser.fail('open doesn\'t match close');
 
-             }
 
-             return succeed(cmd);
 
-           })
 
-         ;
 
-       })
 
-     ;
 
-   };
 
- });
 
- LatexCmds.right = P(MathCommand, function(_) {
 
-   _.parser = function() {
 
-     return Parser.fail('unmatched \\right');
 
-   };
 
- });
 
- LatexCmds.lbrace =
 
- CharCmds['{'] = bind(Bracket, '{', '}', '\\{', '\\}');
 
- LatexCmds.langle =
 
- LatexCmds.lang = bind(Bracket, '⟨','⟩','\\langle ','\\rangle ');
 
- // Closing bracket matching opening bracket above
 
- var CloseBracket = P(Bracket, function(_, _super) {
 
-   _.createLeftOf = function(cursor) {
 
-     // if I'm at the end of my parent who is a matching open-paren,
 
-     // and I am not replacing a selection fragment, don't create me,
 
-     // just put cursor after my parent
 
-     if (!cursor[R] && cursor.parent.parent && cursor.parent.parent.end === this.end && !this.replacedFragment)
 
-       cursor.insRightOf(cursor.parent.parent);
 
-     else
 
-       _super.createLeftOf.call(this, cursor);
 
-   };
 
-   _.placeCursor = function(cursor) {
 
-     this.ends[L].blur();
 
-     cursor.insRightOf(this);
 
-   };
 
- });
 
- LatexCmds.rbrace =
 
- CharCmds['}'] = bind(CloseBracket, '{','}','\\{','\\}');
 
- LatexCmds.rangle =
 
- LatexCmds.rang = bind(CloseBracket, '⟨','⟩','\\langle ','\\rangle ');
 
- var parenMixin = function(_, _super) {
 
-   _.init = function(open, close) {
 
-     _super.init.call(this, open, close, open, close);
 
-   };
 
- };
 
- var Paren = P(Bracket, parenMixin);
 
- LatexCmds.lparen =
 
- CharCmds['('] = bind(Paren, '(', ')');
 
- LatexCmds.lbrack =
 
- LatexCmds.lbracket =
 
- CharCmds['['] = bind(Paren, '[', ']');
 
- var CloseParen = P(CloseBracket, parenMixin);
 
- LatexCmds.rparen =
 
- CharCmds[')'] = bind(CloseParen, '(', ')');
 
- LatexCmds.rbrack =
 
- LatexCmds.rbracket =
 
- CharCmds[']'] = bind(CloseParen, '[', ']');
 
- var Pipes =
 
- LatexCmds.lpipe =
 
- LatexCmds.rpipe =
 
- CharCmds['|'] = P(Paren, function(_, _super) {
 
-   _.init = function() {
 
-     _super.init.call(this, '|', '|');
 
-   };
 
-   _.createLeftOf = CloseBracket.prototype.createLeftOf;
 
- });
 
- var TextBlock =
 
- CharCmds.$ =
 
- LatexCmds.text =
 
- LatexCmds.textnormal =
 
- LatexCmds.textrm =
 
- LatexCmds.textup =
 
- LatexCmds.textmd = P(MathCommand, function(_, _super) {
 
-   _.ctrlSeq = '\\text';
 
-   _.htmlTemplate = '<span class="text">&0</span>';
 
-   _.replaces = function(replacedText) {
 
-     if (replacedText instanceof MathFragment)
 
-       this.replacedText = replacedText.remove().jQ.text();
 
-     else if (typeof replacedText === 'string')
 
-       this.replacedText = replacedText;
 
-   };
 
-   _.textTemplate = ['"', '"'];
 
-   _.parser = function() {
 
-     var self = this;
 
-     // TODO: correctly parse text mode
 
-     var string = Parser.string;
 
-     var regex = Parser.regex;
 
-     var optWhitespace = Parser.optWhitespace;
 
-     return optWhitespace
 
-       .then(string('{')).then(regex(/^[^}]*/)).skip(string('}'))
 
-       .map(function(text) {
 
-         self.createBlocks();
 
-         var block = self.ends[L];
 
-         for (var i = 0; i < text.length; i += 1) {
 
-           var ch = VanillaSymbol(text.charAt(i));
 
-           ch.adopt(block, block.ends[R], 0);
 
-         }
 
-         return self;
 
-       })
 
-     ;
 
-   };
 
-   _.createBlocks = function() {
 
-     //FIXME: another possible Law of Demeter violation, but this seems much cleaner, like it was supposed to be done this way
 
-     this.ends[L] =
 
-     this.ends[R] =
 
-       InnerTextBlock();
 
-     this.blocks = [ this.ends[L] ];
 
-     this.ends[L].parent = this;
 
-   };
 
-   _.finalizeInsert = function() {
 
-     //FIXME HACK blur removes the TextBlock
 
-     this.ends[L].blur = function() { delete this.blur; return this; };
 
-     _super.finalizeInsert.call(this);
 
-   };
 
-   _.createLeftOf = function(cursor) {
 
-     _super.createLeftOf.call(this, this.cursor = cursor);
 
-     if (this.replacedText)
 
-       for (var i = 0; i < this.replacedText.length; i += 1)
 
-         this.ends[L].write(cursor, this.replacedText.charAt(i));
 
-   };
 
- });
 
- var InnerTextBlock = P(MathBlock, function(_, _super) {
 
-   _.onKey = function(key, e) {
 
-     if (key === 'Spacebar' || key === 'Shift-Spacebar') return false;
 
-   };
 
-   // backspace and delete at ends of block don't unwrap
 
-   _.deleteOutOf = function(dir, cursor) {
 
-     if (this.isEmpty()) cursor.insRightOf(this.parent);
 
-   };
 
-   _.write = function(cursor, ch, replacedFragment) {
 
-     if (replacedFragment) replacedFragment.remove();
 
-     if (ch !== '$') {
 
-       var html;
 
-       if (ch === '<') html = '<';
 
-       else if (ch === '>') html = '>';
 
-       VanillaSymbol(ch, html).createLeftOf(cursor);
 
-     }
 
-     else if (this.isEmpty()) {
 
-       cursor.insRightOf(this.parent).backspace();
 
-       VanillaSymbol('\\$','$').createLeftOf(cursor);
 
-     }
 
-     else if (!cursor[R])
 
-       cursor.insRightOf(this.parent);
 
-     else if (!cursor[L])
 
-       cursor.insLeftOf(this.parent);
 
-     else { //split apart
 
-       var rightward = TextBlock();
 
-       rightward.replaces(MathFragment(cursor[R], this.ends[R]));
 
-       cursor.insRightOf(this.parent);
 
-       // FIXME HACK: pretend no prev so they don't get merged when
 
-       // .createLeftOf() calls blur on the InnerTextBlock
 
-       rightward.adopt = function() {
 
-         delete this.adopt;
 
-         this.adopt.apply(this, arguments);
 
-         this[L] = 0;
 
-       };
 
-       rightward.createLeftOf(cursor);
 
-       rightward[L] = this.parent;
 
-       cursor.insLeftOf(rightward);
 
-     }
 
-     return false;
 
-   };
 
-   _.blur = function() {
 
-     this.jQ.removeClass('hasCursor');
 
-     if (this.isEmpty()) {
 
-       var textblock = this.parent, cursor = textblock.cursor;
 
-       if (cursor.parent === this)
 
-         this.jQ.addClass('empty');
 
-       else {
 
-         cursor.hide();
 
-         textblock.remove();
 
-         if (cursor[R] === textblock)
 
-           cursor[R] = textblock[R];
 
-         else if (cursor[L] === textblock)
 
-           cursor[L] = textblock[L];
 
-         cursor.show().parent.bubble('redraw');
 
-       }
 
-     }
 
-     return this;
 
-   };
 
-   _.focus = function() {
 
-     _super.focus.call(this);
 
-     var textblock = this.parent;
 
-     if (textblock[R].ctrlSeq === textblock.ctrlSeq) { //TODO: seems like there should be a better way to move MathElements around
 
-       var innerblock = this,
 
-         cursor = textblock.cursor,
 
-         rightward = textblock[R].ends[L];
 
-       rightward.eachChild(function(child){
 
-         child.parent = innerblock;
 
-         child.jQ.appendTo(innerblock.jQ);
 
-       });
 
-       if (this.ends[R])
 
-         this.ends[R][R] = rightward.ends[L];
 
-       else
 
-         this.ends[L] = rightward.ends[L];
 
-       rightward.ends[L][L] = this.ends[R];
 
-       this.ends[R] = rightward.ends[R];
 
-       rightward.parent.remove();
 
-       if (cursor[L])
 
-         cursor.insRightOf(cursor[L]);
 
-       else
 
-         cursor.insAtLeftEnd(this);
 
-       cursor.parent.bubble('redraw');
 
-     }
 
-     else if (textblock[L].ctrlSeq === textblock.ctrlSeq) {
 
-       var cursor = textblock.cursor;
 
-       if (cursor[L])
 
-         textblock[L].ends[L].focus();
 
-       else
 
-         cursor.insAtRightEnd(textblock[L].ends[L]);
 
-     }
 
-     return this;
 
-   };
 
- });
 
- function makeTextBlock(latex, tagName, attrs) {
 
-   return P(TextBlock, {
 
-     ctrlSeq: latex,
 
-     htmlTemplate: '<'+tagName+' '+attrs+'>&0</'+tagName+'>'
 
-   });
 
- }
 
- LatexCmds.em = LatexCmds.italic = LatexCmds.italics =
 
- LatexCmds.emph = LatexCmds.textit = LatexCmds.textsl =
 
-   makeTextBlock('\\textit', 'i', 'class="text"');
 
- LatexCmds.strong = LatexCmds.bold = LatexCmds.textbf =
 
-   makeTextBlock('\\textbf', 'b', 'class="text"');
 
- LatexCmds.sf = LatexCmds.textsf =
 
-   makeTextBlock('\\textsf', 'span', 'class="sans-serif text"');
 
- LatexCmds.tt = LatexCmds.texttt =
 
-   makeTextBlock('\\texttt', 'span', 'class="monospace text"');
 
- LatexCmds.textsc =
 
-   makeTextBlock('\\textsc', 'span', 'style="font-variant:small-caps" class="text"');
 
- LatexCmds.uppercase =
 
-   makeTextBlock('\\uppercase', 'span', 'style="text-transform:uppercase" class="text"');
 
- LatexCmds.lowercase =
 
-   makeTextBlock('\\lowercase', 'span', 'style="text-transform:lowercase" class="text"');
 
- // input box to type a variety of LaTeX commands beginning with a backslash
 
- var LatexCommandInput =
 
- CharCmds['\\'] = P(MathCommand, function(_, _super) {
 
-   _.ctrlSeq = '\\';
 
-   _.replaces = function(replacedFragment) {
 
-     this._replacedFragment = replacedFragment.disown();
 
-     this.isEmpty = function() { return false; };
 
-   };
 
-   _.htmlTemplate = '<span class="latex-command-input non-leaf">\\<span>&0</span></span>';
 
-   _.textTemplate = ['\\'];
 
-   _.createBlocks = function() {
 
-     _super.createBlocks.call(this);
 
-     this.ends[L].focus = function() {
 
-       this.parent.jQ.addClass('hasCursor');
 
-       if (this.isEmpty())
 
-         this.parent.jQ.removeClass('empty');
 
-       return this;
 
-     };
 
-     this.ends[L].blur = function() {
 
-       this.parent.jQ.removeClass('hasCursor');
 
-       if (this.isEmpty())
 
-         this.parent.jQ.addClass('empty');
 
-       return this;
 
-     };
 
-   };
 
-   _.createLeftOf = function(cursor) {
 
-     _super.createLeftOf.call(this, cursor);
 
-     this.cursor = cursor.insAtRightEnd(this.ends[L]);
 
-     if (this._replacedFragment) {
 
-       var el = this.jQ[0];
 
-       this.jQ =
 
-         this._replacedFragment.jQ.addClass('blur').bind(
 
-           'mousedown mousemove', //FIXME: is monkey-patching the mousedown and mousemove handlers the right way to do this?
 
-           function(e) {
 
-             $(e.target = el).trigger(e);
 
-             return false;
 
-           }
 
-         ).insertBefore(this.jQ).add(this.jQ);
 
-     }
 
-     this.ends[L].write = function(cursor, ch, replacedFragment) {
 
-       if (replacedFragment) replacedFragment.remove();
 
-       if (ch.match(/[a-z]/i)) VanillaSymbol(ch).createLeftOf(cursor);
 
-       else {
 
-         this.parent.renderCommand();
 
-         if (ch !== '\\' || !this.isEmpty()) this.parent.parent.write(cursor, ch);
 
-       }
 
-     };
 
-   };
 
-   _.latex = function() {
 
-     return '\\' + this.ends[L].latex() + ' ';
 
-   };
 
-   _.onKey = function(key, e) {
 
-     if (key === 'Tab' || key === 'Enter' || key === 'Spacebar') {
 
-       this.renderCommand();
 
-       e.preventDefault();
 
-       return false;
 
-     }
 
-   };
 
-   _.renderCommand = function() {
 
-     this.jQ = this.jQ.last();
 
-     this.remove();
 
-     if (this[R]) {
 
-       this.cursor.insLeftOf(this[R]);
 
-     } else {
 
-       this.cursor.insAtRightEnd(this.parent);
 
-     }
 
-     var latex = this.ends[L].latex(), cmd;
 
-     if (!latex) latex = 'backslash';
 
-     this.cursor.insertCmd(latex, this._replacedFragment);
 
-   };
 
- });
 
- var Binomial =
 
- LatexCmds.binom =
 
- LatexCmds.binomial = P(MathCommand, function(_, _super) {
 
-   _.ctrlSeq = '\\binom';
 
-   _.htmlTemplate =
 
-       '<span class="paren scaled">(</span>'
 
-     + '<span class="non-leaf">'
 
-     +   '<span class="array non-leaf">'
 
-     +     '<span>&0</span>'
 
-     +     '<span>&1</span>'
 
-     +   '</span>'
 
-     + '</span>'
 
-     + '<span class="paren scaled">)</span>'
 
-   ;
 
-   _.textTemplate = ['choose(',',',')'];
 
-   _.redraw = function() {
 
-     var blockjQ = this.jQ.eq(1);
 
-     var height = blockjQ.outerHeight()/+blockjQ.css('fontSize').slice(0,-2);
 
-     var parens = this.jQ.filter('.paren');
 
-     scale(parens, min(1 + .2*(height - 1), 1.2), 1.05*height);
 
-   };
 
- });
 
- var Choose =
 
- LatexCmds.choose = P(Binomial, function(_) {
 
-   _.createLeftOf = LiveFraction.prototype.createLeftOf;
 
- });
 
- var Vector =
 
- LatexCmds.vector = P(MathCommand, function(_, _super) {
 
-   _.ctrlSeq = '\\vector';
 
-   _.htmlTemplate = '<span class="array"><span>&0</span></span>';
 
-   _.latex = function() {
 
-     return '\\begin{matrix}' + this.foldChildren([], function(latex, child) {
 
-       latex.push(child.latex());
 
-       return latex;
 
-     }).join('\\\\') + '\\end{matrix}';
 
-   };
 
-   _.text = function() {
 
-     return '[' + this.foldChildren([], function(text, child) {
 
-       text.push(child.text());
 
-       return text;
 
-     }).join() + ']';
 
-   };
 
-   _.createLeftOf = function(cursor) {
 
-     _super.createLeftOf.call(this, this.cursor = cursor);
 
-   };
 
-   _.onKey = function(key, e) {
 
-     var currentBlock = this.cursor.parent;
 
-     if (currentBlock.parent === this) {
 
-       if (key === 'Enter') { //enter
 
-         var newBlock = MathBlock();
 
-         newBlock.parent = this;
 
-         newBlock.jQ = $('<span></span>')
 
-           .attr(mqBlockId, newBlock.id)
 
-           .insertAfter(currentBlock.jQ);
 
-         if (currentBlock[R])
 
-           currentBlock[R][L] = newBlock;
 
-         else
 
-           this.ends[R] = newBlock;
 
-         newBlock[R] = currentBlock[R];
 
-         currentBlock[R] = newBlock;
 
-         newBlock[L] = currentBlock;
 
-         this.bubble('redraw').cursor.insAtRightEnd(newBlock);
 
-         e.preventDefault();
 
-         return false;
 
-       }
 
-       else if (key === 'Tab' && !currentBlock[R]) {
 
-         if (currentBlock.isEmpty()) {
 
-           if (currentBlock[L]) {
 
-             this.cursor.insRightOf(this);
 
-             delete currentBlock[L][R];
 
-             this.ends[R] = currentBlock[L];
 
-             currentBlock.jQ.remove();
 
-             this.bubble('redraw');
 
-             e.preventDefault();
 
-             return false;
 
-           }
 
-           else
 
-             return;
 
-         }
 
-         var newBlock = MathBlock();
 
-         newBlock.parent = this;
 
-         newBlock.jQ = $('<span></span>').attr(mqBlockId, newBlock.id).appendTo(this.jQ);
 
-         this.ends[R] = newBlock;
 
-         currentBlock[R] = newBlock;
 
-         newBlock[L] = currentBlock;
 
-         this.bubble('redraw').cursor.insAtRightEnd(newBlock);
 
-         e.preventDefault();
 
-         return false;
 
-       }
 
-       else if (e.which === 8) { //backspace
 
-         if (currentBlock.isEmpty()) {
 
-           if (currentBlock[L]) {
 
-             this.cursor.insAtRightEnd(currentBlock[L])
 
-             currentBlock[L][R] = currentBlock[R];
 
-           }
 
-           else {
 
-             this.cursor.insLeftOf(this);
 
-             this.ends[L] = currentBlock[R];
 
-           }
 
-           if (currentBlock[R])
 
-             currentBlock[R][L] = currentBlock[L];
 
-           else
 
-             this.ends[R] = currentBlock[L];
 
-           currentBlock.jQ.remove();
 
-           if (this.isEmpty())
 
-             this.cursor.deleteForward();
 
-           else
 
-             this.bubble('redraw');
 
-           e.preventDefault();
 
-           return false;
 
-         }
 
-         else if (!this.cursor[L]) {
 
-           e.preventDefault();
 
-           return false;
 
-         }
 
-       }
 
-     }
 
-   };
 
- });
 
- LatexCmds.editable = P(RootMathCommand, function(_, _super) {
 
-   _.init = function() {
 
-     MathCommand.prototype.init.call(this, '\\editable');
 
-   };
 
-   _.jQadd = function() {
 
-     var self = this;
 
-     // FIXME: this entire method is a giant hack to get around
 
-     // having to call createBlocks, and createRoot expecting to
 
-     // render the contents' LaTeX. Both need to be refactored.
 
-     _super.jQadd.apply(self, arguments);
 
-     var block = self.ends[L].disown();
 
-     var blockjQ = self.jQ.children().detach();
 
-     self.ends[L] =
 
-     self.ends[R] =
 
-       RootMathBlock();
 
-     self.blocks = [ self.ends[L] ];
 
-     self.ends[L].parent = self;
 
-     createRoot(self.jQ, self.ends[L], false, true);
 
-     self.cursor = self.ends[L].cursor;
 
-     block.children().adopt(self.ends[L], 0, 0);
 
-     blockjQ.appendTo(self.ends[L].jQ);
 
-     self.ends[L].cursor.insAtRightEnd(self.ends[L]);
 
-   };
 
-   _.latex = function(){ return this.ends[L].latex(); };
 
-   _.text = function(){ return this.ends[L].text(); };
 
- });
 
- /**********************************
 
-  * Symbols and Special Characters
 
-  *********************************/
 
- LatexCmds.f = bind(Symbol, 'f', '<var class="florin">ƒ</var><span style="display:inline-block;width:0"> </span>');
 
- var Variable = P(Symbol, function(_, _super) {
 
-   _.init = function(ch, html) {
 
-     _super.init.call(this, ch, '<var>'+(html || ch)+'</var>');
 
-   };
 
-   _.text = function() {
 
-     var text = this.ctrlSeq;
 
-     if (this[L] && !(this[L] instanceof Variable)
 
-         && !(this[L] instanceof BinaryOperator))
 
-       text = '*' + text;
 
-     if (this[R] && !(this[R] instanceof BinaryOperator)
 
-         && !(this[R].ctrlSeq === '^'))
 
-       text += '*';
 
-     return text;
 
-   };
 
- });
 
- var VanillaSymbol = P(Symbol, function(_, _super) {
 
-   _.init = function(ch, html) {
 
-     _super.init.call(this, ch, '<span>'+(html || ch)+'</span>');
 
-   };
 
- });
 
- CharCmds[' '] = bind(VanillaSymbol, '\\:', ' ');
 
- LatexCmds.prime = CharCmds["'"] = bind(VanillaSymbol, "'", '′');
 
- // does not use Symbola font
 
- var NonSymbolaSymbol = P(Symbol, function(_, _super) {
 
-   _.init = function(ch, html) {
 
-     _super.init.call(this, ch, '<span class="nonSymbola">'+(html || ch)+'</span>');
 
-   };
 
- });
 
- LatexCmds['@'] = NonSymbolaSymbol;
 
- LatexCmds['&'] = bind(NonSymbolaSymbol, '\\&', '&');
 
- LatexCmds['%'] = bind(NonSymbolaSymbol, '\\%', '%');
 
- //the following are all Greek to me, but this helped a lot: http://www.ams.org/STIX/ion/stixsig03.html
 
- //lowercase Greek letter variables
 
- LatexCmds.alpha =
 
- LatexCmds.beta =
 
- LatexCmds.gamma =
 
- LatexCmds.delta =
 
- LatexCmds.zeta =
 
- LatexCmds.eta =
 
- LatexCmds.theta =
 
- LatexCmds.iota =
 
- LatexCmds.kappa =
 
- LatexCmds.mu =
 
- LatexCmds.nu =
 
- LatexCmds.xi =
 
- LatexCmds.rho =
 
- LatexCmds.sigma =
 
- LatexCmds.tau =
 
- LatexCmds.chi =
 
- LatexCmds.psi =
 
- LatexCmds.omega = P(Variable, function(_, _super) {
 
-   _.init = function(latex) {
 
-     _super.init.call(this,'\\'+latex+' ','&'+latex+';');
 
-   };
 
- });
 
- //why can't anybody FUCKING agree on these
 
- LatexCmds.phi = //W3C or Unicode?
 
-   bind(Variable,'\\phi ','ϕ');
 
- LatexCmds.phiv = //Elsevier and 9573-13
 
- LatexCmds.varphi = //AMS and LaTeX
 
-   bind(Variable,'\\varphi ','φ');
 
- LatexCmds.epsilon = //W3C or Unicode?
 
-   bind(Variable,'\\epsilon ','ϵ');
 
- LatexCmds.epsiv = //Elsevier and 9573-13
 
- LatexCmds.varepsilon = //AMS and LaTeX
 
-   bind(Variable,'\\varepsilon ','ε');
 
- LatexCmds.piv = //W3C/Unicode and Elsevier and 9573-13
 
- LatexCmds.varpi = //AMS and LaTeX
 
-   bind(Variable,'\\varpi ','ϖ');
 
- LatexCmds.sigmaf = //W3C/Unicode
 
- LatexCmds.sigmav = //Elsevier
 
- LatexCmds.varsigma = //LaTeX
 
-   bind(Variable,'\\varsigma ','ς');
 
- LatexCmds.thetav = //Elsevier and 9573-13
 
- LatexCmds.vartheta = //AMS and LaTeX
 
- LatexCmds.thetasym = //W3C/Unicode
 
-   bind(Variable,'\\vartheta ','ϑ');
 
- LatexCmds.upsilon = //AMS and LaTeX and W3C/Unicode
 
- LatexCmds.upsi = //Elsevier and 9573-13
 
-   bind(Variable,'\\upsilon ','υ');
 
- //these aren't even mentioned in the HTML character entity references
 
- LatexCmds.gammad = //Elsevier
 
- LatexCmds.Gammad = //9573-13 -- WTF, right? I dunno if this was a typo in the reference (see above)
 
- LatexCmds.digamma = //LaTeX
 
-   bind(Variable,'\\digamma ','ϝ');
 
- LatexCmds.kappav = //Elsevier
 
- LatexCmds.varkappa = //AMS and LaTeX
 
-   bind(Variable,'\\varkappa ','ϰ');
 
- LatexCmds.rhov = //Elsevier and 9573-13
 
- LatexCmds.varrho = //AMS and LaTeX
 
-   bind(Variable,'\\varrho ','ϱ');
 
- //Greek constants, look best in un-italicised Times New Roman
 
- LatexCmds.pi = LatexCmds['π'] = bind(NonSymbolaSymbol,'\\pi ','π');
 
- LatexCmds.lambda = bind(NonSymbolaSymbol,'\\lambda ','λ');
 
- //uppercase greek letters
 
- LatexCmds.Upsilon = //LaTeX
 
- LatexCmds.Upsi = //Elsevier and 9573-13
 
- LatexCmds.upsih = //W3C/Unicode "upsilon with hook"
 
- LatexCmds.Upsih = //'cos it makes sense to me
 
-   bind(Symbol,'\\Upsilon ','<var style="font-family: serif">ϒ</var>'); //Symbola's 'upsilon with a hook' is a capital Y without hooks :(
 
- //other symbols with the same LaTeX command and HTML character entity reference
 
- LatexCmds.Gamma =
 
- LatexCmds.Delta =
 
- LatexCmds.Theta =
 
- LatexCmds.Lambda =
 
- LatexCmds.Xi =
 
- LatexCmds.Pi =
 
- LatexCmds.Sigma =
 
- LatexCmds.Phi =
 
- LatexCmds.Psi =
 
- LatexCmds.Omega =
 
- LatexCmds.forall = P(VanillaSymbol, function(_, _super) {
 
-   _.init = function(latex) {
 
-     _super.init.call(this,'\\'+latex+' ','&'+latex+';');
 
-   };
 
- });
 
- // symbols that aren't a single MathCommand, but are instead a whole
 
- // Fragment. Creates the Fragment from a LaTeX string
 
- var LatexFragment = P(MathCommand, function(_) {
 
-   _.init = function(latex) { this.latex = latex; };
 
-   _.createLeftOf = function(cursor) { cursor.writeLatex(this.latex); };
 
-   _.parser = function() {
 
-     var frag = latexMathParser.parse(this.latex).children();
 
-     return Parser.succeed(frag);
 
-   };
 
- });
 
- // for what seems to me like [stupid reasons][1], Unicode provides
 
- // subscripted and superscripted versions of all ten Arabic numerals,
 
- // as well as [so-called "vulgar fractions"][2].
 
- // Nobody really cares about most of them, but some of them actually
 
- // predate Unicode, dating back to [ISO-8859-1][3], apparently also
 
- // known as "Latin-1", which among other things [Windows-1252][4]
 
- // largely coincides with, so Microsoft Word sometimes inserts them
 
- // and they get copy-pasted into MathQuill.
 
- //
 
- // (Irrelevant but funny story: Windows-1252 is actually a strict
 
- // superset of the "closely related but distinct"[3] "ISO 8859-1" --
 
- // see the lack of a dash after "ISO"? Completely different character
 
- // set, like elephants vs elephant seals, or "Zombies" vs "Zombie
 
- // Redneck Torture Family". What kind of idiot would get them confused.
 
- // People in fact got them confused so much, it was so common to
 
- // mislabel Windows-1252 text as ISO-8859-1, that most modern web
 
- // browsers and email clients treat the MIME charset of ISO-8859-1
 
- // as actually Windows-1252, behavior now standard in the HTML5 spec.)
 
- //
 
- // [1]: http://en.wikipedia.org/wiki/Unicode_subscripts_and_superscripts
 
- // [2]: http://en.wikipedia.org/wiki/Number_Forms
 
- // [3]: http://en.wikipedia.org/wiki/ISO/IEC_8859-1
 
- // [4]: http://en.wikipedia.org/wiki/Windows-1252
 
- LatexCmds['¹'] = bind(LatexFragment, '^1');
 
- LatexCmds['²'] = bind(LatexFragment, '^2');
 
- LatexCmds['³'] = bind(LatexFragment, '^3');
 
- LatexCmds['¼'] = bind(LatexFragment, '\\frac14');
 
- LatexCmds['½'] = bind(LatexFragment, '\\frac12');
 
- LatexCmds['¾'] = bind(LatexFragment, '\\frac34');
 
- var BinaryOperator = P(Symbol, function(_, _super) {
 
-   _.init = function(ctrlSeq, html, text) {
 
-     _super.init.call(this,
 
-       ctrlSeq, '<span class="binary-operator">'+html+'</span>', text
 
-     );
 
-   };
 
- });
 
- var PlusMinus = P(BinaryOperator, function(_) {
 
-   _.init = VanillaSymbol.prototype.init;
 
-   _.respace = function() {
 
-     if (!this[L]) {
 
-       this.jQ[0].className = '';
 
-     }
 
-     else if (
 
-       this[L] instanceof BinaryOperator &&
 
-       this[R] && !(this[R] instanceof BinaryOperator)
 
-     ) {
 
-       this.jQ[0].className = 'unary-operator';
 
-     }
 
-     else {
 
-       this.jQ[0].className = 'binary-operator';
 
-     }
 
-     return this;
 
-   };
 
- });
 
- LatexCmds['+'] = bind(PlusMinus, '+', '+');
 
- //yes, these are different dashes, I think one is an en dash and the other is a hyphen
 
- LatexCmds['–'] = LatexCmds['-'] = bind(PlusMinus, '-', '−');
 
- LatexCmds['±'] = LatexCmds.pm = LatexCmds.plusmn = LatexCmds.plusminus =
 
-   bind(PlusMinus,'\\pm ','±');
 
- LatexCmds.mp = LatexCmds.mnplus = LatexCmds.minusplus =
 
-   bind(PlusMinus,'\\mp ','∓');
 
- CharCmds['*'] = LatexCmds.sdot = LatexCmds.cdot =
 
-   bind(BinaryOperator, '\\cdot ', '·');
 
- //semantically should be ⋅, but · looks better
 
- LatexCmds['='] = bind(BinaryOperator, '=', '=');
 
- LatexCmds['<'] = bind(BinaryOperator, '<', '<');
 
- LatexCmds['>'] = bind(BinaryOperator, '>', '>');
 
- LatexCmds.notin =
 
- LatexCmds.sim =
 
- LatexCmds.cong =
 
- LatexCmds.equiv =
 
- LatexCmds.oplus =
 
- LatexCmds.otimes = P(BinaryOperator, function(_, _super) {
 
-   _.init = function(latex) {
 
-     _super.init.call(this, '\\'+latex+' ', '&'+latex+';');
 
-   };
 
- });
 
- LatexCmds.times = bind(BinaryOperator, '\\times ', '×', '[x]');
 
- LatexCmds['÷'] = LatexCmds.div = LatexCmds.divide = LatexCmds.divides =
 
-   bind(BinaryOperator,'\\div ','÷', '[/]');
 
- LatexCmds['≠'] = LatexCmds.ne = LatexCmds.neq = bind(BinaryOperator,'\\ne ','≠');
 
- LatexCmds.ast = LatexCmds.star = LatexCmds.loast = LatexCmds.lowast =
 
-   bind(BinaryOperator,'\\ast ','∗');
 
-   //case 'there4 = // a special exception for this one, perhaps?
 
- LatexCmds.therefor = LatexCmds.therefore =
 
-   bind(BinaryOperator,'\\therefore ','∴');
 
- LatexCmds.cuz = // l33t
 
- LatexCmds.because = bind(BinaryOperator,'\\because ','∵');
 
- LatexCmds.prop = LatexCmds.propto = bind(BinaryOperator,'\\propto ','∝');
 
- LatexCmds['≈'] = LatexCmds.asymp = LatexCmds.approx = bind(BinaryOperator,'\\approx ','≈');
 
- LatexCmds.lt = bind(BinaryOperator,'<','<');
 
- LatexCmds.gt = bind(BinaryOperator,'>','>');
 
- LatexCmds['≤'] = LatexCmds.le = LatexCmds.leq = bind(BinaryOperator,'\\le ','≤');
 
- LatexCmds['≥'] = LatexCmds.ge = LatexCmds.geq = bind(BinaryOperator,'\\ge ','≥');
 
- LatexCmds.isin = LatexCmds['in'] = bind(BinaryOperator,'\\in ','∈');
 
- LatexCmds.ni = LatexCmds.contains = bind(BinaryOperator,'\\ni ','∋');
 
- LatexCmds.notni = LatexCmds.niton = LatexCmds.notcontains = LatexCmds.doesnotcontain =
 
-   bind(BinaryOperator,'\\not\\ni ','∌');
 
- LatexCmds.sub = LatexCmds.subset = bind(BinaryOperator,'\\subset ','⊂');
 
- LatexCmds.sup = LatexCmds.supset = LatexCmds.superset =
 
-   bind(BinaryOperator,'\\supset ','⊃');
 
- LatexCmds.nsub = LatexCmds.notsub =
 
- LatexCmds.nsubset = LatexCmds.notsubset =
 
-   bind(BinaryOperator,'\\not\\subset ','⊄');
 
- LatexCmds.nsup = LatexCmds.notsup =
 
- LatexCmds.nsupset = LatexCmds.notsupset =
 
- LatexCmds.nsuperset = LatexCmds.notsuperset =
 
-   bind(BinaryOperator,'\\not\\supset ','⊅');
 
- LatexCmds.sube = LatexCmds.subeq = LatexCmds.subsete = LatexCmds.subseteq =
 
-   bind(BinaryOperator,'\\subseteq ','⊆');
 
- LatexCmds.supe = LatexCmds.supeq =
 
- LatexCmds.supsete = LatexCmds.supseteq =
 
- LatexCmds.supersete = LatexCmds.superseteq =
 
-   bind(BinaryOperator,'\\supseteq ','⊇');
 
- LatexCmds.nsube = LatexCmds.nsubeq =
 
- LatexCmds.notsube = LatexCmds.notsubeq =
 
- LatexCmds.nsubsete = LatexCmds.nsubseteq =
 
- LatexCmds.notsubsete = LatexCmds.notsubseteq =
 
-   bind(BinaryOperator,'\\not\\subseteq ','⊈');
 
- LatexCmds.nsupe = LatexCmds.nsupeq =
 
- LatexCmds.notsupe = LatexCmds.notsupeq =
 
- LatexCmds.nsupsete = LatexCmds.nsupseteq =
 
- LatexCmds.notsupsete = LatexCmds.notsupseteq =
 
- LatexCmds.nsupersete = LatexCmds.nsuperseteq =
 
- LatexCmds.notsupersete = LatexCmds.notsuperseteq =
 
-   bind(BinaryOperator,'\\not\\supseteq ','⊉');
 
- //sum, product, coproduct, integral
 
- var BigSymbol = P(Symbol, function(_, _super) {
 
-   _.init = function(ch, html) {
 
-     _super.init.call(this, ch, '<big>'+html+'</big>');
 
-   };
 
- });
 
- LatexCmds['∑'] = LatexCmds.sum = LatexCmds.summation = bind(BigSymbol,'\\sum ','∑');
 
- LatexCmds['∏'] = LatexCmds.prod = LatexCmds.product = bind(BigSymbol,'\\prod ','∏');
 
- LatexCmds.coprod = LatexCmds.coproduct = bind(BigSymbol,'\\coprod ','∐');
 
- LatexCmds['∫'] = LatexCmds['int'] = LatexCmds.integral = bind(BigSymbol,'\\int ','∫');
 
- //the canonical sets of numbers
 
- LatexCmds.N = LatexCmds.naturals = LatexCmds.Naturals =
 
-   bind(VanillaSymbol,'\\mathbb{N}','ℕ');
 
- LatexCmds.P =
 
- LatexCmds.primes = LatexCmds.Primes =
 
- LatexCmds.projective = LatexCmds.Projective =
 
- LatexCmds.probability = LatexCmds.Probability =
 
-   bind(VanillaSymbol,'\\mathbb{P}','ℙ');
 
- LatexCmds.Z = LatexCmds.integers = LatexCmds.Integers =
 
-   bind(VanillaSymbol,'\\mathbb{Z}','ℤ');
 
- LatexCmds.Q = LatexCmds.rationals = LatexCmds.Rationals =
 
-   bind(VanillaSymbol,'\\mathbb{Q}','ℚ');
 
- LatexCmds.R = LatexCmds.reals = LatexCmds.Reals =
 
-   bind(VanillaSymbol,'\\mathbb{R}','ℝ');
 
- LatexCmds.C =
 
- LatexCmds.complex = LatexCmds.Complex =
 
- LatexCmds.complexes = LatexCmds.Complexes =
 
- LatexCmds.complexplane = LatexCmds.Complexplane = LatexCmds.ComplexPlane =
 
-   bind(VanillaSymbol,'\\mathbb{C}','ℂ');
 
- LatexCmds.H = LatexCmds.Hamiltonian = LatexCmds.quaternions = LatexCmds.Quaternions =
 
-   bind(VanillaSymbol,'\\mathbb{H}','ℍ');
 
- //spacing
 
- LatexCmds.quad = LatexCmds.emsp = bind(VanillaSymbol,'\\quad ','    ');
 
- LatexCmds.qquad = bind(VanillaSymbol,'\\qquad ','        ');
 
- /* spacing special characters, gonna have to implement this in LatexCommandInput::onText somehow
 
- case ',':
 
-   return VanillaSymbol('\\, ',' ');
 
- case ':':
 
-   return VanillaSymbol('\\: ','  ');
 
- case ';':
 
-   return VanillaSymbol('\\; ','   ');
 
- case '!':
 
-   return Symbol('\\! ','<span style="margin-right:-.2em"></span>');
 
- */
 
- //binary operators
 
- LatexCmds.diamond = bind(VanillaSymbol, '\\diamond ', '◇');
 
- LatexCmds.bigtriangleup = bind(VanillaSymbol, '\\bigtriangleup ', '△');
 
- LatexCmds.ominus = bind(VanillaSymbol, '\\ominus ', '⊖');
 
- LatexCmds.uplus = bind(VanillaSymbol, '\\uplus ', '⊎');
 
- LatexCmds.bigtriangledown = bind(VanillaSymbol, '\\bigtriangledown ', '▽');
 
- LatexCmds.sqcap = bind(VanillaSymbol, '\\sqcap ', '⊓');
 
- LatexCmds.triangleleft = bind(VanillaSymbol, '\\triangleleft ', '⊲');
 
- LatexCmds.sqcup = bind(VanillaSymbol, '\\sqcup ', '⊔');
 
- LatexCmds.triangleright = bind(VanillaSymbol, '\\triangleright ', '⊳');
 
- LatexCmds.odot = bind(VanillaSymbol, '\\odot ', '⊙');
 
- LatexCmds.bigcirc = bind(VanillaSymbol, '\\bigcirc ', '◯');
 
- LatexCmds.dagger = bind(VanillaSymbol, '\\dagger ', '†');
 
- LatexCmds.ddagger = bind(VanillaSymbol, '\\ddagger ', '‡');
 
- LatexCmds.wr = bind(VanillaSymbol, '\\wr ', '≀');
 
- LatexCmds.amalg = bind(VanillaSymbol, '\\amalg ', '∐');
 
- //relationship symbols
 
- LatexCmds.models = bind(VanillaSymbol, '\\models ', '⊨');
 
- LatexCmds.prec = bind(VanillaSymbol, '\\prec ', '≺');
 
- LatexCmds.succ = bind(VanillaSymbol, '\\succ ', '≻');
 
- LatexCmds.preceq = bind(VanillaSymbol, '\\preceq ', '≼');
 
- LatexCmds.succeq = bind(VanillaSymbol, '\\succeq ', '≽');
 
- LatexCmds.simeq = bind(VanillaSymbol, '\\simeq ', '≃');
 
- LatexCmds.mid = bind(VanillaSymbol, '\\mid ', '∣');
 
- LatexCmds.ll = bind(VanillaSymbol, '\\ll ', '≪');
 
- LatexCmds.gg = bind(VanillaSymbol, '\\gg ', '≫');
 
- LatexCmds.parallel = bind(VanillaSymbol, '\\parallel ', '∥');
 
- LatexCmds.bowtie = bind(VanillaSymbol, '\\bowtie ', '⋈');
 
- LatexCmds.sqsubset = bind(VanillaSymbol, '\\sqsubset ', '⊏');
 
- LatexCmds.sqsupset = bind(VanillaSymbol, '\\sqsupset ', '⊐');
 
- LatexCmds.smile = bind(VanillaSymbol, '\\smile ', '⌣');
 
- LatexCmds.sqsubseteq = bind(VanillaSymbol, '\\sqsubseteq ', '⊑');
 
- LatexCmds.sqsupseteq = bind(VanillaSymbol, '\\sqsupseteq ', '⊒');
 
- LatexCmds.doteq = bind(VanillaSymbol, '\\doteq ', '≐');
 
- LatexCmds.frown = bind(VanillaSymbol, '\\frown ', '⌢');
 
- LatexCmds.vdash = bind(VanillaSymbol, '\\vdash ', '⊦');
 
- LatexCmds.dashv = bind(VanillaSymbol, '\\dashv ', '⊣');
 
- //arrows
 
- LatexCmds.longleftarrow = bind(VanillaSymbol, '\\longleftarrow ', '←');
 
- LatexCmds.longrightarrow = bind(VanillaSymbol, '\\longrightarrow ', '→');
 
- LatexCmds.Longleftarrow = bind(VanillaSymbol, '\\Longleftarrow ', '⇐');
 
- LatexCmds.Longrightarrow = bind(VanillaSymbol, '\\Longrightarrow ', '⇒');
 
- LatexCmds.longleftrightarrow = bind(VanillaSymbol, '\\longleftrightarrow ', '↔');
 
- LatexCmds.updownarrow = bind(VanillaSymbol, '\\updownarrow ', '↕');
 
- LatexCmds.Longleftrightarrow = bind(VanillaSymbol, '\\Longleftrightarrow ', '⇔');
 
- LatexCmds.Updownarrow = bind(VanillaSymbol, '\\Updownarrow ', '⇕');
 
- LatexCmds.mapsto = bind(VanillaSymbol, '\\mapsto ', '↦');
 
- LatexCmds.nearrow = bind(VanillaSymbol, '\\nearrow ', '↗');
 
- LatexCmds.hookleftarrow = bind(VanillaSymbol, '\\hookleftarrow ', '↩');
 
- LatexCmds.hookrightarrow = bind(VanillaSymbol, '\\hookrightarrow ', '↪');
 
- LatexCmds.searrow = bind(VanillaSymbol, '\\searrow ', '↘');
 
- LatexCmds.leftharpoonup = bind(VanillaSymbol, '\\leftharpoonup ', '↼');
 
- LatexCmds.rightharpoonup = bind(VanillaSymbol, '\\rightharpoonup ', '⇀');
 
- LatexCmds.swarrow = bind(VanillaSymbol, '\\swarrow ', '↙');
 
- LatexCmds.leftharpoondown = bind(VanillaSymbol, '\\leftharpoondown ', '↽');
 
- LatexCmds.rightharpoondown = bind(VanillaSymbol, '\\rightharpoondown ', '⇁');
 
- LatexCmds.nwarrow = bind(VanillaSymbol, '\\nwarrow ', '↖');
 
- //Misc
 
- LatexCmds.ldots = bind(VanillaSymbol, '\\ldots ', '…');
 
- LatexCmds.cdots = bind(VanillaSymbol, '\\cdots ', '⋯');
 
- LatexCmds.vdots = bind(VanillaSymbol, '\\vdots ', '⋮');
 
- LatexCmds.ddots = bind(VanillaSymbol, '\\ddots ', '⋰');
 
- LatexCmds.surd = bind(VanillaSymbol, '\\surd ', '√');
 
- LatexCmds.triangle = bind(VanillaSymbol, '\\triangle ', '▵');
 
- LatexCmds.ell = bind(VanillaSymbol, '\\ell ', 'ℓ');
 
- LatexCmds.top = bind(VanillaSymbol, '\\top ', '⊤');
 
- LatexCmds.flat = bind(VanillaSymbol, '\\flat ', '♭');
 
- LatexCmds.natural = bind(VanillaSymbol, '\\natural ', '♮');
 
- LatexCmds.sharp = bind(VanillaSymbol, '\\sharp ', '♯');
 
- LatexCmds.wp = bind(VanillaSymbol, '\\wp ', '℘');
 
- LatexCmds.bot = bind(VanillaSymbol, '\\bot ', '⊥');
 
- LatexCmds.clubsuit = bind(VanillaSymbol, '\\clubsuit ', '♣');
 
- LatexCmds.diamondsuit = bind(VanillaSymbol, '\\diamondsuit ', '♢');
 
- LatexCmds.heartsuit = bind(VanillaSymbol, '\\heartsuit ', '♡');
 
- LatexCmds.spadesuit = bind(VanillaSymbol, '\\spadesuit ', '♠');
 
- //variable-sized
 
- LatexCmds.oint = bind(VanillaSymbol, '\\oint ', '∮');
 
- LatexCmds.bigcap = bind(VanillaSymbol, '\\bigcap ', '∩');
 
- LatexCmds.bigcup = bind(VanillaSymbol, '\\bigcup ', '∪');
 
- LatexCmds.bigsqcup = bind(VanillaSymbol, '\\bigsqcup ', '⊔');
 
- LatexCmds.bigvee = bind(VanillaSymbol, '\\bigvee ', '∨');
 
- LatexCmds.bigwedge = bind(VanillaSymbol, '\\bigwedge ', '∧');
 
- LatexCmds.bigodot = bind(VanillaSymbol, '\\bigodot ', '⊙');
 
- LatexCmds.bigotimes = bind(VanillaSymbol, '\\bigotimes ', '⊗');
 
- LatexCmds.bigoplus = bind(VanillaSymbol, '\\bigoplus ', '⊕');
 
- LatexCmds.biguplus = bind(VanillaSymbol, '\\biguplus ', '⊎');
 
- //delimiters
 
- LatexCmds.lfloor = bind(VanillaSymbol, '\\lfloor ', '⌊');
 
- LatexCmds.rfloor = bind(VanillaSymbol, '\\rfloor ', '⌋');
 
- LatexCmds.lceil = bind(VanillaSymbol, '\\lceil ', '⌈');
 
- LatexCmds.rceil = bind(VanillaSymbol, '\\rceil ', '⌉');
 
- LatexCmds.slash = bind(VanillaSymbol, '\\slash ', '/');
 
- LatexCmds.opencurlybrace = bind(VanillaSymbol, '\\opencurlybrace ', '{');
 
- LatexCmds.closecurlybrace = bind(VanillaSymbol, '\\closecurlybrace ', '}');
 
- //various symbols
 
- LatexCmds.caret = bind(VanillaSymbol,'\\caret ','^');
 
- LatexCmds.underscore = bind(VanillaSymbol,'\\underscore ','_');
 
- LatexCmds.backslash = bind(VanillaSymbol,'\\backslash ','\\');
 
- LatexCmds.vert = bind(VanillaSymbol,'|');
 
- LatexCmds.perp = LatexCmds.perpendicular = bind(VanillaSymbol,'\\perp ','⊥');
 
- LatexCmds.nabla = LatexCmds.del = bind(VanillaSymbol,'\\nabla ','∇');
 
- LatexCmds.hbar = bind(VanillaSymbol,'\\hbar ','ℏ');
 
- LatexCmds.AA = LatexCmds.Angstrom = LatexCmds.angstrom =
 
-   bind(VanillaSymbol,'\\text\\AA ','Å');
 
- LatexCmds.ring = LatexCmds.circ = LatexCmds.circle =
 
-   bind(VanillaSymbol,'\\circ ','∘');
 
- LatexCmds.bull = LatexCmds.bullet = bind(VanillaSymbol,'\\bullet ','•');
 
- LatexCmds.setminus = LatexCmds.smallsetminus =
 
-   bind(VanillaSymbol,'\\setminus ','∖');
 
- LatexCmds.not = //bind(Symbol,'\\not ','<span class="not">/</span>');
 
- LatexCmds['¬'] = LatexCmds.neg = bind(VanillaSymbol,'\\neg ','¬');
 
- LatexCmds['…'] = LatexCmds.dots = LatexCmds.ellip = LatexCmds.hellip =
 
- LatexCmds.ellipsis = LatexCmds.hellipsis =
 
-   bind(VanillaSymbol,'\\dots ','…');
 
- LatexCmds.converges =
 
- LatexCmds.darr = LatexCmds.dnarr = LatexCmds.dnarrow = LatexCmds.downarrow =
 
-   bind(VanillaSymbol,'\\downarrow ','↓');
 
- LatexCmds.dArr = LatexCmds.dnArr = LatexCmds.dnArrow = LatexCmds.Downarrow =
 
-   bind(VanillaSymbol,'\\Downarrow ','⇓');
 
- LatexCmds.diverges = LatexCmds.uarr = LatexCmds.uparrow =
 
-   bind(VanillaSymbol,'\\uparrow ','↑');
 
- LatexCmds.uArr = LatexCmds.Uparrow = bind(VanillaSymbol,'\\Uparrow ','⇑');
 
- LatexCmds.to = bind(BinaryOperator,'\\to ','→');
 
- LatexCmds.rarr = LatexCmds.rightarrow = bind(VanillaSymbol,'\\rightarrow ','→');
 
- LatexCmds.implies = bind(BinaryOperator,'\\Rightarrow ','⇒');
 
- LatexCmds.rArr = LatexCmds.Rightarrow = bind(VanillaSymbol,'\\Rightarrow ','⇒');
 
- LatexCmds.gets = bind(BinaryOperator,'\\gets ','←');
 
- LatexCmds.larr = LatexCmds.leftarrow = bind(VanillaSymbol,'\\leftarrow ','←');
 
- LatexCmds.impliedby = bind(BinaryOperator,'\\Leftarrow ','⇐');
 
- LatexCmds.lArr = LatexCmds.Leftarrow = bind(VanillaSymbol,'\\Leftarrow ','⇐');
 
- LatexCmds.harr = LatexCmds.lrarr = LatexCmds.leftrightarrow =
 
-   bind(VanillaSymbol,'\\leftrightarrow ','↔');
 
- LatexCmds.iff = bind(BinaryOperator,'\\Leftrightarrow ','⇔');
 
- LatexCmds.hArr = LatexCmds.lrArr = LatexCmds.Leftrightarrow =
 
-   bind(VanillaSymbol,'\\Leftrightarrow ','⇔');
 
- LatexCmds.Re = LatexCmds.Real = LatexCmds.real = bind(VanillaSymbol,'\\Re ','ℜ');
 
- LatexCmds.Im = LatexCmds.imag =
 
- LatexCmds.image = LatexCmds.imagin = LatexCmds.imaginary = LatexCmds.Imaginary =
 
-   bind(VanillaSymbol,'\\Im ','ℑ');
 
- LatexCmds.part = LatexCmds.partial = bind(VanillaSymbol,'\\partial ','∂');
 
- LatexCmds.inf = LatexCmds.infin = LatexCmds.infty = LatexCmds.infinity =
 
-   bind(VanillaSymbol,'\\infty ','∞');
 
- LatexCmds.alef = LatexCmds.alefsym = LatexCmds.aleph = LatexCmds.alephsym =
 
-   bind(VanillaSymbol,'\\aleph ','ℵ');
 
- LatexCmds.xist = //LOL
 
- LatexCmds.xists = LatexCmds.exist = LatexCmds.exists =
 
-   bind(VanillaSymbol,'\\exists ','∃');
 
- LatexCmds.and = LatexCmds.land = LatexCmds.wedge =
 
-   bind(VanillaSymbol,'\\wedge ','∧');
 
- LatexCmds.or = LatexCmds.lor = LatexCmds.vee = bind(VanillaSymbol,'\\vee ','∨');
 
- LatexCmds.o = LatexCmds.O =
 
- LatexCmds.empty = LatexCmds.emptyset =
 
- LatexCmds.oslash = LatexCmds.Oslash =
 
- LatexCmds.nothing = LatexCmds.varnothing =
 
-   bind(BinaryOperator,'\\varnothing ','∅');
 
- LatexCmds.cup = LatexCmds.union = bind(BinaryOperator,'\\cup ','∪');
 
- LatexCmds.cap = LatexCmds.intersect = LatexCmds.intersection =
 
-   bind(BinaryOperator,'\\cap ','∩');
 
- LatexCmds.deg = LatexCmds.degree = bind(VanillaSymbol,'^\\circ ','°');
 
- LatexCmds.ang = LatexCmds.angle = bind(VanillaSymbol,'\\angle ','∠');
 
- var NonItalicizedFunction = P(Symbol, function(_, _super) {
 
-   _.init = function(fn) {
 
-     _super.init.call(this, '\\'+fn+' ', '<span>'+fn+'</span>');
 
-   };
 
-   _.respace = function()
 
-   {
 
-     this.jQ[0].className =
 
-       (this[R] instanceof SupSub || this[R] instanceof Bracket) ?
 
-       '' : 'non-italicized-function';
 
-   };
 
- });
 
- LatexCmds.ln =
 
- LatexCmds.lg =
 
- LatexCmds.log =
 
- LatexCmds.span =
 
- LatexCmds.proj =
 
- LatexCmds.det =
 
- LatexCmds.dim =
 
- LatexCmds.min =
 
- LatexCmds.max =
 
- LatexCmds.mod =
 
- LatexCmds.lcm =
 
- LatexCmds.gcd =
 
- LatexCmds.gcf =
 
- LatexCmds.hcf =
 
- LatexCmds.lim = NonItalicizedFunction;
 
- (function() {
 
-   var trig = ['sin', 'cos', 'tan', 'sec', 'cosec', 'csc', 'cotan', 'cot'];
 
-   for (var i in trig) {
 
-     LatexCmds[trig[i]] =
 
-     LatexCmds[trig[i]+'h'] =
 
-     LatexCmds['a'+trig[i]] = LatexCmds['arc'+trig[i]] =
 
-     LatexCmds['a'+trig[i]+'h'] = LatexCmds['arc'+trig[i]+'h'] =
 
-       NonItalicizedFunction;
 
-   }
 
- }());
 
- // Parser MathCommand
 
- var latexMathParser = (function() {
 
-   function commandToBlock(cmd) {
 
-     var block = MathBlock();
 
-     cmd.adopt(block, 0, 0);
 
-     return block;
 
-   }
 
-   function joinBlocks(blocks) {
 
-     var firstBlock = blocks[0] || MathBlock();
 
-     for (var i = 1; i < blocks.length; i += 1) {
 
-       blocks[i].children().adopt(firstBlock, firstBlock.ends[R], 0);
 
-     }
 
-     return firstBlock;
 
-   }
 
-   var string = Parser.string;
 
-   var regex = Parser.regex;
 
-   var letter = Parser.letter;
 
-   var any = Parser.any;
 
-   var optWhitespace = Parser.optWhitespace;
 
-   var succeed = Parser.succeed;
 
-   var fail = Parser.fail;
 
-   // Parsers yielding MathCommands
 
-   var variable = letter.map(Variable);
 
-   var symbol = regex(/^[^${}\\_^]/).map(VanillaSymbol);
 
-   var controlSequence =
 
-     regex(/^[^\\a-eg-zA-Z]/) // hotfix #164; match MathBlock::write
 
-     .or(string('\\').then(
 
-       regex(/^[a-z]+/i)
 
-       .or(regex(/^\s+/).result(' '))
 
-       .or(any)
 
-     )).then(function(ctrlSeq) {
 
-       var cmdKlass = LatexCmds[ctrlSeq];
 
-       if (cmdKlass) {
 
-         return cmdKlass(ctrlSeq).parser();
 
-       }
 
-       else {
 
-         return fail('unknown command: \\'+ctrlSeq);
 
-       }
 
-     })
 
-   ;
 
-   var command =
 
-     controlSequence
 
-     .or(variable)
 
-     .or(symbol)
 
-   ;
 
-   // Parsers yielding MathBlocks
 
-   var mathGroup = string('{').then(function() { return mathSequence; }).skip(string('}'));
 
-   var mathBlock = optWhitespace.then(mathGroup.or(command.map(commandToBlock)));
 
-   var mathSequence = mathBlock.many().map(joinBlocks).skip(optWhitespace);
 
-   var optMathBlock =
 
-     string('[').then(
 
-       mathBlock.then(function(block) {
 
-         return block.join('latex') !== ']' ? succeed(block) : fail();
 
-       })
 
-       .many().map(joinBlocks).skip(optWhitespace)
 
-     ).skip(string(']'))
 
-   ;
 
-   var latexMath = mathSequence;
 
-   latexMath.block = mathBlock;
 
-   latexMath.optBlock = optMathBlock;
 
-   return latexMath;
 
- })();
 
- /********************************************
 
-  * Cursor and Selection "singleton" classes
 
-  *******************************************/
 
- /* The main thing that manipulates the Math DOM. Makes sure to manipulate the
 
- HTML DOM to match. */
 
- /* Sort of singletons, since there should only be one per editable math
 
- textbox, but any one HTML document can contain many such textboxes, so any one
 
- JS environment could actually contain many instances. */
 
- //A fake cursor in the fake textbox that the math is rendered in.
 
- var Cursor = P(Point, function(_) {
 
-   _.init = function(root) {
 
-     this.parent = this.root = root;
 
-     var jQ = this.jQ = this._jQ = $('<span class="cursor">‍</span>');
 
-     //closured for setInterval
 
-     this.blink = function(){ jQ.toggleClass('blink'); };
 
-     this.upDownCache = {};
 
-   };
 
-   _.show = function() {
 
-     this.jQ = this._jQ.removeClass('blink');
 
-     if ('intervalId' in this) //already was shown, just restart interval
 
-       clearInterval(this.intervalId);
 
-     else { //was hidden and detached, insert this.jQ back into HTML DOM
 
-       if (this[R]) {
 
-         if (this.selection && this.selection.ends[L][L] === this[L])
 
-           this.jQ.insertBefore(this.selection.jQ);
 
-         else
 
-           this.jQ.insertBefore(this[R].jQ.first());
 
-       }
 
-       else
 
-         this.jQ.appendTo(this.parent.jQ);
 
-       this.parent.focus();
 
-     }
 
-     this.intervalId = setInterval(this.blink, 500);
 
-     return this;
 
-   };
 
-   _.hide = function() {
 
-     if ('intervalId' in this)
 
-       clearInterval(this.intervalId);
 
-     delete this.intervalId;
 
-     this.jQ.detach();
 
-     this.jQ = $();
 
-     return this;
 
-   };
 
-   _.withDirInsertAt = function(dir, parent, withDir, oppDir) {
 
-     var oldParent = this.parent;
 
-     this.parent = parent;
 
-     this[dir] = withDir;
 
-     this[-dir] = oppDir;
 
-     oldParent.blur();
 
-   };
 
-   _.insDirOf = function(dir, el) {
 
-     prayDirection(dir);
 
-     this.withDirInsertAt(dir, el.parent, el[dir], el);
 
-     this.parent.jQ.addClass('hasCursor');
 
-     this.jQ.insDirOf(dir, el.jQ);
 
-     return this;
 
-   };
 
-   _.insLeftOf = function(el) { return this.insDirOf(L, el); };
 
-   _.insRightOf = function(el) { return this.insDirOf(R, el); };
 
-   _.insAtDirEnd = function(dir, el) {
 
-     prayDirection(dir);
 
-     this.withDirInsertAt(dir, el, 0, el.ends[dir]);
 
-     // never insert before textarea
 
-     if (dir === L && el.textarea) {
 
-       this.jQ.insDirOf(-dir, el.textarea);
 
-     }
 
-     else {
 
-       this.jQ.insAtDirEnd(dir, el.jQ);
 
-     }
 
-     el.focus();
 
-     return this;
 
-   };
 
-   _.insAtLeftEnd = function(el) { return this.insAtDirEnd(L, el); };
 
-   _.insAtRightEnd = function(el) { return this.insAtDirEnd(R, el); };
 
-   _.hopDir = function(dir) {
 
-     prayDirection(dir);
 
-     this.jQ.insDirOf(dir, this[dir].jQ);
 
-     this[-dir] = this[dir];
 
-     this[dir] = this[dir][dir];
 
-     return this;
 
-   };
 
-   _.hopLeft = function() { return this.hopDir(L); };
 
-   _.hopRight = function() { return this.hopDir(R); };
 
-   _.moveDirWithin = function(dir, block) {
 
-     prayDirection(dir);
 
-     if (this[dir]) {
 
-       if (this[dir].ends[-dir]) this.insAtDirEnd(-dir, this[dir].ends[-dir]);
 
-       else this.hopDir(dir);
 
-     }
 
-     else {
 
-       // we're at the beginning/end of the containing block, so do nothing
 
-       if (this.parent === block) return;
 
-       if (this.parent[dir]) this.insAtDirEnd(-dir, this.parent[dir]);
 
-       else this.insDirOf(dir, this.parent.parent);
 
-     }
 
-   };
 
-   _.moveLeftWithin = function(block) {
 
-     return this.moveDirWithin(L, block);
 
-   };
 
-   _.moveRightWithin = function(block) {
 
-     return this.moveDirWithin(R, block);
 
-   };
 
-   _.moveDir = function(dir) {
 
-     prayDirection(dir);
 
-     clearUpDownCache(this);
 
-     if (this.selection)  {
 
-       this.insDirOf(dir, this.selection.ends[dir]).clearSelection();
 
-     }
 
-     else {
 
-       this.moveDirWithin(dir, this.root);
 
-     }
 
-     return this.show();
 
-   };
 
-   _.moveLeft = function() { return this.moveDir(L); };
 
-   _.moveRight = function() { return this.moveDir(R); };
 
-   /**
 
-    * moveUp and moveDown have almost identical algorithms:
 
-    * - first check left and right, if so insAtLeft/RightEnd of them
 
-    * - else check the parent's 'up'/'down' property - if it's a function,
 
-    *   call it with the cursor as the sole argument and use the return value.
 
-    *
 
-    *   Given undefined, will bubble up to the next ancestor block.
 
-    *   Given false, will stop bubbling.
 
-    *   Given a MathBlock,
 
-    *     + moveUp will insAtRightEnd of it
 
-    *     + moveDown will insAtLeftEnd of it
 
-    *
 
-    */
 
-   _.moveUp = function() { return moveUpDown(this, 'up'); };
 
-   _.moveDown = function() { return moveUpDown(this, 'down'); };
 
-   function moveUpDown(self, dir) {
 
-     if (self[R][dir]) self.insAtLeftEnd(self[R][dir]);
 
-     else if (self[L][dir]) self.insAtRightEnd(self[L][dir]);
 
-     else {
 
-       var ancestorBlock = self.parent;
 
-       do {
 
-         var prop = ancestorBlock[dir];
 
-         if (prop) {
 
-           if (typeof prop === 'function') prop = ancestorBlock[dir](self);
 
-           if (prop === false || prop instanceof MathBlock) {
 
-             self.upDownCache[ancestorBlock.id] = Point(self.parent, self[L], self[R]);
 
-             if (prop instanceof MathBlock) {
 
-               var cached = self.upDownCache[prop.id];
 
-               if (cached) {
 
-                 if (cached[R]) {
 
-                   self.insLeftOf(cached[R]);
 
-                 } else {
 
-                   self.insAtRightEnd(cached.parent);
 
-                 }
 
-               } else {
 
-                 var pageX = offset(self).left;
 
-                 self.insAtRightEnd(prop);
 
-                 self.seekHoriz(pageX, prop);
 
-               }
 
-             }
 
-             break;
 
-           }
 
-         }
 
-         ancestorBlock = ancestorBlock.parent.parent;
 
-       } while (ancestorBlock);
 
-     }
 
-     return self.clearSelection().show();
 
-   }
 
-   _.seek = function(target, pageX, pageY) {
 
-     clearUpDownCache(this);
 
-     var cmd, block, cursor = this.clearSelection().show();
 
-     if (target.hasClass('empty')) {
 
-       cursor.insAtLeftEnd(MathElement[target.attr(mqBlockId)]);
 
-       return cursor;
 
-     }
 
-     cmd = MathElement[target.attr(mqCmdId)];
 
-     if (cmd instanceof Symbol) { //insert at whichever side is closer
 
-       if (target.outerWidth() > 2*(pageX - target.offset().left))
 
-         cursor.insLeftOf(cmd);
 
-       else
 
-         cursor.insRightOf(cmd);
 
-       return cursor;
 
-     }
 
-     if (!cmd) {
 
-       block = MathElement[target.attr(mqBlockId)];
 
-       if (!block) { //if no MathQuill data, try parent, if still no, just start from the root
 
-         target = target.parent();
 
-         cmd = MathElement[target.attr(mqCmdId)];
 
-         if (!cmd) {
 
-           block = MathElement[target.attr(mqBlockId)];
 
-           if (!block) block = cursor.root;
 
-         }
 
-       }
 
-     }
 
-     if (cmd)
 
-       cursor.insRightOf(cmd);
 
-     else
 
-       cursor.insAtRightEnd(block);
 
-     return cursor.seekHoriz(pageX, cursor.root);
 
-   };
 
-   _.seekHoriz = function(pageX, block) {
 
-     //move cursor to position closest to click
 
-     var cursor = this;
 
-     var dist = offset(cursor).left - pageX;
 
-     var leftDist;
 
-     do {
 
-       cursor.moveLeftWithin(block);
 
-       leftDist = dist;
 
-       dist = offset(cursor).left - pageX;
 
-     }
 
-     while (dist > 0 && (cursor[L] || cursor.parent !== block));
 
-     if (-dist > leftDist) cursor.moveRightWithin(block);
 
-     return cursor;
 
-   };
 
-   function offset(self) {
 
-     //in Opera 11.62, .getBoundingClientRect() and hence jQuery::offset()
 
-     //returns all 0's on inline elements with negative margin-right (like
 
-     //the cursor) at the end of their parent, so temporarily remove the
 
-     //negative margin-right when calling jQuery::offset()
 
-     //Opera bug DSK-360043
 
-     //http://bugs.jquery.com/ticket/11523
 
-     //https://github.com/jquery/jquery/pull/717
 
-     var offset = self.jQ.removeClass('cursor').offset();
 
-     self.jQ.addClass('cursor');
 
-     return offset;
 
-   }
 
-   _.writeLatex = function(latex) {
 
-     var self = this;
 
-     clearUpDownCache(self);
 
-     self.show().deleteSelection();
 
-     var all = Parser.all;
 
-     var eof = Parser.eof;
 
-     var block = latexMathParser.skip(eof).or(all.result(false)).parse(latex);
 
-     if (block) {
 
-       block.children().adopt(self.parent, self[L], self[R]);
 
-       MathElement.jQize(block.join('html')).insertBefore(self.jQ);
 
-       self[L] = block.ends[R];
 
-       block.finalizeInsert();
 
-       self.parent.bubble('redraw');
 
-     }
 
-     return this.hide();
 
-   };
 
-   _.write = function(ch) {
 
-     var seln = this.prepareWrite();
 
-     return this.insertCh(ch, seln);
 
-   };
 
-   _.insertCh = function(ch, replacedFragment) {
 
-     this.parent.write(this, ch, replacedFragment);
 
-     return this;
 
-   };
 
-   _.insertCmd = function(latexCmd, replacedFragment) {
 
-     var cmd = LatexCmds[latexCmd];
 
-     if (cmd) {
 
-       cmd = cmd(latexCmd);
 
-       if (replacedFragment) cmd.replaces(replacedFragment);
 
-       cmd.createLeftOf(this);
 
-     }
 
-     else {
 
-       cmd = TextBlock();
 
-       cmd.replaces(latexCmd);
 
-       cmd.ends[L].focus = function(){ delete this.focus; return this; };
 
-       cmd.createLeftOf(this);
 
-       this.insRightOf(cmd);
 
-       if (replacedFragment)
 
-         replacedFragment.remove();
 
-     }
 
-     return this;
 
-   };
 
-   _.unwrapGramp = function() {
 
-     var gramp = this.parent.parent;
 
-     var greatgramp = gramp.parent;
 
-     var rightward = gramp[R];
 
-     var cursor = this;
 
-     var leftward = gramp[L];
 
-     gramp.disown().eachChild(function(uncle) {
 
-       if (uncle.isEmpty()) return;
 
-       uncle.children()
 
-         .adopt(greatgramp, leftward, rightward)
 
-         .each(function(cousin) {
 
-           cousin.jQ.insertBefore(gramp.jQ.first());
 
-         })
 
-       ;
 
-       leftward = uncle.ends[R];
 
-     });
 
-     if (!this[R]) { //then find something to be rightward to insLeftOf
 
-       if (this[L])
 
-         this[R] = this[L][R];
 
-       else {
 
-         while (!this[R]) {
 
-           this.parent = this.parent[R];
 
-           if (this.parent)
 
-             this[R] = this.parent.ends[L];
 
-           else {
 
-             this[R] = gramp[R];
 
-             this.parent = greatgramp;
 
-             break;
 
-           }
 
-         }
 
-       }
 
-     }
 
-     if (this[R])
 
-       this.insLeftOf(this[R]);
 
-     else
 
-       this.insAtRightEnd(greatgramp);
 
-     gramp.jQ.remove();
 
-     if (gramp[L])
 
-       gramp[L].respace();
 
-     if (gramp[R])
 
-       gramp[R].respace();
 
-   };
 
-   _.deleteDir = function(dir) {
 
-     prayDirection(dir);
 
-     clearUpDownCache(this);
 
-     this.show();
 
-     if (this.deleteSelection()); // pass
 
-     else if (this[dir]) {
 
-       if (this[dir].isEmpty())
 
-         this[dir] = this[dir].remove()[dir];
 
-       else
 
-         this.selectDir(dir);
 
-     }
 
-     else if (this.parent !== this.root) {
 
-       if (this.parent.parent.isEmpty())
 
-         return this.insDirOf(-dir, this.parent.parent).deleteDir(dir);
 
-       else
 
-         this.unwrapGramp();
 
-     }
 
-     if (this[L])
 
-       this[L].respace();
 
-     if (this[R])
 
-       this[R].respace();
 
-     this.parent.bubble('redraw');
 
-     return this;
 
-   };
 
-   _.backspace = function() { return this.deleteDir(L); };
 
-   _.deleteForward = function() { return this.deleteDir(R); };
 
-   _.selectFrom = function(anticursor) {
 
-     //find ancestors of each with common parent
 
-     var oneA = this, otherA = anticursor; //one ancestor, the other ancestor
 
-     loopThroughAncestors: while (true) {
 
-       for (var oneI = this; oneI !== oneA.parent.parent; oneI = oneI.parent.parent) //one intermediate, the other intermediate
 
-         if (oneI.parent === otherA.parent) {
 
-           left = oneI;
 
-           right = otherA;
 
-           break loopThroughAncestors;
 
-         }
 
-       for (var otherI = anticursor; otherI !== otherA.parent.parent; otherI = otherI.parent.parent)
 
-         if (oneA.parent === otherI.parent) {
 
-           left = oneA;
 
-           right = otherI;
 
-           break loopThroughAncestors;
 
-         }
 
-       if (oneA.parent.parent)
 
-         oneA = oneA.parent.parent;
 
-       if (otherA.parent.parent)
 
-         otherA = otherA.parent.parent;
 
-     }
 
-     //figure out which is leftward and which is rightward
 
-     var left, right, leftRight;
 
-     if (left[R] !== right) {
 
-       for (var rightward = left; rightward; rightward = rightward[R]) {
 
-         if (rightward === right[L]) {
 
-           leftRight = true;
 
-           break;
 
-         }
 
-       }
 
-       if (!leftRight) {
 
-         leftRight = right;
 
-         right = left;
 
-         left = leftRight;
 
-       }
 
-     }
 
-     this.hide().selection = Selection(left[L][R] || left.parent.ends[L], right[R][L] || right.parent.ends[R]);
 
-     this.insRightOf(right[R][L] || right.parent.ends[R]);
 
-     this.root.selectionChanged();
 
-   };
 
-   _.selectDir = function(dir) {
 
-     prayDirection(dir);
 
-     clearUpDownCache(this);
 
-     if (this.selection) {
 
-       // if cursor is at the (dir) edge of selection
 
-       if (this.selection.ends[dir] === this[-dir]) {
 
-         // then extend (dir) if possible
 
-         if (this[dir]) this.hopDir(dir).selection.extendDir(dir);
 
-         // else level up if possible
 
-         else if (this.parent !== this.root) {
 
-           this.insDirOf(dir, this.parent.parent).selection.levelUp();
 
-         }
 
-       }
 
-       // else cursor is at the (-dir) edge of selection, retract if possible
 
-       else {
 
-         this.hopDir(dir);
 
-         // clear the selection if we only have one thing selected
 
-         if (this.selection.ends[dir] === this.selection.ends[-dir]) {
 
-           this.clearSelection().show();
 
-           return;
 
-         }
 
-         this.selection.retractDir(dir);
 
-       }
 
-     }
 
-     // no selection, create one
 
-     else {
 
-       if (this[dir]) this.hopDir(dir);
 
-       // else edge of a block
 
-       else {
 
-         if (this.parent === this.root) return;
 
-         this.insDirOf(dir, this.parent.parent);
 
-       }
 
-       this.hide().selection = Selection(this[-dir]);
 
-     }
 
-     this.root.selectionChanged();
 
-   };
 
-   _.selectLeft = function() { return this.selectDir(L); };
 
-   _.selectRight = function() { return this.selectDir(R); };
 
-   function clearUpDownCache(self) {
 
-     self.upDownCache = {};
 
-   }
 
-   _.prepareMove = function() {
 
-     clearUpDownCache(this);
 
-     return this.show().clearSelection();
 
-   };
 
-   _.prepareEdit = function() {
 
-     clearUpDownCache(this);
 
-     return this.show().deleteSelection();
 
-   };
 
-   _.prepareWrite = function() {
 
-     clearUpDownCache(this);
 
-     return this.show().replaceSelection();
 
-   };
 
-   _.clearSelection = function() {
 
-     if (this.selection) {
 
-       this.selection.clear();
 
-       delete this.selection;
 
-       this.root.selectionChanged();
 
-     }
 
-     return this;
 
-   };
 
-   _.deleteSelection = function() {
 
-     if (!this.selection) return false;
 
-     this[L] = this.selection.ends[L][L];
 
-     this[R] = this.selection.ends[R][R];
 
-     this.selection.remove();
 
-     this.root.selectionChanged();
 
-     return delete this.selection;
 
-   };
 
-   _.replaceSelection = function() {
 
-     var seln = this.selection;
 
-     if (seln) {
 
-       this[L] = seln.ends[L][L];
 
-       this[R] = seln.ends[R][R];
 
-       delete this.selection;
 
-     }
 
-     return seln;
 
-   };
 
- });
 
- var Selection = P(MathFragment, function(_, _super) {
 
-   _.init = function() {
 
-     var frag = this;
 
-     _super.init.apply(frag, arguments);
 
-     frag.jQwrap(frag.jQ);
 
-   };
 
-   _.jQwrap = function(children) {
 
-     this.jQ = children.wrapAll('<span class="selection"></span>').parent();
 
-       //can't do wrapAll(this.jQ = $(...)) because wrapAll will clone it
 
-   };
 
-   _.adopt = function() {
 
-     this.jQ.replaceWith(this.jQ = this.jQ.children());
 
-     return _super.adopt.apply(this, arguments);
 
-   };
 
-   _.clear = function() {
 
-     this.jQ.replaceWith(this.jQ.children());
 
-     return this;
 
-   };
 
-   _.levelUp = function() {
 
-     var seln = this,
 
-       gramp = seln.ends[L] = seln.ends[R] = seln.ends[R].parent.parent;
 
-     seln.clear().jQwrap(gramp.jQ);
 
-     return seln;
 
-   };
 
-   _.extendDir = function(dir) {
 
-     prayDirection(dir);
 
-     this.ends[dir] = this.ends[dir][dir];
 
-     this.ends[dir].jQ.insAtDirEnd(dir, this.jQ);
 
-     return this;
 
-   };
 
-   _.extendLeft = function() { return this.extendDir(L); };
 
-   _.extendRight = function() { return this.extendDir(R); };
 
-   _.retractDir = function(dir) {
 
-     prayDirection(dir);
 
-     this.ends[-dir].jQ.insDirOf(-dir, this.jQ);
 
-     this.ends[-dir] = this.ends[-dir][dir];
 
-   };
 
-   _.retractRight = function() { return this.retractDir(R); };
 
-   _.retractLeft = function() { return this.retractDir(L); };
 
- });
 
- /*********************************************************
 
-  * The actual jQuery plugin and document ready handlers.
 
-  ********************************************************/
 
- //The publicy exposed method of jQuery.prototype, available (and meant to be
 
- //called) on jQuery-wrapped HTML DOM elements.
 
- jQuery.fn.mathquill = function(cmd, latex) {
 
-   switch (cmd) {
 
-   case 'redraw':
 
-     return this.each(function() {
 
-       var blockId = $(this).attr(mqBlockId),
 
-         rootBlock = blockId && MathElement[blockId];
 
-       if (rootBlock) {
 
-         (function postOrderRedraw(el) {
 
-           el.eachChild(postOrderRedraw);
 
-           if (el.redraw) el.redraw();
 
-         }(rootBlock));
 
-       }
 
-     });
 
-   case 'revert':
 
-     return this.each(function() {
 
-       var blockId = $(this).attr(mqBlockId),
 
-         block = blockId && MathElement[blockId];
 
-       if (block && block.revert)
 
-         block.revert();
 
-     });
 
-   case 'latex':
 
-     if (arguments.length > 1) {
 
-       return this.each(function() {
 
-         var blockId = $(this).attr(mqBlockId),
 
-           block = blockId && MathElement[blockId];
 
-         if (block)
 
-           block.renderLatex(latex);
 
-       });
 
-     }
 
-     var blockId = $(this).attr(mqBlockId),
 
-       block = blockId && MathElement[blockId];
 
-     return block && block.latex();
 
-   case 'text':
 
-     var blockId = $(this).attr(mqBlockId),
 
-       block = blockId && MathElement[blockId];
 
-     return block && block.text();
 
-   case 'html':
 
-     return this.html().replace(/ ?hasCursor|hasCursor /, '')
 
-       .replace(/ class=(""|(?= |>))/g, '')
 
-       .replace(/<span class="?cursor( blink)?"?><\/span>/i, '')
 
-       .replace(/<span class="?textarea"?><textarea><\/textarea><\/span>/i, '');
 
-   case 'write':
 
-     if (arguments.length > 1)
 
-       return this.each(function() {
 
-         var blockId = $(this).attr(mqBlockId),
 
-           block = blockId && MathElement[blockId],
 
-           cursor = block && block.cursor;
 
-         if (cursor)
 
-           cursor.writeLatex(latex).parent.blur();
 
-       });
 
-   case 'cmd':
 
-     if (arguments.length > 1)
 
-       return this.each(function() {
 
-         var blockId = $(this).attr(mqBlockId),
 
-           block = blockId && MathElement[blockId],
 
-           cursor = block && block.cursor;
 
-         if (cursor) {
 
-           var seln = cursor.prepareWrite();
 
-           if (/^\\[a-z]+$/i.test(latex)) cursor.insertCmd(latex.slice(1), seln);
 
-           else cursor.insertCh(latex, seln);
 
-           cursor.hide().parent.blur();
 
-         }
 
-       });
 
-   default:
 
-     var textbox = cmd === 'textbox',
 
-       editable = textbox || cmd === 'editable',
 
-       RootBlock = textbox ? RootTextBlock : RootMathBlock;
 
-     return this.each(function() {
 
-       createRoot($(this), RootBlock(), textbox, editable);
 
-     });
 
-   }
 
- };
 
- //on document ready, mathquill-ify all `<tag class="mathquill-*">latex</tag>`
 
- //elements according to their CSS class.
 
- jQuery(function() {
 
-   jQuery('.mathquill-editable:not(.mathquill-rendered-math)').mathquill('editable');
 
-   jQuery('.mathquill-textbox:not(.mathquill-rendered-math)').mathquill('textbox');
 
-   jQuery('.mathquill-embedded-latex').mathquill();
 
- });
 
- }());
 
 
  |