2011-04-15 07:08:16 +08:00
/ * *
2012-03-01 10:19:49 +08:00
* Instructure tweaks / additions :
* * wysiwyg toolbar and misc fixes , i . e . https : //github.com/jenseng/mathquill/tree/fancyeditor2
* * AMD - ified
* * I18n ' d toolbar tabs
* /
2014-12-19 17:35:02 +08:00
// xsslint jqueryObject.identifier contents textareaSpan
// xsslint jqueryObject.property jQ
2012-03-01 10:19:49 +08:00
/ * *
* 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
2011-04-15 07:08:16 +08:00
* /
2011-11-11 00:31:45 +08:00
define ( [
'i18n!mathquill' ,
2014-12-19 17:35:02 +08:00
'jquery' , /* jQuery, $ */
'str/htmlEscape'
] , function ( I18n , $ , htmlEscape ) {
2011-04-15 07:08:16 +08:00
2012-03-01 10:19:49 +08:00
var undefined ,
_ , //temp variable of prototypes
jQueryDataKey = '[[mathquill internal data]]' ,
min = Math . min ,
max = Math . max ;
2011-11-08 01:15:13 +08:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Abstract base classes of blocks and commands .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * *
* MathElement is the core Math DOM tree node prototype .
* Both MathBlock 's and MathCommand' s descend from it .
* /
function MathElement ( ) { }
_ = MathElement . prototype ;
_ . prev = 0 ;
_ . next = 0 ;
_ . parent = 0 ;
_ . firstChild = 0 ;
_ . lastChild = 0 ;
_ . eachChild = function ( fn ) {
for ( var child = this . firstChild ; child ; child = child . next )
if ( fn . call ( this , child ) === false ) break ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
return this ;
} ;
_ . foldChildren = function ( fold , fn ) {
this . eachChild ( function ( child ) {
fold = fn . call ( this , fold , child ) ;
} ) ;
return fold ;
} ;
_ . keydown = function ( e ) {
return this . parent . keydown ( e ) ;
} ;
_ . textInput = function ( ch ) {
return this . parent . textInput ( ch ) ;
} ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
/ * *
* Commands and operators , like subscripts , exponents , or fractions .
* Descendant commands are organized into blocks .
* May be passed a MathFragment that ' s being replaced .
* /
2012-03-01 10:19:49 +08:00
function MathCommand ( ) { }
_ = MathCommand . prototype = new MathElement ;
_ . init = function ( cmd , html _template , text _template , replacedFragment ) {
2011-11-08 01:15:13 +08:00
var self = this ; // minifier optimization
2012-03-01 10:19:49 +08:00
if ( cmd ) self . cmd = cmd ;
2011-11-08 01:15:13 +08:00
if ( html _template ) self . html _template = html _template ;
if ( text _template ) self . text _template = text _template ;
self . jQ = $ ( self . html _template [ 0 ] ) . data ( jQueryDataKey , { cmd : self } ) ;
self . initBlocks ( replacedFragment ) ;
2012-03-01 10:19:49 +08:00
} ;
2011-11-08 01:15:13 +08:00
_ . initBlocks = function ( replacedFragment ) {
var self = this ;
//single-block commands
if ( self . html _template . length === 1 ) {
self . firstChild =
self . lastChild =
self . jQ . data ( jQueryDataKey ) . block =
( replacedFragment && replacedFragment . blockify ( ) ) || new MathBlock ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
self . firstChild . parent = self ;
self . firstChild . jQ = self . jQ . append ( self . firstChild . jQ ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
return ;
}
//otherwise, the succeeding elements of html_template should be child blocks
var newBlock , prev , num _blocks = self . html _template . length ;
this . firstChild = newBlock = prev =
( replacedFragment && replacedFragment . blockify ( ) ) || new MathBlock ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
newBlock . parent = self ;
newBlock . jQ = $ ( self . html _template [ 1 ] )
2011-04-15 07:08:16 +08:00
. data ( jQueryDataKey , { block : newBlock } )
2011-11-08 01:15:13 +08:00
. append ( newBlock . jQ )
2011-04-15 07:08:16 +08:00
. appendTo ( self . jQ ) ;
newBlock . blur ( ) ;
2011-11-08 01:15:13 +08:00
for ( var i = 2 ; i < num _blocks ; i += 1 ) {
newBlock = new MathBlock ;
newBlock . parent = self ;
newBlock . prev = prev ;
prev . next = newBlock ;
prev = newBlock ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
newBlock . jQ = $ ( self . html _template [ i ] )
. data ( jQueryDataKey , { block : newBlock } )
. appendTo ( self . jQ ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
newBlock . blur ( ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
self . lastChild = newBlock ;
} ;
_ . latex = function ( ) {
return this . foldChildren ( this . cmd , function ( latex , child ) {
return latex + '{' + ( child . latex ( ) || ' ' ) + '}' ;
} ) ;
} ;
2012-03-01 10:19:49 +08:00
_ . text _template = [ '' ] ;
2011-11-08 01:15:13 +08:00
_ . text = function ( ) {
var i = 0 ;
return this . foldChildren ( this . text _template [ i ] , function ( text , child ) {
i += 1 ;
var child _text = child . text ( ) ;
if ( text && this . text _template [ i ] === '('
&& child _text [ 0 ] === '(' && child _text . slice ( - 1 ) === ')' )
return text + child _text . slice ( 1 , - 1 ) + this . text _template [ i ] ;
return text + child . text ( ) + ( this . text _template [ i ] || '' ) ;
} ) ;
} ;
2012-03-01 10:19:49 +08:00
_ . insertAt = function ( cursor ) {
var cmd = this ;
cmd . parent = cursor . parent ;
cmd . next = cursor . next ;
cmd . prev = cursor . prev ;
if ( cursor . prev )
cursor . prev . next = cmd ;
else
cursor . parent . firstChild = cmd ;
if ( cursor . next )
cursor . next . prev = cmd ;
else
cursor . parent . lastChild = cmd ;
cursor . prev = cmd ;
cmd . jQ . insertBefore ( cursor . jQ ) ;
//adjust context-sensitive spacing
cmd . respace ( ) ;
if ( cmd . next )
cmd . next . respace ( ) ;
if ( cmd . prev )
cmd . prev . respace ( ) ;
cmd . placeCursor ( cursor ) ;
cursor . redraw ( ) ; //this will soon be cmd.trigger('redraw')
} ;
_ . respace = $ . noop ; //placeholder for context-sensitive spacing
_ . placeCursor = function ( cursor ) {
//append the cursor to the first empty child, or if none empty, the last one
cursor . appendTo ( this . foldChildren ( this . firstChild , function ( prev , child ) {
return prev . isEmpty ( ) ? prev : child ;
} ) ) ;
} ;
_ . isEmpty = function ( ) {
return this . foldChildren ( true , function ( isEmpty , child ) {
return isEmpty && child . isEmpty ( ) ;
} ) ;
} ;
2011-11-08 01:15:13 +08:00
_ . remove = function ( ) {
var self = this ,
prev = self . prev ,
next = self . next ,
parent = self . parent ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
if ( prev )
prev . next = next ;
else
parent . firstChild = next ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
if ( next )
next . prev = prev ;
else
parent . lastChild = prev ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
self . jQ . remove ( ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
return self ;
} ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
/ * *
* Lightweight command without blocks or children .
* /
function Symbol ( cmd , html , text ) {
2012-03-01 10:19:49 +08:00
this . init ( cmd , [ html ] ,
2011-11-08 01:15:13 +08:00
[ text || ( cmd && cmd . length > 1 ? cmd . slice ( 1 ) : cmd ) ] ) ;
}
_ = Symbol . prototype = new MathCommand ;
_ . initBlocks = $ . noop ;
_ . latex = function ( ) { return this . cmd ; } ;
_ . text = function ( ) { return this . text _template ; } ;
_ . 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 .
* /
function MathBlock ( ) { }
_ = MathBlock . prototype = new MathElement ;
_ . latex = function ( ) {
return this . foldChildren ( '' , function ( latex , child ) {
return latex + child . latex ( ) ;
} ) ;
} ;
_ . text = function ( ) {
return this . firstChild === this . lastChild ?
this . firstChild . text ( ) :
this . foldChildren ( '(' , function ( text , child ) {
return text + child . text ( ) ;
} ) + ')' ;
} ;
_ . isEmpty = function ( ) {
return this . firstChild === 0 && this . lastChild === 0 ;
} ;
_ . focus = function ( ) {
this . jQ . addClass ( 'hasCursor' ) ;
if ( this . isEmpty ( ) )
this . jQ . removeClass ( 'empty' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
return this ;
} ;
_ . blur = function ( ) {
this . jQ . removeClass ( 'hasCursor' ) ;
if ( this . isEmpty ( ) )
this . jQ . addClass ( 'empty' ) ;
return this ;
} ;
/ * *
* An entity outside the Math DOM 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 delimit a list of symbols and operators .
* /
function MathFragment ( parent , prev , next ) {
if ( ! arguments . length ) return ;
var self = this ;
self . parent = parent ;
self . prev = prev || 0 ; //so you can do 'new MathFragment(block)' without
self . next = next || 0 ; //ending up with this.prev or this.next === undefined
self . jQinit ( self . fold ( $ ( ) , function ( jQ , child ) { return child . jQ . add ( jQ ) ; } ) ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ = MathFragment . prototype ;
2012-03-01 10:19:49 +08:00
_ . remove = MathCommand . prototype . remove ;
2011-11-08 01:15:13 +08:00
_ . jQinit = function ( children ) {
this . jQ = children ;
} ;
_ . each = function ( fn ) {
for ( var el = this . prev . next || this . parent . firstChild ; el !== this . next ; el = el . next )
if ( fn . call ( this , el ) === false ) break ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
return this ;
} ;
_ . fold = function ( fold , fn ) {
this . each ( function ( el ) {
fold = fn . call ( this , fold , el ) ;
} ) ;
return fold ;
} ;
_ . latex = function ( ) {
return this . fold ( '' , function ( latex , el ) { return latex + el . latex ( ) ; } ) ;
} ;
_ . blockify = function ( ) {
var self = this ,
prev = self . prev ,
next = self . next ,
parent = self . parent ,
newBlock = new MathBlock ,
newFirstChild = newBlock . firstChild = prev . next || parent . firstChild ,
newLastChild = newBlock . lastChild = next . prev || parent . lastChild ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
if ( prev )
prev . next = next ;
else
parent . firstChild = next ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
if ( next )
next . prev = prev ;
2011-04-15 07:08:16 +08:00
else
2011-11-08 01:15:13 +08:00
parent . lastChild = prev ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
newFirstChild . prev = self . prev = 0 ;
newLastChild . next = self . next = 0 ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
self . parent = newBlock ;
self . each ( function ( el ) { el . parent = newBlock ; } ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
newBlock . jQ = self . jQ ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
return newBlock ;
} ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Root math elements with event delegation .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
function createRoot ( jQ , root , textbox , editable , include _toolbar ) {
var contents = jQ . contents ( ) . detach ( ) ;
if ( ! textbox )
jQ . addClass ( 'mathquill-rendered-math' ) ;
root . jQ = jQ . data ( jQueryDataKey , {
block : root ,
revert : function ( ) {
jQ . empty ( ) . unbind ( '.mathquill' )
. removeClass ( 'mathquill-rendered-math mathquill-editable mathquill-textbox mathquill-editor' )
. append ( contents ) ;
}
} ) ;
var cursor = root . cursor = new Cursor ( root ) ;
root . renderLatex ( contents . text ( ) ) ;
2012-03-01 10:19:49 +08:00
//textarea stuff
var textareaSpan = root . textarea = $ ( '<span class="textarea"><textarea></textarea></span>' ) ,
textarea = textareaSpan . children ( ) ;
if ( include _toolbar )
addToolbar ( root , jQ ) ;
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 ( ) + '$' : '' ;
textarea . val ( latex ) ;
if ( latex ) {
if ( textarea [ 0 ] . select )
textarea [ 0 ] . select ( ) ;
else if ( document . selection ) {
var range = textarea [ 0 ] . createTextRange ( ) ;
range . expand ( 'textedit' ) ;
range . select ( ) ;
}
}
} ;
//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 ) {
cursor . blink = $ . noop ;
cursor . seek ( $ ( e . target ) , e . pageX , e . pageY ) ;
anticursor = new MathFragment ( cursor . parent , cursor . prev , cursor . next ) ;
if ( ! editable )
jQ . prepend ( textareaSpan ) ;
jQ . mousemove ( mousemove ) ;
$ ( document ) . mousemove ( docmousemove ) . mouseup ( mouseup ) ;
e . stopPropagation ( ) ;
} ) ;
function mousemove ( e ) {
cursor . seek ( $ ( e . target ) , e . pageX , e . pageY ) ;
if ( cursor . prev !== anticursor . prev
|| cursor . parent !== anticursor . parent )
cursor . selectFrom ( anticursor ) ;
return false ;
}
function docmousemove ( e ) {
delete e . target ;
return mousemove ( e ) ;
}
function mouseup ( e ) {
anticursor = undefined ;
cursor . blink = blink ;
if ( ! cursor . selection ) {
if ( editable )
cursor . show ( ) ;
else
textareaSpan . detach ( ) ;
}
jQ . unbind ( 'mousemove' , mousemove ) ;
$ ( document ) . unbind ( 'mousemove' , docmousemove ) . unbind ( 'mouseup' , mouseup ) ;
}
if ( ! editable ) {
jQ . bind ( 'cut paste' , false ) . bind ( 'copy' , setTextareaSelection )
2014-12-19 17:35:02 +08:00
. prepend ( '<span class="selectable">$' + htmlEscape ( root . latex ( ) ) + '$</span>' ) ;
2012-03-01 10:19:49 +08:00
textarea . blur ( function ( ) {
cursor . clearSelection ( ) ;
setTimeout ( detach ) ; //detaching during blur explodes in WebKit
} ) ;
function detach ( ) {
textareaSpan . detach ( ) ;
}
2011-11-08 01:15:13 +08:00
return ;
2012-03-01 10:19:49 +08:00
}
jQ . prepend ( textareaSpan ) ;
2011-11-08 01:15:13 +08:00
2012-03-01 10:19:49 +08:00
//root CSS classes
jQ . addClass ( 'mathquill-editable' ) ;
2011-11-08 01:15:13 +08:00
if ( textbox )
jQ . addClass ( 'mathquill-textbox' ) ;
2012-03-01 10:19:49 +08:00
//focus and blur handling
2011-11-08 01:15:13 +08:00
textarea . focus ( function ( e ) {
if ( ! cursor . parent )
cursor . appendTo ( root ) ;
cursor . parent . jQ . addClass ( 'hasCursor' ) ;
2012-03-01 10:19:49 +08:00
if ( cursor . selection ) {
2011-11-08 01:15:13 +08:00
cursor . selection . jQ . removeClass ( 'blur' ) ;
2012-03-01 10:19:49 +08:00
setTimeout ( root . selectionChanged ) ; //select textarea after focus
}
2011-11-08 01:15:13 +08:00
else
cursor . show ( ) ;
e . stopPropagation ( ) ;
} ) . blur ( function ( e ) {
cursor . hide ( ) . parent . blur ( ) ;
if ( cursor . selection )
cursor . selection . jQ . addClass ( 'blur' ) ;
e . stopPropagation ( ) ;
} ) ;
2012-03-01 10:19:49 +08:00
jQ . bind ( 'focus.mathquill blur.mathquill' , function ( e ) {
textarea . trigger ( e ) ;
} ) . bind ( 'mousedown.mathquill' , function ( ) {
setTimeout ( focus ) ;
} ) . bind ( 'click.mathquill' , focus ) //stupid Mobile Safari
. blur ( ) ;
function focus ( ) {
textarea . focus ( ) ;
}
//clipboard event handling
jQ . bind ( 'cut' , function ( e ) {
setTextareaSelection ( ) ;
if ( cursor . selection )
setTimeout ( function ( ) { cursor . deleteSelection ( ) ; cursor . redraw ( ) ; } ) ;
e . stopPropagation ( ) ;
} ) . bind ( 'copy' , function ( e ) {
setTextareaSelection ( ) ;
skipTextInput = true ;
e . stopPropagation ( ) ;
} ) . bind ( 'paste' , function ( e ) {
skipTextInput = true ;
setTimeout ( paste ) ;
e . stopPropagation ( ) ;
} ) ;
function paste ( ) {
//FIXME HACK the parser in RootTextBlock needs to be moved to
//Cursor::writeLatex or something so this'll work with MathQuill textboxes
var latex = textarea . val ( ) ;
if ( latex . slice ( 0 , 1 ) === '$' && latex . slice ( - 1 ) === '$' )
latex = latex . slice ( 1 , - 1 ) ;
else if ( ! latex . match ( /^\\/ ) ) // make it text if it doesn't look like latex
latex = '\\text{' + latex + '}' ;
cursor . writeLatex ( latex ) . show ( ) ;
2011-11-08 01:15:13 +08:00
textarea . val ( '' ) ;
}
2012-03-01 10:19:49 +08:00
//keyboard events and text input, see Wiki page "Keyboard Events"
var lastKeydn , lastKeydnHappened , lastKeypressWhich , skipTextInput = false ;
jQ . bind ( 'keydown.mathquill' , function ( e ) {
lastKeydn = e ;
lastKeydnHappened = true ;
if ( cursor . parent . keydown ( e ) === false )
e . preventDefault ( ) ;
} ) . bind ( 'keypress.mathquill' , function ( e ) {
if ( lastKeydnHappened )
lastKeydnHappened = false ;
2011-11-08 01:15:13 +08:00
else {
2012-03-01 10:19:49 +08:00
//there's two ways keypress might be triggered without a keydown happening first:
if ( lastKeypressWhich !== e . which )
//all browsers do that if this textarea is given focus during the keydown of
//a different focusable element, i.e. by that element's keydown event handler.
//No way of knowing original keydown, so ignore this keypress
return ;
else
//some browsers do that when auto-repeating key events, replay the keydown
cursor . parent . keydown ( lastKeydn ) ;
2011-11-08 01:15:13 +08:00
}
2012-03-01 10:19:49 +08:00
lastKeypressWhich = e . which ;
2011-11-08 01:15:13 +08:00
2012-03-01 10:19:49 +08:00
if ( textareaSelectionTimeout !== undefined )
clearTimeout ( textareaSelectionTimeout ) ;
2011-11-08 01:15:13 +08:00
//after keypress event, trigger virtual textInput event if text was
//input to textarea
2012-03-01 10:19:49 +08:00
skipTextInput = false ;
2011-11-08 01:15:13 +08:00
setTimeout ( textInput ) ;
2012-03-01 10:19:49 +08:00
} ) ;
2011-04-15 07:08:16 +08:00
2012-03-01 10:19:49 +08:00
function textInput ( ) {
if ( skipTextInput ) return ;
var text = textarea . val ( ) ;
if ( text ) {
textarea . val ( '' ) ;
for ( var i = 0 ; i < text . length ; i += 1 ) {
cursor . parent . textInput ( text . charAt ( i ) ) ;
}
}
else {
if ( cursor . selection || textareaSelectionTimeout !== undefined )
setTextareaSelection ( ) ;
}
2011-11-08 01:15:13 +08:00
}
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
function addToolbar ( root , jQ ) {
// the button groups include most LatexCmds, de-duped and categorized.
// functions like "log" are excluded, since we have some fu to auto-convert
// them as they are typed (i.e. you can just type "log", don't need the \ )
var button _tabs = [
{ name : I18n . t ( 'tabs.basic' , 'Basic' ) ,
example : '+' ,
button _groups : [
[ "subscript" , "supscript" , "frac" , "sqrt" , "nthroot" , "langle" , "binomial" , "vector" , "f" , "prime" ] ,
[ "+" , "-" , "pm" , "mp" , "cdot" , "=" , "times" , "div" , "ast" ] ,
[ "therefore" , "because" ] ,
[ "sum" , "prod" , "coprod" , "int" ] ,
[ "N" , "P" , "Z" , "Q" , "R" , "C" , "H" ]
] } ,
{ name : I18n . t ( 'tabs.greek' , 'Greek' ) ,
2015-02-28 01:40:08 +08:00
example : 'π' ,
2011-11-08 01:15:13 +08:00
button _groups : [
[ "alpha" , "beta" , "gamma" , "delta" , "epsilon" , "zeta" , "eta" , "theta" , "iota" , "kappa" , "lambda" , "mu" , "nu" , "xi" , "pi" , "rho" , "sigma" , "tau" , "upsilon" , "phi" , "chi" , "psi" , "omega" ] ,
[ "digamma" , "varepsilon" , "vartheta" , "varkappa" , "varpi" , "varrho" , "varsigma" , "varphi" ] ,
[ "Gamma" , "Delta" , "Theta" , "Lambda" , "Xi" , "Pi" , "Sigma" , "Upsilon" , "Phi" , "Psi" , "Omega" ]
] } ,
{ name : I18n . t ( 'tabs.operators' , 'Operators' ) ,
2015-02-28 01:40:08 +08:00
example : '⊕' ,
2011-11-08 01:15:13 +08:00
button _groups : [ [ "wedge" , "vee" , "cup" , "cap" , "diamond" , "bigtriangleup" , "ominus" , "uplus" , "otimes" , "oplus" , "bigtriangledown" , "sqcap" , "triangleleft" , "sqcup" , "triangleright" , "odot" , "bigcirc" , "dagger" , "ddagger" , "wr" , "amalg" ]
] } ,
{ name : I18n . t ( 'tabs.relationships' , 'Relationships' ) ,
2015-02-28 01:40:08 +08:00
example : '≤' ,
2011-11-08 01:15:13 +08:00
button _groups : [ [ "<" , ">" , "equiv" , "cong" , "sim" , "notin" , "ne" , "propto" , "approx" , "le" , "ge" , "in" , "ni" , "notni" , "subset" , "supset" , "notsubset" , "notsupset" , "subseteq" , "supseteq" , "notsubseteq" , "notsupseteq" , "models" , "prec" , "succ" , "preceq" , "succeq" , "simeq" , "mid" , "ll" , "gg" , "parallel" , "bowtie" , "sqsubset" , "sqsupset" , "smile" , "sqsubseteq" , "sqsupseteq" , "doteq" , "frown" , "vdash" , "dashv" , "exists" , "varnothing" ]
] } ,
{ name : I18n . t ( 'tabs.arrows' , 'Arrows' ) ,
2015-02-28 01:40:08 +08:00
example : '⇔' ,
2011-11-08 01:15:13 +08:00
button _groups : [ [ "longleftarrow" , "longrightarrow" , "Longleftarrow" , "Longrightarrow" , "longleftrightarrow" , "updownarrow" , "Longleftrightarrow" , "Updownarrow" , "mapsto" , "nearrow" , "hookleftarrow" , "hookrightarrow" , "searrow" , "leftharpoonup" , "rightharpoonup" , "swarrow" , "leftharpoondown" , "rightharpoondown" , "nwarrow" , "downarrow" , "Downarrow" , "uparrow" , "Uparrow" , "rightarrow" , "Rightarrow" , "leftarrow" , "lArr" , "leftrightarrow" , "Leftrightarrow" ]
] } ,
{ name : I18n . t ( 'tabs.delimiters' , 'Delimiters' ) ,
example : '{' ,
2015-09-09 04:00:52 +08:00
button _groups : [ [ "lfloor" , "rfloor" , "lceil" , "rceil" , "slash" , "lbrace" , "rbrace" ]
2011-11-08 01:15:13 +08:00
] } ,
{ name : I18n . t ( 'tabs.miscellaneous' , 'Misc' ) ,
2015-02-28 01:40:08 +08:00
example : '∞' ,
2011-11-08 01:15:13 +08:00
button _groups : [ [ "forall" , "ldots" , "cdots" , "vdots" , "ddots" , "surd" , "triangle" , "ell" , "top" , "flat" , "natural" , "sharp" , "wp" , "bot" , "clubsuit" , "diamondsuit" , "heartsuit" , "spadesuit" , "caret" , "underscore" , "backslash" , "vert" , "perp" , "nabla" , "hbar" , "AA" , "circ" , "bullet" , "setminus" , "neg" , "dots" , "Re" , "Im" , "partial" , "infty" , "aleph" , "deg" , "angle" ]
] }
] ;
//some html_templates aren't very pretty/useful, so we override them.
var html _template _overrides = {
2013-03-06 03:31:39 +08:00
binomial : '<span style="font-size: 0.48em"><span class="paren" style="font-size: 2.087912087912088em; ">(</span><span class="array"><span><var>n</var></span><span><var>m</var></span></span><span class="paren" style="font-size: 2.087912087912088em; ">)</span></span>' ,
2012-03-01 10:19:49 +08:00
frac : '<span style="font-size: 0.55em; vertical-align: middle" class="fraction"><span class="numerator"><var>n</var></span><span class="denominator"><var>m</var></span><span style="width:0"></span></span>' ,
sqrt : '<span class="block"><span class="sqrt-prefix">√</span><span class="sqrt-stem"> </span></span>' ,
nthroot : '<span style="font-size: 0.7em"><sup class="nthroot"><var>n</var></sup><span class="block"><span class="sqrt-prefix">√</span><span class="sqrt-stem"> </span></span></span>' ,
2011-11-08 01:15:13 +08:00
supscript : '<sup style="font-size: 0.6em">sup</sup>' ,
subscript : '<sub style="font-size: 0.6em; line-height: 3.5;">sub</sub>' ,
2012-03-01 10:19:49 +08:00
vector : '<span class="array" style="vertical-align: middle; font-size: 0.4em; line-height: 0.9em"><span>1</span><span>2</span><span>3</span></span>'
2011-11-08 01:15:13 +08:00
}
var tabs = [ ] ;
var panes = [ ] ;
$ . each ( button _tabs , function ( index , tab ) {
2013-12-19 05:54:01 +08:00
tabs . push (
2014-12-19 17:35:02 +08:00
'<li><a href="#' + htmlEscape ( tab . name ) + '_tab" role="tab" tabindex="-1" aria-controls="' + htmlEscape ( tab . name ) + '_tab">'
+ ' <span class="mathquill-rendered-math">' + htmlEscape ( tab . example ) + '</span>' + htmlEscape ( tab . name ) + '</a></li>'
2013-12-19 05:54:01 +08:00
) ;
2011-11-08 01:15:13 +08:00
var buttons = [ ] ;
$ . each ( tab . button _groups , function ( index , group ) {
$ . each ( group , function ( index , cmd ) {
var obj = new LatexCmds [ cmd ] ( undefined , cmd ) ;
2014-12-19 17:35:02 +08:00
buttons . push ( '<li><a class="mathquill-rendered-math" href="#" title="' + htmlEscape ( cmd . match ( /^[a-z]+$/i ) ? '\\' + cmd : cmd ) + '">' +
$ . raw ( html _template _overrides [ cmd ] ? html _template _overrides [ cmd ] : '<span style="line-height: 1.5em">' + $ . raw ( obj . html _template . join ( '' ) ) + '</span>' ) +
2011-11-08 01:15:13 +08:00
'</a></li>' ) ;
} ) ;
buttons . push ( '<li class="mathquill-button-spacer"></li>' ) ;
2011-04-15 07:08:16 +08:00
} ) ;
2014-12-19 17:35:02 +08:00
panes . push ( '<div class="mathquill-tab-pane" id="' + htmlEscape ( tab . name ) + '_tab" role="tabpanel"><ul>' + $ . raw ( buttons . join ( '' ) ) + '</ul></div>' ) ;
2011-04-15 07:08:16 +08:00
} ) ;
2014-12-19 17:35:02 +08:00
root . toolbar = $ ( '#mathquill-view .mathquill-toolbar' ) . html ( '<ul class="mathquill-tab-bar" role="tablist">' + $ . raw ( tabs . join ( '' ) ) + '</ul><div class="mathquill-toolbar-panes">' + $ . raw ( panes . join ( '' ) ) + '</div>' ) ;
2013-12-19 05:54:01 +08:00
$ ( '#mathquill-view .mathquill-tab-bar li a' ) . click ( function ( e ) {
e . preventDefault ( ) ;
$ ( '#mathquill-view .mathquill-tab-bar li' ) . removeClass ( 'mathquill-tab-selected' )
. find ( 'a' ) . attr ( 'tabindex' , '-1' ) . attr ( 'aria-selected' , 'false' ) ;
2013-03-06 03:31:39 +08:00
$ ( '#mathquill-view .mathquill-tab-pane' ) . removeClass ( 'mathquill-tab-pane-selected' ) ;
2013-12-19 05:54:01 +08:00
$ ( this ) . attr ( 'tabindex' , '0' ) . attr ( 'aria-selected' , 'true' ) . focus ( )
. parent ( ) . addClass ( 'mathquill-tab-selected' ) ;
2011-11-08 01:15:13 +08:00
$ ( this . href . replace ( /.*#/ , '#' ) ) . addClass ( 'mathquill-tab-pane-selected' ) ;
2013-12-19 05:54:01 +08:00
} ) . keydown ( function ( e ) {
var direction , listIndex , $tabLinks ;
switch ( e . keyCode ) {
case 37 :
direction = 'l' ;
break ;
case 39 :
direction = 'r' ;
break ;
default :
return true ;
}
e . preventDefault ( ) ;
$tabLinks = $ ( '#mathquill-view .mathquill-tab-bar li a' ) ;
listIndex = $tabLinks . index ( this ) ;
if ( listIndex === $tabLinks . length - 1 && direction === 'r' ) {
listIndex = - 1 ;
}
( direction === 'r' ) ? listIndex ++ : listIndex -- ;
$ ( $tabLinks . get ( listIndex ) ) . focus ( ) . click ( ) ;
2011-11-08 01:15:13 +08:00
} ) ;
2013-03-06 03:31:39 +08:00
$ ( '#mathquill-view .mathquill-tab-bar li:first-child a' ) . click ( ) ;
$ ( '#mathquill-view a.mathquill-rendered-math' ) . mousedown ( function ( e ) {
2011-11-08 01:15:13 +08:00
e . stopPropagation ( ) ;
} ) . click ( function ( ) {
root . cursor . writeLatex ( this . title , true ) ;
jQ . focus ( ) ;
} ) ;
}
function RootMathBlock ( ) { }
_ = RootMathBlock . prototype = new MathBlock ;
_ . latex = function ( ) {
return MathBlock . prototype . 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 ) {
this . jQ . children ( ) . slice ( 1 ) . remove ( ) ;
this . firstChild = this . lastChild = 0 ;
this . cursor . appendTo ( this ) . writeLatex ( latex ) ;
this . blur ( ) ;
} ;
_ . keydown = function ( e )
{
e . ctrlKey = e . ctrlKey || e . metaKey ;
switch ( ( e . originalEvent && e . originalEvent . keyIdentifier ) || e . which ) {
case 8 : //backspace
case 'Backspace' :
case 'U+0008' :
if ( e . ctrlKey )
while ( this . cursor . prev || this . cursor . selection )
this . cursor . backspace ( ) ;
else
2011-04-15 07:08:16 +08:00
this . cursor . backspace ( ) ;
2011-11-08 01:15:13 +08:00
break ;
case 27 : //may as well be the same as tab until we figure out what to do with it
case 'Esc' :
case 'U+001B' :
case 9 : //tab
case 'Tab' :
case 'U+0009' :
if ( e . ctrlKey ) break ;
var parent = this . cursor . parent ;
if ( e . shiftKey ) { //shift+Tab = go one block left if it exists, else escape left.
2012-03-01 10:19:49 +08:00
if ( parent === this . cursor . root ) //cursor is in root editable, continue default
return this . skipTextInput = true ;
2011-11-08 01:15:13 +08:00
else if ( parent . prev ) //go one block left
this . cursor . appendTo ( parent . prev ) ;
else //get out of the block
this . cursor . insertBefore ( parent . parent ) ;
}
else { //plain Tab = go one block right if it exists, else escape right.
2012-03-01 10:19:49 +08:00
if ( parent === this . cursor . root ) //cursor is in root editable, continue default
2011-11-08 01:15:13 +08:00
return this . skipTextInput = true ;
else if ( parent . next ) //go one block right
this . cursor . prependTo ( parent . next ) ;
else //get out of the block
this . cursor . insertAfter ( parent . parent ) ;
}
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
this . cursor . clearSelection ( ) ;
2012-03-01 10:19:49 +08:00
break ;
2011-11-08 01:15:13 +08:00
case 13 : //enter
case 'Enter' :
break ;
case 35 : //end
case 'End' :
if ( e . shiftKey )
while ( this . cursor . next || ( e . ctrlKey && this . cursor . parent !== this ) )
this . cursor . selectRight ( ) ;
else //move to the end of the root block or the current block.
this . cursor . clearSelection ( ) . appendTo ( e . ctrlKey ? this : this . cursor . parent ) ;
2012-03-01 10:19:49 +08:00
break ;
2011-11-08 01:15:13 +08:00
case 36 : //home
case 'Home' :
if ( e . shiftKey )
while ( this . cursor . prev || ( e . ctrlKey && this . cursor . parent !== this ) )
2011-04-15 07:08:16 +08:00
this . cursor . selectLeft ( ) ;
2011-11-08 01:15:13 +08:00
else //move to the start of the root block or the current block.
this . cursor . clearSelection ( ) . prependTo ( e . ctrlKey ? this : this . cursor . parent ) ;
2012-03-01 10:19:49 +08:00
break ;
2011-11-08 01:15:13 +08:00
case 37 : //left
case 'Left' :
if ( e . ctrlKey ) break ;
if ( e . shiftKey )
2011-04-15 07:08:16 +08:00
this . cursor . selectLeft ( ) ;
else
2011-11-08 01:15:13 +08:00
this . cursor . moveLeft ( ) ;
2012-03-01 10:19:49 +08:00
break ;
2011-11-08 01:15:13 +08:00
case 38 : //up
case 'Up' :
if ( e . ctrlKey ) break ;
if ( e . shiftKey ) {
if ( this . cursor . prev )
while ( this . cursor . prev )
this . cursor . selectLeft ( ) ;
else
this . cursor . selectLeft ( ) ;
}
else if ( this . cursor . parent . prev )
this . cursor . clearSelection ( ) . appendTo ( this . cursor . parent . prev ) ;
else if ( this . cursor . prev )
this . cursor . clearSelection ( ) . prependTo ( this . cursor . parent ) ;
else if ( this . cursor . parent !== this )
this . cursor . clearSelection ( ) . insertBefore ( this . cursor . parent . parent ) ;
2012-03-01 10:19:49 +08:00
break ;
2011-11-08 01:15:13 +08:00
case 39 : //right
case 'Right' :
if ( e . ctrlKey ) break ;
if ( e . shiftKey )
2011-04-15 07:08:16 +08:00
this . cursor . selectRight ( ) ;
2011-11-08 01:15:13 +08:00
else
this . cursor . moveRight ( ) ;
2012-03-01 10:19:49 +08:00
break ;
2011-11-08 01:15:13 +08:00
case 40 : //down
case 'Down' :
if ( e . ctrlKey ) break ;
if ( e . shiftKey ) {
if ( this . cursor . next )
while ( this . cursor . next )
this . cursor . selectRight ( ) ;
else
this . cursor . selectRight ( ) ;
}
else if ( this . cursor . parent . next )
this . cursor . clearSelection ( ) . prependTo ( this . cursor . parent . next ) ;
else if ( this . cursor . next )
this . cursor . clearSelection ( ) . appendTo ( this . cursor . parent ) ;
else if ( this . cursor . parent !== this )
this . cursor . clearSelection ( ) . insertAfter ( this . cursor . parent . parent ) ;
2012-03-01 10:19:49 +08:00
break ;
2011-11-08 01:15:13 +08:00
case 46 : //delete
case 'Del' :
case 'U+007F' :
if ( e . ctrlKey )
while ( this . cursor . next || this . cursor . selection )
this . cursor . deleteForward ( ) ;
else
this . cursor . deleteForward ( ) ;
break ;
case 65 : //the 'A' key, as in Ctrl+A Select All
case 'A' :
case 'U+0041' :
if ( e . ctrlKey && ! e . shiftKey && ! e . altKey ) {
if ( this !== this . cursor . root ) //so not stopPropagation'd at RootMathCommand
return this . parent . keydown ( e ) ;
this . cursor . clearSelection ( ) . appendTo ( this ) ;
while ( this . cursor . prev )
this . cursor . selectLeft ( ) ;
2012-03-01 10:19:49 +08:00
break ;
2011-11-08 01:15:13 +08:00
}
default :
2011-04-15 07:08:16 +08:00
this . skipTextInput = false ;
2012-03-01 10:19:49 +08:00
return true ;
2011-04-15 07:08:16 +08:00
}
2012-03-01 10:19:49 +08:00
this . skipTextInput = true ;
return false ;
2011-11-08 01:15:13 +08:00
} ;
_ . textInput = function ( ch ) {
if ( ! this . skipTextInput )
this . cursor . write ( ch ) ;
} ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
function RootMathCommand ( cursor ) {
2012-03-01 10:19:49 +08:00
this . init ( '$' ) ;
2011-11-08 01:15:13 +08:00
this . firstChild . cursor = cursor ;
this . firstChild . textInput = function ( ch ) {
if ( this . skipTextInput ) return ;
if ( ch !== '$' || cursor . parent !== this )
cursor . write ( ch ) ;
else if ( this . isEmpty ( ) ) {
cursor . insertAfter ( this . parent ) . backspace ( )
. insertNew ( new VanillaSymbol ( '\\$' , '$' ) ) . show ( ) ;
}
else if ( ! cursor . next )
cursor . insertAfter ( this . parent ) ;
else if ( ! cursor . prev )
cursor . insertBefore ( this . parent ) ;
else
cursor . write ( ch ) ;
} ;
}
_ = RootMathCommand . prototype = new MathCommand ;
_ . html _template = [ '<span class="mathquill-rendered-math"></span>' ] ;
_ . initBlocks = function ( ) {
this . firstChild =
this . lastChild =
this . jQ . data ( jQueryDataKey ) . block =
new RootMathBlock ;
this . firstChild . parent = this ;
this . firstChild . jQ = this . jQ ;
} ;
2012-03-01 10:19:49 +08:00
_ . latex = function ( ) {
return '$' + this . firstChild . latex ( ) + '$' ;
} ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
function RootTextBlock ( ) { }
_ = RootTextBlock . prototype = new MathBlock ;
_ . renderLatex = function ( latex ) {
var self = this , cursor = self . cursor ;
self . jQ . children ( ) . slice ( 1 ) . remove ( ) ;
self . firstChild = self . lastChild = 0 ;
cursor . show ( ) . appendTo ( self ) ;
latex = latex . match ( /(?:\\\$|[^$])+|\$(?:\\\$|[^$])*\$|\$(?:\\\$|[^$])*$/g ) || '' ;
for ( var i = 0 ; i < latex . length ; i += 1 ) {
var chunk = latex [ i ] ;
if ( chunk [ 0 ] === '$' ) {
if ( chunk [ - 1 + chunk . length ] === '$' && chunk [ - 2 + chunk . length ] !== '\\' )
chunk = chunk . slice ( 1 , - 1 ) ;
else
chunk = chunk . slice ( 1 ) ;
var root = new RootMathCommand ( cursor ) ;
cursor . insertNew ( root ) ;
root . firstChild . renderLatex ( chunk ) ;
cursor . show ( ) . insertAfter ( root ) ;
}
else {
for ( var j = 0 ; j < chunk . length ; j += 1 )
this . cursor . insertNew ( new VanillaSymbol ( chunk [ j ] ) ) ;
}
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
} ;
_ . keydown = RootMathBlock . prototype . keydown ;
_ . textInput = function ( ch ) {
2011-04-15 07:08:16 +08:00
if ( this . skipTextInput ) return ;
2011-11-08 01:15:13 +08:00
this . cursor . deleteSelection ( ) ;
if ( ch === '$' )
this . cursor . insertNew ( new RootMathCommand ( this . cursor ) ) ;
2011-04-15 07:08:16 +08:00
else
2011-11-08 01:15:13 +08:00
this . cursor . insertNew ( new VanillaSymbol ( ch ) ) ;
} ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Commands and Operators .
* * * * * * * * * * * * * * * * * * * * * * * * * * /
var CharCmds = { } , LatexCmds = { } ; //single character commands, LaTeX commands
2012-03-01 10:19:49 +08:00
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 . addClass ( 'matrixed' ) . css ( {
fontSize : y + 'em' ,
marginTop : '-.1em' ,
filter : 'progid:DXImageTransform.Microsoft'
+ '.Matrix(M11=' + x + ",SizingMethod='auto expand')"
} ) ;
function calculateMarginRight ( ) {
jQ . css ( 'marginRight' , ( 1 + jQ . width ( ) ) * ( 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' ) ;
} ;
}
2011-11-08 01:15:13 +08:00
function proto ( parent , child ) { //shorthand for prototyping
child . prototype = parent . prototype ;
return child ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
2012-03-01 10:19:49 +08:00
function bind ( cons ) { //shorthand for binding arguments to constructor
var args = Array . prototype . slice . call ( arguments , 1 ) ;
return proto ( cons , function ( ) {
cons . apply ( this , Array . prototype . concat . apply ( args , arguments ) ) ;
} ) ;
}
//because I miss the <font> tag
//(that's a joke, I hate this, it's like actively *fighting*
// separation of presentation and content and everything HTML and CSS
// are about, but it's an intrinsic problem with WYSIWYG)
//TODO: WYSIWYM?
function Style ( cmd , html _template , replacedFragment ) {
this . init ( cmd , [ html _template ] , undefined , replacedFragment ) ;
}
proto ( MathCommand , Style ) ;
//fonts
LatexCmds . mathrm = bind ( Style , '\\mathrm' , '<span class="roman font"></span>' ) ;
LatexCmds . mathit = bind ( Style , '\\mathit' , '<i class="font"></i>' ) ;
LatexCmds . mathbf = bind ( Style , '\\mathbf' , '<b class="font"></b>' ) ;
LatexCmds . mathsf = bind ( Style , '\\mathsf' , '<span class="sans-serif font"></span>' ) ;
LatexCmds . mathtt = bind ( Style , '\\mathtt' , '<span class="monospace font"></span>' ) ;
//text-decoration
LatexCmds . underline = bind ( Style , '\\underline' , '<span class="underline"></span>' ) ;
LatexCmds . overline = LatexCmds . bar = bind ( Style , '\\overline' , '<span class="overline"></span>' ) ;
2011-11-08 01:15:13 +08:00
function SupSub ( cmd , html , text , replacedFragment ) {
2012-03-01 10:19:49 +08:00
this . init ( cmd , [ html ] , [ text ] , replacedFragment ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ = SupSub . prototype = new MathCommand ;
_ . latex = function ( ) {
var latex = this . firstChild . latex ( ) ;
if ( latex . length === 1 )
return this . cmd + latex ;
else
return this . cmd + '{' + ( latex || ' ' ) + '}' ;
} ;
_ . redraw = function ( ) {
if ( this . prev )
this . prev . respace ( ) ;
2012-03-01 10:19:49 +08:00
//SupSub::respace recursively calls respace on all the following SupSubs
//so if prev is a SupSub, no need to call respace on this or following nodes
if ( ! ( this . prev instanceof SupSub ) ) {
this . respace ( ) ;
//and if next is a SupSub, then this.respace() will have already called
//this.next.respace()
if ( this . next && ! ( this . next instanceof SupSub ) )
this . next . respace ( ) ;
}
2011-11-08 01:15:13 +08:00
} ;
_ . respace = function ( ) {
if (
this . prev . cmd === '\\int ' || (
2012-03-01 10:19:49 +08:00
this . prev instanceof SupSub && this . prev . cmd != this . cmd
&& this . prev . prev && this . prev . prev . cmd === '\\int '
2011-11-08 01:15:13 +08:00
)
) {
if ( ! this . limit ) {
this . limit = true ;
this . jQ . addClass ( 'limit' ) ;
}
}
else {
if ( this . limit ) {
this . limit = false ;
this . jQ . removeClass ( 'limit' ) ;
}
2011-04-15 07:08:16 +08:00
}
2012-03-01 10:19:49 +08:00
this . respaced = this . prev instanceof SupSub && this . prev . cmd != this . cmd && ! this . prev . respaced ;
if ( this . respaced ) {
var fontSize = + this . jQ . css ( 'fontSize' ) . slice ( 0 , - 2 ) ,
prevWidth = this . prev . jQ . outerWidth ( )
thisWidth = this . jQ . outerWidth ( ) ;
this . jQ . css ( {
left : ( this . limit && this . cmd === '_' ? - . 25 : 0 ) - prevWidth / fontSize + 'em' ,
marginRight : . 1 - min ( thisWidth , prevWidth ) / fontSize + 'em'
//1px extra so it doesn't wrap in retarded browsers (Firefox 2, I think)
} ) ;
2011-11-08 01:15:13 +08:00
}
else if ( this . limit && this . cmd === '_' ) {
2011-04-15 07:08:16 +08:00
this . jQ . css ( {
2011-11-08 01:15:13 +08:00
left : '-.25em' ,
marginRight : ''
2011-04-15 07:08:16 +08:00
} ) ;
}
else {
this . jQ . css ( {
2011-11-08 01:15:13 +08:00
left : '' ,
marginRight : ''
2011-04-15 07:08:16 +08:00
} ) ;
}
2011-11-08 01:15:13 +08:00
2012-03-01 10:19:49 +08:00
if ( this . next instanceof SupSub )
this . next . respace ( ) ;
2011-11-08 01:15:13 +08:00
return this ;
} ;
LatexCmds . subscript = LatexCmds . _ = proto ( SupSub , function ( replacedFragment ) {
SupSub . call ( this , '_' , '<sub></sub>' , '_' , replacedFragment ) ;
} ) ;
LatexCmds . superscript =
LatexCmds . supscript =
LatexCmds [ '^' ] = proto ( SupSub , function ( replacedFragment ) {
SupSub . call ( this , '^' , '<sup></sup>' , '**' , replacedFragment ) ;
} ) ;
function Fraction ( replacedFragment ) {
2012-03-01 10:19:49 +08:00
this . init ( '\\frac' , undefined , undefined , replacedFragment ) ;
this . jQ . append ( '<span style="display:inline-block;width:0"> </span>' ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ = Fraction . prototype = new MathCommand ;
_ . html _template = [
'<span class="fraction"></span>' ,
'<span class="numerator"></span>' ,
'<span class="denominator"></span>'
] ;
_ . text _template = [ '(' , '/' , ')' ] ;
2012-03-01 10:19:49 +08:00
LatexCmds . frac = LatexCmds . dfrac = LatexCmds . cfrac = LatexCmds . fraction = Fraction ;
2011-11-08 01:15:13 +08:00
function LiveFraction ( ) {
Fraction . apply ( this , arguments ) ;
}
_ = LiveFraction . prototype = new Fraction ;
2012-03-01 10:19:49 +08:00
_ . placeCursor = function ( cursor ) { //TODO: better architecture so this can be done more cleanly, highjacking MathCommand::placeCursor doesn't seem like the right place to do this
2011-11-08 01:15:13 +08:00
if ( this . firstChild . isEmpty ( ) ) {
var prev = this . prev ;
while ( prev &&
! (
prev instanceof BinaryOperator ||
prev instanceof TextBlock ||
prev instanceof BigSymbol
) //lookbehind for operator
)
prev = prev . prev ;
if ( prev instanceof BigSymbol && prev . next instanceof SupSub ) {
prev = prev . next ;
if ( prev . next instanceof SupSub && prev . next . cmd != prev . cmd )
prev = prev . next ;
}
2012-03-01 10:19:49 +08:00
if ( prev !== this . prev ) { //FIXME: major Law of Demeter violation, shouldn't know here that MathCommand::initBlocks does some initialization that MathFragment::blockify doesn't
2011-11-08 01:15:13 +08:00
var newBlock = new MathFragment ( this . parent , prev , this ) . blockify ( ) ;
newBlock . jQ = this . firstChild . jQ . empty ( ) . removeClass ( 'empty' ) . append ( newBlock . jQ ) . data ( jQueryDataKey , { block : newBlock } ) ;
newBlock . next = this . lastChild ;
newBlock . parent = this ;
this . firstChild = this . lastChild . prev = newBlock ;
}
}
cursor . appendTo ( this . lastChild ) ;
} ;
2012-03-01 10:19:49 +08:00
LatexCmds . over = CharCmds [ '/' ] = LiveFraction ;
2011-11-08 01:15:13 +08:00
function SquareRoot ( replacedFragment ) {
2012-03-01 10:19:49 +08:00
this . init ( '\\sqrt' , undefined , undefined , replacedFragment ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ = SquareRoot . prototype = new MathCommand ;
_ . html _template = [
2012-03-01 10:19:49 +08:00
'<span class="block"><span class="sqrt-prefix">√</span></span>' ,
2011-11-08 01:15:13 +08:00
'<span class="sqrt-stem"></span>'
] ;
_ . text _template = [ 'sqrt(' , ')' ] ;
_ . redraw = function ( ) {
2012-03-01 10:19:49 +08:00
var block = this . lastChild . jQ ;
scale ( block . prev ( ) , 1 , block . innerHeight ( ) / + block . css ( 'fontSize' ) . slice ( 0 , - 2 ) - . 1 ) ;
2011-11-08 01:15:13 +08:00
} ;
_ . optional _arg _command = 'nthroot' ;
LatexCmds . sqrt = LatexCmds [ '√' ] = SquareRoot ;
function NthRoot ( replacedFragment ) {
SquareRoot . call ( this , replacedFragment ) ;
this . jQ = this . firstChild . jQ . detach ( ) . add ( this . jQ ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ = NthRoot . prototype = new SquareRoot ;
_ . html _template = [
2012-03-01 10:19:49 +08:00
'<span class="block"><span class="sqrt-prefix">√</span></span>' ,
2011-11-08 01:15:13 +08:00
'<sup class="nthroot"></sup>' ,
'<span class="sqrt-stem"></span>'
] ;
_ . text _template = [ 'sqrt[' , '](' , ')' ] ;
_ . latex = function ( ) {
return '\\sqrt[' + this . firstChild . latex ( ) + ']{' + this . lastChild . latex ( ) + '}' ;
} ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . nthroot = NthRoot ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
// Round/Square/Curly/Angle Brackets (aka Parens/Brackets/Braces)
function Bracket ( open , close , cmd , end , replacedFragment ) {
2012-03-01 10:19:49 +08:00
this . init ( '\\left' + cmd ,
2014-12-19 17:35:02 +08:00
[ '<span class="block"><span class="paren">' + htmlEscape ( open ) + '</span><span class="block"></span><span class="paren">' + htmlEscape ( close ) + '</span></span>' ] ,
2011-11-08 01:15:13 +08:00
[ open , close ] ,
replacedFragment ) ;
2012-03-01 10:19:49 +08:00
this . end = '\\right' + end ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ = Bracket . prototype = new MathCommand ;
2012-03-01 10:19:49 +08:00
_ . initBlocks = function ( replacedFragment ) { //FIXME: possible Law of Demeter violation, hardcore MathCommand::initBlocks knowledge needed here
2011-11-08 01:15:13 +08:00
this . firstChild = this . lastChild =
( replacedFragment && replacedFragment . blockify ( ) ) || new MathBlock ;
this . firstChild . parent = this ;
this . firstChild . jQ = this . jQ . children ( ':eq(1)' )
. data ( jQueryDataKey , { block : this . firstChild } )
. append ( this . firstChild . jQ ) ;
2012-03-01 10:19:49 +08:00
var block = this . blockjQ = this . firstChild . jQ ;
this . bracketjQs = block . prev ( ) . add ( block . next ( ) ) ;
2011-11-08 01:15:13 +08:00
} ;
_ . latex = function ( ) {
return this . cmd + this . firstChild . latex ( ) + this . end ;
} ;
_ . redraw = function ( ) {
2012-03-01 10:19:49 +08:00
var height = this . blockjQ . outerHeight ( ) / + this . blockjQ . css ( 'fontSize' ) . slice ( 0 , - 2 ) ;
scale ( this . bracketjQs , min ( 1 + . 2 * ( height - 1 ) , 1.2 ) , 1.05 * height ) ;
2011-11-08 01:15:13 +08:00
} ;
2015-09-09 04:00:52 +08:00
CharCmds [ '{' ] = proto ( Bracket , function ( replacedFragment ) {
2011-11-08 01:15:13 +08:00
Bracket . call ( this , '{' , '}' , '\\{' , '\\}' , replacedFragment ) ;
2011-04-15 07:08:16 +08:00
} ) ;
2011-11-08 01:15:13 +08:00
LatexCmds . langle = LatexCmds . lang = proto ( Bracket , function ( replacedFragment ) {
2012-03-01 10:19:49 +08:00
Bracket . call ( this , '⟨' , '⟩' , '\\langle ' , '\\rangle ' , replacedFragment ) ;
2011-11-08 01:15:13 +08:00
} ) ;
// Closing bracket matching opening bracket above
function CloseBracket ( open , close , cmd , end , replacedFragment ) {
Bracket . apply ( this , arguments ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ = CloseBracket . prototype = new Bracket ;
_ . placeCursor = function ( cursor ) {
//if I'm at the end of my parent who is a matching open-paren, and I was not passed
// a selection fragment, get rid of me and put cursor after my parent
if ( ! this . next && this . parent . parent && this . parent . parent . end === this . end && this . firstChild . isEmpty ( ) )
cursor . backspace ( ) . insertAfter ( this . parent . parent ) ;
2012-03-01 10:19:49 +08:00
else {
2011-11-08 01:15:13 +08:00
this . firstChild . blur ( ) ;
2012-03-01 10:19:49 +08:00
this . redraw ( ) ;
}
2011-11-08 01:15:13 +08:00
} ;
2015-09-09 04:00:52 +08:00
CharCmds [ '}' ] = proto ( CloseBracket , function ( replacedFragment ) {
2011-11-08 01:15:13 +08:00
CloseBracket . call ( this , '{' , '}' , '\\{' , '\\}' , replacedFragment ) ;
} ) ;
LatexCmds . rangle = LatexCmds . rang = proto ( CloseBracket , function ( replacedFragment ) {
2012-03-01 10:19:49 +08:00
CloseBracket . call ( this , '⟨' , '⟩' , '\\langle ' , '\\rangle ' , replacedFragment ) ;
2011-11-08 01:15:13 +08:00
} ) ;
function Paren ( open , close , replacedFragment ) {
Bracket . call ( this , open , close , open , close , replacedFragment ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
Paren . prototype = Bracket . prototype ;
LatexCmds . lparen = CharCmds [ '(' ] = proto ( Paren , function ( replacedFragment ) {
Paren . call ( this , '(' , ')' , replacedFragment ) ;
} ) ;
2012-03-01 10:19:49 +08:00
LatexCmds . lbrack = LatexCmds . lbracket = CharCmds [ '[' ] = proto ( Paren , function ( replacedFragment ) {
Paren . call ( this , '[' , ']' , replacedFragment ) ;
} ) ;
2011-11-08 01:15:13 +08:00
function CloseParen ( open , close , replacedFragment ) {
CloseBracket . call ( this , open , close , open , close , replacedFragment ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
CloseParen . prototype = CloseBracket . prototype ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . rparen = CharCmds [ ')' ] = proto ( CloseParen , function ( replacedFragment ) {
CloseParen . call ( this , '(' , ')' , replacedFragment ) ;
} ) ;
2012-03-01 10:19:49 +08:00
LatexCmds . rbrack = LatexCmds . rbracket = CharCmds [ ']' ] = proto ( CloseParen , function ( replacedFragment ) {
CloseParen . call ( this , '[' , ']' , replacedFragment ) ;
} ) ;
2011-11-08 01:15:13 +08:00
function Pipes ( replacedFragment ) {
Paren . call ( this , '|' , '|' , replacedFragment ) ;
}
_ = Pipes . prototype = new Paren ;
_ . placeCursor = function ( cursor ) {
if ( ! this . next && this . parent . parent && this . parent . parent . end === this . end && this . firstChild . isEmpty ( ) )
cursor . backspace ( ) . insertAfter ( this . parent . parent ) ;
2011-04-15 07:08:16 +08:00
else
2011-11-08 01:15:13 +08:00
cursor . appendTo ( this . firstChild ) ;
} ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . lpipe = LatexCmds . rpipe = CharCmds [ '|' ] = Pipes ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
function TextBlock ( replacedText ) {
if ( replacedText instanceof MathFragment )
this . replacedText = replacedText . remove ( ) . jQ . text ( ) ;
else if ( typeof replacedText === 'string' )
this . replacedText = replacedText ;
2011-04-15 07:08:16 +08:00
2012-03-01 10:19:49 +08:00
this . init ( ) ;
2011-11-08 01:15:13 +08:00
}
_ = TextBlock . prototype = new MathCommand ;
2012-03-01 10:19:49 +08:00
_ . cmd = '\\text' ;
2011-11-08 01:15:13 +08:00
_ . html _template = [ '<span class="text"></span>' ] ;
_ . text _template = [ '"' , '"' ] ;
2012-03-01 10:19:49 +08:00
_ . initBlocks = function ( ) { //FIXME: another possible Law of Demeter violation, but this seems much cleaner, like it was supposed to be done this way
2011-11-08 01:15:13 +08:00
this . firstChild =
this . lastChild =
this . jQ . data ( jQueryDataKey ) . block = new InnerTextBlock ;
this . firstChild . parent = this ;
this . firstChild . jQ = this . jQ . append ( this . firstChild . jQ ) ;
} ;
2012-03-01 10:19:49 +08:00
_ . placeCursor = function ( cursor ) { //TODO: this should be done in the constructor that's passed replacedFragment, but you need the cursor to create new characters and insert them
2011-11-08 01:15:13 +08:00
( this . cursor = cursor ) . appendTo ( this . firstChild ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
if ( this . replacedText )
for ( var i = 0 ; i < this . replacedText . length ; i += 1 )
this . write ( this . replacedText . charAt ( i ) ) ;
} ;
_ . write = function ( ch ) {
this . cursor . insertNew ( new VanillaSymbol ( ch ) ) ;
} ;
_ . keydown = function ( e ) {
//backspace and delete and ends of block don't unwrap
if ( ! this . cursor . selection &&
(
( e . which === 8 && ! this . cursor . prev ) ||
( e . which === 46 && ! this . cursor . next )
)
) {
if ( this . isEmpty ( ) )
this . cursor . insertAfter ( this ) ;
return false ;
}
return this . parent . keydown ( e ) ;
} ;
_ . textInput = function ( ch ) {
this . cursor . deleteSelection ( ) ;
if ( ch !== '$' )
this . write ( ch ) ;
else if ( this . isEmpty ( ) )
this . cursor . insertAfter ( this ) . backspace ( ) . insertNew ( new VanillaSymbol ( '\\$' , '$' ) ) ;
else if ( ! this . cursor . next )
this . cursor . insertAfter ( this ) ;
else if ( ! this . cursor . prev )
this . cursor . insertBefore ( this ) ;
else { //split apart
var next = new TextBlock ( new MathFragment ( this . firstChild , this . cursor . prev ) ) ;
2012-03-01 10:19:49 +08:00
next . placeCursor = function ( cursor ) { //FIXME HACK: pretend no prev so they don't get merged
2011-11-08 01:15:13 +08:00
this . prev = 0 ;
delete this . placeCursor ;
this . placeCursor ( cursor ) ;
} ;
next . firstChild . focus = function ( ) { return this ; } ;
this . cursor . insertAfter ( this ) . insertNew ( next ) ;
next . prev = this ;
this . cursor . insertBefore ( next ) ;
delete next . firstChild . focus ;
}
} ;
function InnerTextBlock ( ) { }
_ = InnerTextBlock . prototype = new MathBlock ;
_ . 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 . next === textblock )
cursor . next = textblock . next ;
else if ( cursor . prev === textblock )
cursor . prev = textblock . prev ;
cursor . show ( ) . redraw ( ) ;
}
}
return this ;
} ;
_ . focus = function ( ) {
MathBlock . prototype . focus . call ( this ) ;
var textblock = this . parent ;
2012-03-01 10:19:49 +08:00
if ( textblock . next . cmd === textblock . cmd ) { //TODO: seems like there should be a better way to move MathElements around
2011-11-08 01:15:13 +08:00
var innerblock = this ,
cursor = textblock . cursor ,
next = textblock . next . firstChild ;
next . eachChild ( function ( child ) {
child . parent = innerblock ;
child . jQ . appendTo ( innerblock . jQ ) ;
} ) ;
if ( this . lastChild )
this . lastChild . next = next . firstChild ;
else
this . firstChild = next . firstChild ;
next . firstChild . prev = this . lastChild ;
this . lastChild = next . lastChild ;
next . parent . remove ( ) ;
if ( cursor . prev )
cursor . insertAfter ( cursor . prev ) ;
else
cursor . prependTo ( this ) ;
cursor . redraw ( ) ;
}
2012-03-01 10:19:49 +08:00
else if ( textblock . prev . cmd === textblock . cmd ) {
2011-11-08 01:15:13 +08:00
var cursor = textblock . cursor ;
if ( cursor . prev )
textblock . prev . firstChild . focus ( ) ;
else
cursor . appendTo ( textblock . prev . firstChild ) ;
}
return this ;
} ;
2012-03-01 10:19:49 +08:00
CharCmds . $ =
LatexCmds . text =
LatexCmds . textnormal =
LatexCmds . textrm =
LatexCmds . textup =
LatexCmds . textmd =
TextBlock ;
function makeTextBlock ( latex , html ) {
function SomeTextBlock ( ) {
TextBlock . apply ( this , arguments ) ;
}
_ = SomeTextBlock . prototype = new TextBlock ;
_ . cmd = latex ;
_ . html _template = [ html ] ;
return SomeTextBlock ;
}
LatexCmds . em = LatexCmds . italic = LatexCmds . italics =
LatexCmds . emph = LatexCmds . textit = LatexCmds . textsl =
makeTextBlock ( '\\textit' , '<i class="text"></i>' ) ;
LatexCmds . strong = LatexCmds . bold = LatexCmds . textbf =
makeTextBlock ( '\\textbf' , '<b class="text"></b>' ) ;
LatexCmds . sf = LatexCmds . textsf =
makeTextBlock ( '\\textsf' , '<span class="sans-serif text"></span>' ) ;
LatexCmds . tt = LatexCmds . texttt =
makeTextBlock ( '\\texttt' , '<span class="monospace text"></span>' ) ;
LatexCmds . textsc =
makeTextBlock ( '\\textsc' , '<span style="font-variant:small-caps" class="text"></span>' ) ;
LatexCmds . uppercase =
makeTextBlock ( '\\uppercase' , '<span style="text-transform:uppercase" class="text"></span>' ) ;
LatexCmds . lowercase =
makeTextBlock ( '\\lowercase' , '<span style="text-transform:lowercase" class="text"></span>' ) ;
2011-11-08 01:15:13 +08:00
// input box to type a variety of LaTeX commands beginning with a backslash
function LatexCommandInput ( replacedFragment ) {
2012-03-01 10:19:49 +08:00
this . init ( '\\' ) ;
2011-11-08 01:15:13 +08:00
if ( replacedFragment ) {
this . replacedFragment = replacedFragment . detach ( ) ;
this . isEmpty = function ( ) { return false ; } ;
}
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ = LatexCommandInput . prototype = new MathCommand ;
2012-03-01 10:19:49 +08:00
_ . html _template = [ '<span class="latex-command-input">\\</span>' ] ;
2011-11-08 01:15:13 +08:00
_ . text _template = [ '\\' ] ;
2012-03-01 10:19:49 +08:00
_ . placeCursor = function ( cursor ) { //TODO: better architecture, better place for this to be done, and more cleanly
2011-11-08 01:15:13 +08:00
this . cursor = cursor . appendTo ( this . firstChild ) ;
if ( this . replacedFragment )
this . jQ =
this . jQ . add ( this . replacedFragment . jQ . addClass ( 'blur' ) . bind (
2012-03-01 10:19:49 +08:00
'mousedown mousemove' , //FIXME: is monkey-patching the mousedown and mousemove handlers the right way to do this?
2011-11-08 01:15:13 +08:00
function ( e ) {
$ ( e . target = this . nextSibling ) . trigger ( e ) ;
return false ;
}
) . insertBefore ( this . jQ ) ) ;
} ;
_ . latex = function ( ) {
return '\\' + this . firstChild . latex ( ) + ' ' ;
} ;
_ . keydown = function ( e ) {
if ( e . which === 9 || e . which === 13 ) { //tab or enter
this . renderCommand ( ) ;
return false ;
}
return this . parent . keydown ( e ) ;
} ;
_ . textInput = function ( ch ) {
2012-03-01 10:19:49 +08:00
if ( ch . match ( /[a-z:]/i ) ) {
2011-11-08 01:15:13 +08:00
this . cursor . deleteSelection ( ) ;
this . cursor . insertNew ( new VanillaSymbol ( ch ) ) ;
return ;
}
this . renderCommand ( ) ;
if ( ch === ' ' || ( ch === '\\' && this . firstChild . isEmpty ( ) ) )
return ;
this . cursor . parent . textInput ( ch ) ;
} ;
_ . renderCommand = function ( ) {
this . jQ = this . jQ . last ( ) ;
this . remove ( ) ;
if ( this . next )
this . cursor . insertBefore ( this . next ) ;
else
this . cursor . appendTo ( this . parent ) ;
2012-03-01 10:19:49 +08:00
var latex = this . firstChild . latex ( ) ;
if ( latex )
this . cursor . insertCmd ( latex , this . replacedFragment ) ;
else {
var cmd = new VanillaSymbol ( '\\backslash ' , '\\' ) ;
this . cursor . insertNew ( cmd ) ;
if ( this . replacedFragment )
this . replacedFragment . remove ( ) ;
2011-11-08 01:15:13 +08:00
}
} ;
CharCmds [ '\\' ] = LatexCommandInput ;
function Binomial ( replacedFragment ) {
2012-03-01 10:19:49 +08:00
this . init ( '\\binom' , undefined , undefined , replacedFragment ) ;
this . jQ . wrapInner ( '<span class="array"></span>' ) ;
this . blockjQ = this . jQ . children ( ) ;
this . bracketjQs =
$ ( '<span class="paren">(</span>' ) . prependTo ( this . jQ )
. add ( $ ( '<span class="paren">)</span>' ) . appendTo ( this . jQ ) ) ;
2011-11-08 01:15:13 +08:00
}
_ = Binomial . prototype = new MathCommand ;
_ . html _template =
2012-03-01 10:19:49 +08:00
[ '<span class="block"></span>' , '<span></span>' , '<span></span>' ] ;
2011-11-08 01:15:13 +08:00
_ . text _template = [ 'choose(' , ',' , ')' ] ;
2012-03-01 10:19:49 +08:00
_ . redraw = Bracket . prototype . redraw ;
2011-11-08 01:15:13 +08:00
LatexCmds . binom = LatexCmds . binomial = Binomial ;
function Choose ( ) {
Binomial . apply ( this , arguments ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ = Choose . prototype = new Binomial ;
_ . placeCursor = LiveFraction . prototype . placeCursor ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . choose = Choose ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
function Vector ( replacedFragment ) {
2012-03-01 10:19:49 +08:00
this . init ( '\\vector' , undefined , undefined , replacedFragment ) ;
2011-11-08 01:15:13 +08:00
}
_ = Vector . prototype = new MathCommand ;
_ . html _template = [ '<span class="array"></span>' , '<span></span>' ] ;
_ . latex = function ( ) {
return '\\begin{matrix}' + this . foldChildren ( [ ] , function ( latex , child ) {
latex . push ( child . latex ( ) ) ;
return latex ;
} ) . join ( '\\\\' ) + '\\end{matrix}' ;
} ;
_ . text = function ( ) {
2012-03-01 10:19:49 +08:00
return '[' + this . foldChildren ( [ ] , function ( text , child ) {
2011-11-08 01:15:13 +08:00
text . push ( child . text ( ) ) ;
return text ;
} ) . join ( ) + ']' ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ . placeCursor = function ( cursor ) {
this . cursor = cursor . appendTo ( this . firstChild ) ;
} ;
_ . keydown = function ( e ) {
var currentBlock = this . cursor . parent ;
if ( currentBlock . parent === this ) {
if ( e . which === 13 ) { //enter
var newBlock = new MathBlock ;
newBlock . parent = this ;
newBlock . jQ = $ ( '<span></span>' )
. data ( jQueryDataKey , { block : newBlock } )
. insertAfter ( currentBlock . jQ ) ;
if ( currentBlock . next )
currentBlock . next . prev = newBlock ;
else
this . lastChild = newBlock ;
newBlock . next = currentBlock . next ;
currentBlock . next = newBlock ;
newBlock . prev = currentBlock ;
this . cursor . appendTo ( newBlock ) . redraw ( ) ;
return false ;
}
else if ( e . which === 9 && ! e . shiftKey && ! currentBlock . next ) { //tab
if ( currentBlock . isEmpty ( ) ) {
if ( currentBlock . prev ) {
this . cursor . insertAfter ( this ) ;
delete currentBlock . prev . next ;
this . lastChild = currentBlock . prev ;
currentBlock . jQ . remove ( ) ;
this . cursor . redraw ( ) ;
return false ;
}
else
return this . parent . keydown ( e ) ;
}
var newBlock = new MathBlock ;
newBlock . parent = this ;
newBlock . jQ = $ ( '<span></span>' ) . data ( jQueryDataKey , { block : newBlock } ) . appendTo ( this . jQ ) ;
this . lastChild = newBlock ;
currentBlock . next = newBlock ;
newBlock . prev = currentBlock ;
this . cursor . appendTo ( newBlock ) . redraw ( ) ;
return false ;
}
else if ( e . which === 8 ) { //backspace
if ( currentBlock . isEmpty ( ) ) {
if ( currentBlock . prev ) {
this . cursor . appendTo ( currentBlock . prev )
currentBlock . prev . next = currentBlock . next ;
}
else {
this . cursor . insertBefore ( this ) ;
this . firstChild = currentBlock . next ;
}
if ( currentBlock . next )
currentBlock . next . prev = currentBlock . prev ;
else
this . lastChild = currentBlock . prev ;
currentBlock . jQ . remove ( ) ;
if ( this . isEmpty ( ) )
this . cursor . deleteForward ( ) ;
else
this . cursor . redraw ( ) ;
2011-04-15 07:08:16 +08:00
return false ;
}
2011-11-08 01:15:13 +08:00
else if ( ! this . cursor . prev )
return false ;
}
}
return this . parent . keydown ( e ) ;
} ;
LatexCmds . vector = Vector ;
LatexCmds . editable = proto ( RootMathCommand , function ( ) {
2012-03-01 10:19:49 +08:00
this . init ( '\\editable' ) ;
2011-11-08 01:15:13 +08:00
createRoot ( this . jQ , this . firstChild , false , true ) ;
var cursor ;
this . placeCursor = function ( c ) { cursor = c . appendTo ( this . firstChild ) ; } ;
this . firstChild . blur = function ( ) {
if ( cursor . prev !== this . parent ) return ; //when cursor is inserted after editable, append own cursor FIXME HACK
delete this . blur ;
this . cursor . appendTo ( this ) ;
MathBlock . prototype . blur . call ( this ) ;
} ;
2012-03-01 10:19:49 +08:00
this . latex = function ( ) { return this . firstChild . latex ( ) ; } ;
2011-11-08 01:15:13 +08:00
this . text = function ( ) { return this . firstChild . text ( ) ; } ;
} ) ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Symbols and Special Characters
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2012-03-01 10:19:49 +08:00
LatexCmds . f = bind ( Symbol , 'f' , '<var class="florin">ƒ</var><span style="display:inline-block;width:0"> </span>' ) ;
2011-11-08 01:15:13 +08:00
function Variable ( ch , html ) {
2014-12-19 17:35:02 +08:00
Symbol . call ( this , ch , '<var>' + $ . raw ( html || htmlEscape ( ch ) ) + '</var>' ) ;
2011-11-08 01:15:13 +08:00
}
_ = Variable . prototype = new Symbol ;
_ . text = function ( ) {
var text = this . cmd ;
if ( this . prev && ! ( this . prev instanceof Variable )
&& ! ( this . prev instanceof BinaryOperator ) )
text = '*' + text ;
if ( this . next && ! ( this . next instanceof BinaryOperator )
&& ! ( this . next . cmd === '^' ) )
text += '*' ;
return text ;
} ;
function VanillaSymbol ( ch , html ) {
2014-12-19 17:35:02 +08:00
Symbol . call ( this , ch , '<span>' + $ . raw ( html || htmlEscape ( ch ) ) + '</span>' ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
VanillaSymbol . prototype = Symbol . prototype ;
LatexCmds [ ':' ] = CharCmds [ ' ' ] = bind ( VanillaSymbol , '\\:' , ' ' ) ;
LatexCmds . prime = CharCmds [ "'" ] = bind ( VanillaSymbol , "'" , '′' ) ;
function NonSymbolaSymbol ( ch , html ) { //does not use Symbola font
2014-12-19 17:35:02 +08:00
Symbol . call ( this , ch , '<span class="nonSymbola">' + $ . raw ( html || htmlEscape ( ch ) ) + '</span>' ) ;
2011-11-08 01:15:13 +08:00
}
NonSymbolaSymbol . prototype = Symbol . prototype ;
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 = proto ( Symbol , function ( replacedFragment , latex ) {
Variable . 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 ' , 'ε' ) ;
2012-03-01 10:19:49 +08:00
LatexCmds . piv = //W3C/Unicode and Elsevier and 9573-13
LatexCmds . varpi = //AMS and LaTeX
bind ( Variable , '\\varpi ' , 'ϖ' ) ;
2011-11-08 01:15:13 +08:00
LatexCmds . sigmaf = //W3C/Unicode
LatexCmds . sigmav = //Elsevier
LatexCmds . varsigma = //LaTeX
bind ( Variable , '\\varsigma ' , 'ς' ) ;
2012-03-01 10:19:49 +08:00
LatexCmds . thetav = //Elsevier and 9573-13
LatexCmds . vartheta = //AMS and LaTeX
LatexCmds . thetasym = //W3C/Unicode
bind ( Variable , '\\vartheta ' , 'ϑ' ) ;
2011-11-08 01:15:13 +08:00
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
2012-03-01 10:19:49 +08:00
LatexCmds . Upsilon = //LaTeX
2011-11-08 01:15:13 +08:00
LatexCmds . Upsi = //Elsevier and 9573-13
2012-03-01 10:19:49 +08:00
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 :(
2011-11-08 01:15:13 +08:00
LatexCmds . Gamma =
LatexCmds . Delta =
LatexCmds . Theta =
LatexCmds . Lambda =
LatexCmds . Xi =
LatexCmds . Pi =
LatexCmds . Sigma =
LatexCmds . Phi =
LatexCmds . Psi =
LatexCmds . Omega =
//other symbols with the same LaTeX command and HTML character entity reference
LatexCmds . forall = proto ( Symbol , function ( replacedFragment , latex ) {
VanillaSymbol . call ( this , '\\' + latex + ' ' , '&' + latex + ';' ) ;
} ) ;
function BinaryOperator ( cmd , html , text ) {
Symbol . call ( this , cmd , '<span class="binary-operator">' + html + '</span>' , text ) ;
}
BinaryOperator . prototype = new Symbol ; //so instanceof will work
function PlusMinus ( cmd , html ) {
VanillaSymbol . apply ( this , arguments ) ;
}
_ = PlusMinus . prototype = new BinaryOperator ; //so instanceof will work
_ . respace = function ( ) {
if ( ! this . prev ) {
this . jQ [ 0 ] . className = '' ;
}
else if (
this . prev instanceof BinaryOperator &&
this . next && ! ( this . next instanceof BinaryOperator )
) {
this . jQ [ 0 ] . className = 'unary-operator' ;
}
2011-04-15 07:08:16 +08:00
else {
2011-11-08 01:15:13 +08:00
this . jQ [ 0 ] . className = 'binary-operator' ;
}
return this ;
} ;
2012-03-01 10:19:49 +08:00
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 =
2011-11-08 01:15:13 +08:00
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 = proto ( BinaryOperator , function ( replacedFragment , latex ) {
BinaryOperator . call ( this , '\\' + latex + ' ' , '&' + latex + ';' ) ;
} ) ;
LatexCmds . times = proto ( BinaryOperator , function ( replacedFragment , latex ) {
BinaryOperator . call ( this , '\\times ' , '×' , '[x]' )
} ) ;
2012-03-01 10:19:49 +08:00
LatexCmds [ '÷' ] = LatexCmds . div = LatexCmds . divide = LatexCmds . divides =
2011-11-08 01:15:13 +08:00
bind ( BinaryOperator , '\\div ' , '÷' , '[/]' ) ;
2012-03-01 10:19:49 +08:00
LatexCmds [ '≠' ] = LatexCmds . ne = LatexCmds . neq = bind ( BinaryOperator , '\\ne ' , '≠' ) ;
2011-11-08 01:15:13 +08:00
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 ' , '∝' ) ;
2012-03-01 10:19:49 +08:00
LatexCmds [ '≈' ] = LatexCmds . asymp = LatexCmds . approx = bind ( BinaryOperator , '\\approx ' , '≈' ) ;
2011-11-08 01:15:13 +08:00
LatexCmds . lt = bind ( BinaryOperator , '<' , '<' ) ;
LatexCmds . gt = bind ( BinaryOperator , '>' , '>' ) ;
2012-03-01 10:19:49 +08:00
LatexCmds [ '≤' ] = LatexCmds . le = LatexCmds . leq = bind ( BinaryOperator , '\\le ' , '≤' ) ;
2011-11-08 01:15:13 +08:00
2012-03-01 10:19:49 +08:00
LatexCmds [ '≥' ] = LatexCmds . ge = LatexCmds . geq = bind ( BinaryOperator , '\\ge ' , '≥' ) ;
2011-11-08 01:15:13 +08:00
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 ' , '⊂' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . sup = LatexCmds . supset = LatexCmds . superset =
bind ( BinaryOperator , '\\supset ' , '⊃' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . nsub = LatexCmds . notsub =
LatexCmds . nsubset = LatexCmds . notsubset =
bind ( BinaryOperator , '\\not\\subset ' , '⊄' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . nsup = LatexCmds . notsup =
LatexCmds . nsupset = LatexCmds . notsupset =
LatexCmds . nsuperset = LatexCmds . notsuperset =
bind ( BinaryOperator , '\\not\\supset ' , '⊅' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . sube = LatexCmds . subeq = LatexCmds . subsete = LatexCmds . subseteq =
bind ( BinaryOperator , '\\subseteq ' , '⊆' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . supe = LatexCmds . supeq =
LatexCmds . supsete = LatexCmds . supseteq =
LatexCmds . supersete = LatexCmds . superseteq =
bind ( BinaryOperator , '\\supseteq ' , '⊇' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
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
function BigSymbol ( ch , html ) {
Symbol . call ( this , ch , '<big>' + html + '</big>' ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
BigSymbol . prototype = new Symbol ; //so instanceof will work
2012-03-01 10:19:49 +08:00
LatexCmds [ '∑' ] = LatexCmds . sum = LatexCmds . summation = bind ( BigSymbol , '\\sum ' , '∑' ) ;
LatexCmds [ '∏' ] = LatexCmds . prod = LatexCmds . product = bind ( BigSymbol , '\\prod ' , '∏' ) ;
2011-11-08 01:15:13 +08:00
LatexCmds . coprod = LatexCmds . coproduct = bind ( BigSymbol , '\\coprod ' , '∐' ) ;
2012-05-09 05:02:43 +08:00
LatexCmds [ '∫' ] = LatexCmds [ 'int' ] = LatexCmds . integral = bind ( BigSymbol , '\\int ' , '∫' ) ;
2011-11-08 01:15:13 +08:00
//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 ' , ' ' ) ;
2012-03-01 10:19:49 +08:00
/ * s p a c i n g s p e c i a l c h a r a c t e r s , g o n n a h a v e t o i m p l e m e n t t h i s i n L a t e x C o m m a n d I n p u t : : t e x t I n p u t s o m e h o w
2011-11-08 01:15:13 +08:00
case ',' :
return new VanillaSymbol ( '\\, ' , ' ' ) ;
case ':' :
return new VanillaSymbol ( '\\: ' , ' ' ) ;
case ';' :
return new VanillaSymbol ( '\\; ' , ' ' ) ;
case '!' :
return new Symbol ( '\\! ' , '<span style="margin-right:-.2em"></span>' ) ;
* /
//binary operators
2012-03-01 10:19:49 +08:00
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 ' , '∐' ) ;
2011-11-08 01:15:13 +08:00
//relationship symbols
2012-03-01 10:19:49 +08:00
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 ' , '⊣' ) ;
2011-11-08 01:15:13 +08:00
//arrows
2012-03-01 10:19:49 +08:00
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 ' , '↖' ) ;
2011-11-08 01:15:13 +08:00
//Misc
2012-03-01 10:19:49 +08:00
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 ' , '♠' ) ;
2011-11-08 01:15:13 +08:00
//variable-sized
2012-03-01 10:19:49 +08:00
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 ' , '⊎' ) ;
2011-11-08 01:15:13 +08:00
//delimiters
2012-03-01 10:19:49 +08:00
LatexCmds . lfloor = bind ( VanillaSymbol , '\\lfloor ' , '⌊' ) ;
LatexCmds . rfloor = bind ( VanillaSymbol , '\\rfloor ' , '⌋' ) ;
LatexCmds . lceil = bind ( VanillaSymbol , '\\lceil ' , '⌈' ) ;
LatexCmds . rceil = bind ( VanillaSymbol , '\\rceil ' , '⌉' ) ;
2016-11-23 01:43:41 +08:00
LatexCmds . slash = bind ( VanillaSymbol , '/' ) ;
2015-09-09 04:00:52 +08:00
LatexCmds . lbrace = bind ( VanillaSymbol , '\\lbrace ' , '{' ) ;
LatexCmds . rbrace = bind ( VanillaSymbol , '\\rbrace ' , '}' ) ;
2011-11-08 01:15:13 +08:00
//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>');
2012-03-01 10:19:49 +08:00
LatexCmds [ '¬' ] = LatexCmds . neg = bind ( VanillaSymbol , '\\neg ' , '¬' ) ;
2011-11-08 01:15:13 +08:00
2012-03-01 10:19:49 +08:00
LatexCmds [ '…' ] = LatexCmds . dots = LatexCmds . ellip = LatexCmds . hellip =
2011-11-08 01:15:13 +08:00
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 ' , '⇓' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . diverges = LatexCmds . uarr = LatexCmds . uparrow =
bind ( VanillaSymbol , '\\uparrow ' , '↑' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . uArr = LatexCmds . Uparrow = bind ( VanillaSymbol , '\\Uparrow ' , '⇑' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . to = bind ( BinaryOperator , '\\to ' , '→' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . rarr = LatexCmds . rightarrow = bind ( VanillaSymbol , '\\rightarrow ' , '→' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . implies = bind ( BinaryOperator , '\\Rightarrow ' , '⇒' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . rArr = LatexCmds . Rightarrow = bind ( VanillaSymbol , '\\Rightarrow ' , '⇒' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . gets = bind ( BinaryOperator , '\\gets ' , '←' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . larr = LatexCmds . leftarrow = bind ( VanillaSymbol , '\\leftarrow ' , '←' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . impliedby = bind ( BinaryOperator , '\\Leftarrow ' , '⇐' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . lArr = LatexCmds . Leftarrow = bind ( VanillaSymbol , '\\Leftarrow ' , '⇐' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . harr = LatexCmds . lrarr = LatexCmds . leftrightarrow =
bind ( VanillaSymbol , '\\leftrightarrow ' , '↔' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . iff = bind ( BinaryOperator , '\\Leftrightarrow ' , '⇔' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . hArr = LatexCmds . lrArr = LatexCmds . Leftrightarrow =
bind ( VanillaSymbol , '\\Leftrightarrow ' , '⇔' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . Re = LatexCmds . Real = LatexCmds . real = bind ( VanillaSymbol , '\\Re ' , 'ℜ' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . Im = LatexCmds . imag =
LatexCmds . image = LatexCmds . imagin = LatexCmds . imaginary = LatexCmds . Imaginary =
bind ( VanillaSymbol , '\\Im ' , 'ℑ' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . part = LatexCmds . partial = bind ( VanillaSymbol , '\\partial ' , '∂' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . inf = LatexCmds . infin = LatexCmds . infty = LatexCmds . infinity =
bind ( VanillaSymbol , '\\infty ' , '∞' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . alef = LatexCmds . alefsym = LatexCmds . aleph = LatexCmds . alephsym =
bind ( VanillaSymbol , '\\aleph ' , 'ℵ' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . xist = //LOL
LatexCmds . xists = LatexCmds . exist = LatexCmds . exists =
bind ( VanillaSymbol , '\\exists ' , '∃' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . and = LatexCmds . land = LatexCmds . wedge =
bind ( VanillaSymbol , '\\wedge ' , '∧' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . or = LatexCmds . lor = LatexCmds . vee = bind ( VanillaSymbol , '\\vee ' , '∨' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . o = LatexCmds . O =
LatexCmds . empty = LatexCmds . emptyset =
LatexCmds . oslash = LatexCmds . Oslash =
LatexCmds . nothing = LatexCmds . varnothing =
bind ( BinaryOperator , '\\varnothing ' , '∅' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . cup = LatexCmds . union = bind ( VanillaSymbol , '\\cup ' , '∪' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . cap = LatexCmds . intersect = LatexCmds . intersection =
bind ( VanillaSymbol , '\\cap ' , '∩' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . deg = LatexCmds . degree = bind ( VanillaSymbol , '^\\circ ' , '°' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
LatexCmds . ang = LatexCmds . angle = bind ( VanillaSymbol , '\\angle ' , '∠' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
function NonItalicizedFunction ( replacedFragment , fn ) {
2014-12-19 17:35:02 +08:00
Symbol . call ( this , '\\' + fn + ' ' , '<span>' + htmlEscape ( fn ) + '</span>' ) ;
2011-11-08 01:15:13 +08:00
}
_ = NonItalicizedFunction . prototype = new Symbol ;
_ . respace = function ( )
{
this . jQ [ 0 ] . className =
( this . next instanceof SupSub || this . next 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 ;
}
} ( ) ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Cursor and Selection "singleton" classes
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
/ * T h e m a i n t h i n g t h a t m a n i p u l a t e s t h e M a t h D O M . M a k e s s u r e t o m a n i p u l a t e t h e
HTML DOM to match . * /
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
/ * S o r t o f s i n g l e t o n s , s i n c e t h e r e s h o u l d o n l y b e o n e p e r e d i t a b l e m a t h
textbox , but any one HTML document can contain many such textboxes , so any one
JS environment could actually contain many instances . * /
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
//A fake cursor in the fake textbox that the math is rendered in.
function Cursor ( root ) {
this . parent = this . root = root ;
2012-03-01 10:19:49 +08:00
var jQ = this . jQ = this . _jQ = $ ( '<span class="cursor">‍</span>' ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
//closured for setInterval
this . blink = function ( ) { jQ . toggleClass ( 'blink' ) ; }
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ = Cursor . prototype ;
_ . prev = 0 ;
_ . next = 0 ;
_ . parent = 0 ;
_ . 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 . next ) {
if ( this . selection && this . selection . prev === this . prev )
this . jQ . insertBefore ( this . selection . jQ ) ;
else
this . jQ . insertBefore ( this . next . jQ . first ( ) ) ;
}
2011-04-15 07:08:16 +08:00
else
2011-11-08 01:15:13 +08:00
this . jQ . appendTo ( this . parent . jQ ) ;
this . parent . focus ( ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
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 ;
} ;
_ . redraw = function ( ) {
for ( var ancestor = this . parent ; ancestor ; ancestor = ancestor . parent )
if ( ancestor . redraw )
ancestor . redraw ( ) ;
} ;
2012-03-01 10:19:49 +08:00
_ . insertAt = function ( parent , prev , next ) {
2011-11-08 01:15:13 +08:00
var old _parent = this . parent ;
this . parent = parent ;
this . prev = prev ;
2012-03-01 10:19:49 +08:00
this . next = next ;
2011-11-08 01:15:13 +08:00
old _parent . blur ( ) ; //blur may need to know cursor's destination
} ;
_ . insertBefore = function ( el ) {
2012-03-01 10:19:49 +08:00
this . insertAt ( el . parent , el . prev , el )
2011-11-08 01:15:13 +08:00
this . parent . jQ . addClass ( 'hasCursor' ) ;
this . jQ . insertBefore ( el . jQ . first ( ) ) ;
return this ;
} ;
_ . insertAfter = function ( el ) {
2012-03-01 10:19:49 +08:00
this . insertAt ( el . parent , el , el . next ) ;
2011-11-08 01:15:13 +08:00
this . parent . jQ . addClass ( 'hasCursor' ) ;
this . jQ . insertAfter ( el . jQ . last ( ) ) ;
return this ;
} ;
_ . prependTo = function ( el ) {
2012-03-01 10:19:49 +08:00
this . insertAt ( el , 0 , el . firstChild ) ;
2011-11-08 01:15:13 +08:00
if ( el . textarea ) //never insert before textarea
this . jQ . insertAfter ( el . textarea ) ;
2011-04-15 07:08:16 +08:00
else
2011-11-08 01:15:13 +08:00
this . jQ . prependTo ( el . jQ ) ;
el . focus ( ) ;
return this ;
} ;
_ . appendTo = function ( el ) {
2012-03-01 10:19:49 +08:00
this . insertAt ( el , el . lastChild , 0 ) ;
2011-11-08 01:15:13 +08:00
this . jQ . appendTo ( el . jQ ) ;
el . focus ( ) ;
return this ;
} ;
_ . hopLeft = function ( ) {
this . jQ . insertBefore ( this . prev . jQ . first ( ) ) ;
this . next = this . prev ;
this . prev = this . prev . prev ;
return this ;
} ;
_ . hopRight = function ( ) {
this . jQ . insertAfter ( this . next . jQ . last ( ) ) ;
this . prev = this . next ;
this . next = this . next . next ;
return this ;
} ;
_ . moveLeft = function ( ) {
if ( this . selection )
this . insertBefore ( this . selection . prev . next || this . parent . firstChild ) . clearSelection ( ) ;
else {
if ( this . prev ) {
if ( this . prev . lastChild )
this . appendTo ( this . prev . lastChild )
else
this . hopLeft ( ) ;
}
else { //we're at the beginning of a block
if ( this . parent . prev )
this . appendTo ( this . parent . prev ) ;
else if ( this . parent !== this . root )
this . insertBefore ( this . parent . parent ) ;
//else we're at the beginning of the root, so do nothing.
}
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
return this . show ( ) ;
} ;
_ . moveRight = function ( ) {
if ( this . selection )
this . insertAfter ( this . selection . next . prev || this . parent . lastChild ) . clearSelection ( ) ;
else {
if ( this . next ) {
if ( this . next . firstChild )
this . prependTo ( this . next . firstChild )
else
this . hopRight ( ) ;
}
else { //we're at the end of a block
if ( this . parent . next )
this . prependTo ( this . parent . next ) ;
else if ( this . parent !== this . root )
this . insertAfter ( this . parent . parent ) ;
//else we're at the end of the root, so do nothing.
}
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
return this . show ( ) ;
} ;
_ . seek = function ( target , pageX , pageY ) {
2012-03-01 10:19:49 +08:00
var cursor = this . clearSelection ( ) ;
2011-11-08 01:15:13 +08:00
if ( target . hasClass ( 'empty' ) ) {
2012-03-01 10:19:49 +08:00
cursor . prependTo ( target . data ( jQueryDataKey ) . block ) ;
2011-11-08 01:15:13 +08:00
return cursor ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
var data = target . data ( jQueryDataKey ) ;
if ( data ) {
//if clicked a symbol, insert at whichever side is closer
if ( data . cmd && ! data . block ) {
if ( target . outerWidth ( ) > 2 * ( pageX - target . offset ( ) . left ) )
cursor . insertBefore ( data . cmd ) ;
else
cursor . insertAfter ( data . cmd ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
return cursor ;
}
}
//if no MathQuill data, try parent, if still no, forget it
else {
target = target . parent ( ) ;
data = target . data ( jQueryDataKey ) ;
if ( ! data )
data = { block : cursor . root } ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
if ( data . cmd )
cursor . insertAfter ( data . cmd ) ;
else
cursor . appendTo ( data . block ) ;
//move cursor to position closest to click
var dist = cursor . jQ . offset ( ) . left - pageX , prevDist ;
do {
cursor . moveLeft ( ) ;
prevDist = dist ;
dist = cursor . jQ . offset ( ) . left - pageX ;
}
while ( dist > 0 && ( cursor . prev || cursor . parent !== cursor . root ) ) ;
if ( - dist > prevDist )
cursor . moveRight ( ) ;
return cursor ;
} ;
_ . resolveNonItalicizedFunctions = function ( ) {
var node = this . prev ;
var raw = node ? node . latex ( ) . replace ( / $/ , '' ) : null ;
var count = 0 ;
var functions = { ln : 1 , lg : 1 , log : 1 , span : 1 , proj : 1 , det : 1 , dim : 1 , min : 1 , max : 1 , mod : 1 , lcm : 1 , gcd : 1 , gcf : 1 , hcf : 1 , lim : 1 , sin : 1 , sinh : 1 , asin : 1 , arcsin : 1 , asinh : 1 , arcsinh : 1 , cos : 1 , cosh : 1 , acos : 1 , arccos : 1 , acosh : 1 , arccosh : 1 , tan : 1 , tanh : 1 , atan : 1 , arctan : 1 , atanh : 1 , arctanh : 1 , sec : 1 , sech : 1 , asec : 1 , arcsec : 1 , asech : 1 , arcsech : 1 , cosec : 1 , cosech : 1 , acosec : 1 , arccosec : 1 , acosech : 1 , arccosech : 1 , csc : 1 , csch : 1 , acsc : 1 , arccsc : 1 , acsch : 1 , arccsch : 1 , cotan : 1 , cotanh : 1 , acotan : 1 , arccotan : 1 , acotanh : 1 , arccotanh : 1 , cot : 1 , coth : 1 , acot : 1 , arccot : 1 , acoth : 1 , arccoth : 1 }
var latex = '' ;
while ( node && raw ) {
var single _char = raw . match ( /^[a-z]$/ ) ;
if ( single _char || latex && raw [ 0 ] == '\\' && functions [ raw . substring ( 1 ) ] && functions [ ( raw + latex ) . substring ( 1 ) ] ) {
count ++ ;
latex = raw . replace ( /\\/ , '' ) + latex ;
node = node . prev ;
raw = node ? node . latex ( ) . replace ( / $/ , '' ) : null ;
if ( ! single _char || ( ! node || ! raw . match ( /^[a-z]$/ ) ) && functions [ latex ] ) {
for ( var i = 0 ; i < count ; i ++ ) {
this . selectLeft ( ) ;
}
this . writeLatex ( "\\" + latex ) ;
return ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
}
else {
2011-04-15 07:08:16 +08:00
return ;
}
}
2011-11-08 01:15:13 +08:00
} ;
_ . writeLatex = function ( latex , noMoveCursor ) {
this . deleteSelection ( ) ;
2012-03-01 10:19:49 +08:00
latex = ( latex && latex . match ( /\\text\{([^}]|\\\})*\}|\\:|\\[a-z]*|[^\s]/ig ) ) || 0 ;
2011-11-08 01:15:13 +08:00
( function writeLatexBlock ( cursor ) {
while ( latex . length ) {
var token = latex . shift ( ) ; //pop first item
if ( ! token || token === '}' || token === ']' ) return ;
var cmd ;
if ( token . slice ( 0 , 6 ) === '\\text{' ) {
cmd = new TextBlock ( token . slice ( 6 , - 1 ) ) ;
cursor . insertNew ( cmd ) . insertAfter ( cmd ) ;
continue ; //skip recursing through children
2011-04-15 07:08:16 +08:00
}
2012-03-01 10:19:49 +08:00
else if ( token === '\\left' || token === '\\right' ) { //FIXME HACK: implement real \left and \right LaTeX commands, rather than special casing them here
token = latex . shift ( ) ;
if ( token === '\\' )
token = latex . shift ( ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
cursor . insertCh ( token ) ;
cmd = cursor . prev || cursor . parent . parent ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
if ( cursor . prev ) //was a close-paren, so break recursion
return ;
else //was an open-paren, hack to put the following latex
latex . unshift ( '{' ) ; //in the ParenBlock in the math DOM
}
else if ( /^\\[a-z:]+$/i . test ( token ) ) {
token = token . slice ( 1 ) ;
var cmd = LatexCmds [ token ] ;
if ( cmd ) {
cmd = new cmd ( undefined , token ) ;
if ( latex [ 0 ] === '[' && cmd . optional _arg _command ) {
//e.g. \sqrt{m} -> SquareRoot, \sqrt[n]{m} -> NthRoot
token = cmd . optional _arg _command ;
cmd = new LatexCmds [ token ] ( undefined , token ) ;
}
cursor . insertNew ( cmd ) ;
}
else {
cmd = new TextBlock ( token ) ;
cursor . insertNew ( cmd ) . insertAfter ( cmd ) ;
continue ; //skip recursing through children
2011-04-15 07:08:16 +08:00
}
}
else {
2011-11-08 01:15:13 +08:00
if ( token . match ( /[a-eg-zA-Z]/ ) ) //exclude f because want florin
cmd = new Variable ( token ) ;
else if ( cmd = LatexCmds [ token ] )
cmd = new cmd ;
else
cmd = new VanillaSymbol ( token ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
cursor . insertNew ( cmd ) ;
}
cmd . eachChild ( function ( child ) {
cursor . appendTo ( child ) ;
var token = latex . shift ( ) ;
if ( ! token ) return false ;
if ( token === '{' || token === '[' )
writeLatexBlock ( cursor ) ;
else
cursor . insertCh ( token ) ;
} ) ;
if ( ! noMoveCursor )
cursor . insertAfter ( cmd ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
} ( this ) ) ;
return this . hide ( ) ;
} ;
_ . write = function ( ch ) {
var ret = this . show ( ) . insertCh ( ch ) ;
if ( this . root . toolbar )
this . resolveNonItalicizedFunctions ( ) ;
return ret ;
} ;
_ . insertCh = function ( ch ) {
if ( this . selection ) {
//gotta do this before this.selection is mutated by 'new cmd(this.selection)'
this . prev = this . selection . prev ;
this . next = this . selection . next ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
var cmd ;
if ( ch . match ( /^[a-eg-zA-Z]$/ ) ) //exclude f because want florin
cmd = new Variable ( ch ) ;
else if ( cmd = CharCmds [ ch ] || LatexCmds [ ch ] )
cmd = new cmd ( this . selection , ch ) ;
else
cmd = new VanillaSymbol ( ch ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
if ( this . selection ) {
if ( cmd instanceof Symbol )
this . selection . remove ( ) ;
delete this . selection ;
}
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
return this . insertNew ( cmd ) ;
} ;
_ . insertNew = function ( cmd ) {
2012-03-01 10:19:49 +08:00
cmd . insertAt ( this ) ;
return this ;
} ;
_ . insertCmd = function ( latexCmd , replacedFragment ) {
var cmd = LatexCmds [ latexCmd ] ;
if ( cmd ) {
cmd = new cmd ( replacedFragment , latexCmd ) ;
this . insertNew ( cmd ) ;
if ( cmd instanceof Symbol && replacedFragment )
replacedFragment . remove ( ) ;
}
else {
cmd = new TextBlock ( latexCmd ) ;
cmd . firstChild . focus = function ( ) { delete this . focus ; return this ; } ;
this . insertNew ( cmd ) . insertAfter ( cmd ) ;
if ( replacedFragment )
replacedFragment . remove ( ) ;
}
2011-11-08 01:15:13 +08:00
return this ;
} ;
_ . unwrapGramp = function ( ) {
var gramp = this . parent . parent ,
greatgramp = gramp . parent ,
prev = gramp . prev ,
cursor = this ;
gramp . eachChild ( function ( uncle ) {
if ( uncle . isEmpty ( ) ) return ;
uncle . eachChild ( function ( cousin ) {
cousin . parent = greatgramp ;
2012-03-01 10:19:49 +08:00
cousin . jQ . insertBefore ( gramp . jQ . first ( ) ) ;
2011-11-08 01:15:13 +08:00
} ) ;
uncle . firstChild . prev = prev ;
if ( prev )
prev . next = uncle . firstChild ;
else
greatgramp . firstChild = uncle . firstChild ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
prev = uncle . lastChild ;
2011-04-15 07:08:16 +08:00
} ) ;
2011-11-08 01:15:13 +08:00
prev . next = gramp . next ;
if ( gramp . next )
gramp . next . prev = prev ;
2011-04-15 07:08:16 +08:00
else
2011-11-08 01:15:13 +08:00
greatgramp . lastChild = prev ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
if ( ! this . next ) { //then find something to be next to insertBefore
if ( this . prev )
this . next = this . prev . next ;
else {
while ( ! this . next ) {
this . parent = this . parent . next ;
if ( this . parent )
this . next = this . parent . firstChild ;
else {
this . next = gramp . next ;
this . parent = greatgramp ;
break ;
}
2011-04-15 07:08:16 +08:00
}
}
}
2011-11-08 01:15:13 +08:00
if ( this . next )
this . insertBefore ( this . next ) ;
2011-04-15 07:08:16 +08:00
else
2011-11-08 01:15:13 +08:00
this . appendTo ( greatgramp ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
gramp . jQ . remove ( ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
if ( gramp . prev )
gramp . prev . respace ( ) ;
if ( gramp . next )
gramp . next . respace ( ) ;
} ;
_ . backspace = function ( ) {
if ( this . deleteSelection ( ) ) ;
else if ( this . prev ) {
if ( this . prev . isEmpty ( ) )
this . prev = this . prev . remove ( ) . prev ;
else
this . selectLeft ( ) ;
}
else if ( this . parent !== this . root ) {
if ( this . parent . parent . isEmpty ( ) )
return this . insertAfter ( this . parent . parent ) . backspace ( ) ;
else
this . unwrapGramp ( ) ;
}
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
if ( this . prev )
this . prev . respace ( ) ;
if ( this . next )
this . next . respace ( ) ;
this . redraw ( ) ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
return this ;
} ;
_ . deleteForward = function ( ) {
if ( this . deleteSelection ( ) ) ;
else if ( this . next ) {
if ( this . next . isEmpty ( ) )
this . next = this . next . remove ( ) . next ;
else
this . selectRight ( ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
else if ( this . parent !== this . root ) {
if ( this . parent . parent . isEmpty ( ) )
return this . insertBefore ( this . parent . parent ) . deleteForward ( ) ;
else
this . unwrapGramp ( ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
if ( this . prev )
this . prev . respace ( ) ;
if ( this . next )
this . next . respace ( ) ;
this . redraw ( ) ;
return this ;
} ;
_ . 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 left/prev and which is right/next
var left , right , leftRight ;
if ( left . next !== right ) {
for ( var next = left ; next ; next = next . next ) {
if ( next === right . prev ) {
leftRight = true ;
break ;
}
}
if ( ! leftRight ) {
leftRight = right ;
right = left ;
left = leftRight ;
2011-04-15 07:08:16 +08:00
}
}
2011-11-08 01:15:13 +08:00
this . hide ( ) . selection = new Selection (
left . parent ,
left . prev ,
right . next
) ;
this . insertAfter ( right . next . prev || right . parent . lastChild ) ;
2012-03-01 10:19:49 +08:00
this . root . selectionChanged ( ) ;
2011-11-08 01:15:13 +08:00
} ;
_ . selectLeft = function ( ) {
if ( this . selection ) {
if ( this . selection . prev === this . prev ) { //if cursor is at left edge of selection;
if ( this . prev ) { //then extend left if possible
this . hopLeft ( ) . next . jQ . prependTo ( this . selection . jQ ) ;
this . selection . prev = this . prev ;
}
else if ( this . parent !== this . root ) //else level up if possible
this . insertBefore ( this . parent . parent ) . selection . levelUp ( ) ;
}
else { //else cursor is at right edge of selection, retract left
this . prev . jQ . insertAfter ( this . selection . jQ ) ;
this . hopLeft ( ) . selection . next = this . next ;
2012-03-01 10:19:49 +08:00
if ( this . selection . prev === this . prev ) {
2011-11-08 01:15:13 +08:00
this . deleteSelection ( ) ;
2012-03-01 10:19:49 +08:00
return ;
}
2011-11-08 01:15:13 +08:00
}
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
else {
if ( this . prev )
this . hopLeft ( ) ;
else //end of a block
if ( this . parent !== this . root )
this . insertBefore ( this . parent . parent ) ;
2012-03-01 10:19:49 +08:00
else
return ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
this . hide ( ) . selection = new Selection ( this . parent , this . prev , this . next . next ) ;
}
2012-03-01 10:19:49 +08:00
this . root . selectionChanged ( ) ;
2011-11-08 01:15:13 +08:00
} ;
_ . selectRight = function ( ) {
if ( this . selection ) {
if ( this . selection . next === this . next ) { //if cursor is at right edge of selection;
if ( this . next ) { //then extend right if possible
this . hopRight ( ) . prev . jQ . appendTo ( this . selection . jQ ) ;
this . selection . next = this . next ;
}
else if ( this . parent !== this . root ) //else level up if possible
this . insertAfter ( this . parent . parent ) . selection . levelUp ( ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
else { //else cursor is at left edge of selection, retract right
this . next . jQ . insertBefore ( this . selection . jQ ) ;
this . hopRight ( ) . selection . prev = this . prev ;
2012-03-01 10:19:49 +08:00
if ( this . selection . next === this . next ) {
2011-11-08 01:15:13 +08:00
this . deleteSelection ( ) ;
2012-03-01 10:19:49 +08:00
return ;
}
2011-11-08 01:15:13 +08:00
}
}
else {
if ( this . next )
this . hopRight ( ) ;
else //end of a block
if ( this . parent !== this . root )
this . insertAfter ( this . parent . parent ) ;
2012-03-01 10:19:49 +08:00
else
return ;
2011-11-08 01:15:13 +08:00
this . hide ( ) . selection = new Selection ( this . parent , this . prev . prev , this . next ) ;
}
2012-03-01 10:19:49 +08:00
this . root . selectionChanged ( ) ;
2011-11-08 01:15:13 +08:00
} ;
_ . clearSelection = function ( ) {
if ( this . show ( ) . selection ) {
this . selection . clear ( ) ;
delete this . selection ;
2012-03-01 10:19:49 +08:00
this . root . selectionChanged ( ) ;
2011-11-08 01:15:13 +08:00
}
return this ;
} ;
_ . deleteSelection = function ( ) {
if ( ! this . show ( ) . selection ) return false ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
this . prev = this . selection . prev ;
this . next = this . selection . next ;
this . selection . remove ( ) ;
2011-04-15 07:08:16 +08:00
delete this . selection ;
2012-03-01 10:19:49 +08:00
this . root . selectionChanged ( ) ;
2011-11-08 01:15:13 +08:00
return true ;
} ;
function Selection ( parent , prev , next ) {
MathFragment . apply ( this , arguments ) ;
2011-04-15 07:08:16 +08:00
}
2011-11-08 01:15:13 +08:00
_ = Selection . prototype = new MathFragment ;
2012-03-01 10:19:49 +08:00
_ . jQinit = function ( children ) {
2011-11-08 01:15:13 +08:00
this . jQ = children . wrapAll ( '<span class="selection"></span>' ) . parent ( ) ;
//can't do wrapAll(this.jQ = $(...)) because wrapAll will clone it
} ;
_ . levelUp = function ( ) {
this . clear ( ) . jQinit ( this . parent . parent . jQ ) ;
this . prev = this . parent . parent . prev ;
this . next = this . parent . parent . next ;
this . parent = this . parent . parent . parent ;
2011-04-15 07:08:16 +08:00
return this ;
2011-11-08 01:15:13 +08:00
} ;
_ . clear = function ( ) {
this . jQ . replaceWith ( this . jQ . children ( ) ) ;
return this ;
} ;
_ . blockify = function ( ) {
this . jQ . replaceWith ( this . jQ = this . jQ . children ( ) ) ;
return MathFragment . prototype . blockify . call ( this ) ;
} ;
_ . detach = function ( ) {
var block = MathFragment . prototype . blockify . call ( this ) ;
this . blockify = function ( ) {
this . jQ . replaceWith ( block . jQ = this . jQ = this . jQ . children ( ) ) ;
return block ;
} ;
return this ;
} ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 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.
$ . fn . mathquill = function ( cmd , latex ) {
switch ( cmd ) {
case 'redraw' :
2012-03-01 10:19:49 +08:00
this . find ( ':not(:has(:first))' ) . each ( function ( ) {
var data = $ ( this ) . data ( jQueryDataKey ) ;
if ( data && ( data . cmd || data . block ) ) Cursor . prototype . redraw . call ( data . cmd || data . block ) ;
} ) ;
2011-11-08 01:15:13 +08:00
return this ;
case 'revert' :
2011-04-15 07:08:16 +08:00
return this . each ( function ( ) {
var data = $ ( this ) . data ( jQueryDataKey ) ;
2011-11-08 01:15:13 +08:00
if ( data && data . revert )
data . revert ( ) ;
2011-04-15 07:08:16 +08:00
} ) ;
2011-11-08 01:15:13 +08:00
case 'latex' :
if ( arguments . length > 1 ) {
return this . each ( function ( ) {
var data = $ ( this ) . data ( jQueryDataKey ) ;
if ( data && data . block && data . block . renderLatex )
data . block . renderLatex ( latex ) ;
} ) ;
}
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
var data = this . data ( jQueryDataKey ) ;
return data && data . block && data . block . latex ( ) ;
case 'text' :
var data = this . data ( jQueryDataKey ) ;
return data && data . block && data . block . text ( ) ;
case 'html' :
2012-03-01 10:19:49 +08:00
return this . html ( ) . replace ( / ?hasCursor|hasCursor / , '' )
. replace ( / class=(""|(?= |>))/g , '' )
. replace ( /<span class="?cursor( blink)?"?><\/span>/i , '' )
2011-11-08 01:15:13 +08:00
. replace ( /<span class="?textarea"?><textarea><\/textarea><\/span>/i , '' ) ;
case 'write' :
2012-03-01 10:19:49 +08:00
if ( arguments . length > 1 )
return this . each ( function ( ) {
var data = $ ( this ) . data ( jQueryDataKey ) ,
block = data && data . block ,
cursor = block && block . cursor ;
if ( cursor )
cursor . writeLatex ( latex ) . parent . blur ( ) ;
} ) ;
case 'cmd' :
2011-11-08 01:15:13 +08:00
if ( arguments . length > 1 )
return this . each ( function ( ) {
var data = $ ( this ) . data ( jQueryDataKey ) ,
block = data && data . block ,
cursor = block && block . cursor ;
if ( cursor ) {
2012-03-01 10:19:49 +08:00
cursor . show ( ) ;
if ( /^\\[a-z]+$/i . test ( latex ) ) {
if ( cursor . selection ) {
//gotta do cursor before cursor.selection is mutated by 'new cmd(cursor.selection)'
cursor . prev = cursor . selection . prev ;
cursor . next = cursor . selection . next ;
}
cursor . insertCmd ( latex . slice ( 1 ) , cursor . selection ) ;
delete cursor . selection ;
}
else
cursor . insertCh ( latex ) ;
cursor . hide ( ) . parent . blur ( ) ;
2011-11-08 01:15:13 +08:00
}
} ) ;
default :
var textbox = cmd === 'textbox' ,
include _toolbar = cmd === 'editor' ,
editable = include _toolbar || textbox || cmd === 'editable' ,
RootBlock = textbox ? RootTextBlock : RootMathBlock ;
2011-04-15 07:08:16 +08:00
return this . each ( function ( ) {
2011-11-08 01:15:13 +08:00
createRoot ( $ ( this ) , new RootBlock , textbox , editable , include _toolbar ) ;
2011-04-15 07:08:16 +08:00
} ) ;
2011-11-08 01:15:13 +08:00
}
} ;
2011-04-15 07:08:16 +08:00
2011-11-08 01:15:13 +08:00
//on document ready, mathquill-ify all `<tag class="mathquill-*">latex</tag>`
//elements according to their CSS class.
$ ( function ( ) {
2012-03-01 10:19:49 +08:00
$ ( '.mathquill-editable:not(.mathquill-rendered-math)' ) . mathquill ( 'editable' ) ;
2011-11-08 01:15:13 +08:00
$ ( '.mathquill-editor' ) . mathquill ( 'editor' ) ;
2012-03-01 10:19:49 +08:00
$ ( '.mathquill-textbox:not(.mathquill-rendered-math)' ) . mathquill ( 'textbox' ) ;
2011-11-08 01:15:13 +08:00
$ ( '.mathquill-embedded-latex' ) . mathquill ( ) ;
} ) ;
2011-04-15 07:08:16 +08:00
2012-03-01 10:19:49 +08:00
2012-02-07 00:01:15 +08:00
} ) ;