Skip to content

Commit

Permalink
full blown editor mode (w/ pretty buttons etc.) for latex noobs
Browse files Browse the repository at this point in the history
supports both latex input and button usage. also auto-converts functions
into latex (e.g. sin -> \sin). added example to demo.html
  • Loading branch information
jenseng committed Apr 14, 2011
1 parent a499487 commit 043c9f5
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 7 deletions.
4 changes: 4 additions & 0 deletions demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
.mathquill-embedded-latex .mathquill-editable {
min-width: 1cm;
}
.mathquill-editor {
width:6.5in;
}
</style>
</head>
<body>
Expand All @@ -47,6 +50,7 @@ <h2><a href="http://laughinghan.github.com/mathquill">MathQuill 0.2&alpha;</a></
<pre id="html-source"></pre>
<p>In many applications, such as a chat client, you probably type mostly normal text with some math interspersed, so we also have a MathQuill Textbox that let's you type math between $'s: <span class="mathquill-textbox">lolwut $a^2 + b^2 = c^2$. Also, awesomesauce: $\int_0^1 \sin x dx.</span></p>
<p>LaTeX math can also have textboxes inside: <span class="mathquill-embedded-latex">\int\editable{}dx</span> or even <span class="mathquill-embedded-latex">\sqrt{\editable{x^2+y^2}}</span></p>
<p>Use the "mathquill-editor" class and you get a fancy WYSIWYG editor that's great for LaTeX noobs (it still supports LaTeX commands though): <span class="mathquill-editor">\sqrt[3]{8}=\frac{\sqrt{16}}{2}</span></p>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.js"></script>
<script type="text/javascript" src="build/mathquill.js"></script>
<script type="text/javascript">
Expand Down
114 changes: 114 additions & 0 deletions mathquill.css
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,117 @@
/*.mathquill-rendered-math .not {
margin-right: -.75em;
}*/


/*
* editor/toolbar styles
*/
.mathquill-editor {
padding: 0 4px 10px;
width: 680px;
display: inline-block;
}
.mathquill-toolbar {
font-family: sans-serif, Verdana, Arial;
font-size: 1em;
margin: 0 -4px 10px;
position: relative;
overflow: hidden;
z-index: 101;
}
.mathquill-tab-bar {
padding: 8px 8px 2px 16px;
margin: 0 0 -2px;
line-height: 1.5;
font-size: 9pt;
list-style: none;
overflow: hidden;
display: block;
background: #aaa;
}
.mathquill-tab-bar li {
color: #555;
background: #ccc;
border-color: #999;
border-width: 1px 1px 0 1px;
border-style: solid;
-moz-border-radius-topright: 4px;
-moz-border-radius-topleft: 4px;
-webkit-border-radius-topright: 4px;
-webkit-border-radius-topleft: 4px;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
margin: 0 6px 0 0;
float: left;
top: 1px;
position: relative;
padding-bottom: 1px;
}
.mathquill-tab-bar li.mathquill-tab-selected {
background: #e8e8e8;
border-color: #999;
color: #000;
}
.mathquill-tab-bar li a {
padding: 4px 8px !important;
color: inherit;
text-decoration: none;
cursor: default;
display: block;
}
.mathquill-tab-bar a span {
padding: 0 8px 0 0;
font-weight: bold;
}
.mathquill-toolbar-panes {
border-top: 1px solid #999;
border-bottom: 1px solid #aaa;
background: #e8e8e8;
}
.mathquill-tab-pane {
margin: 0 -100% 0 0;
width: 100%;
visibility: hidden;
float: left;
}
.mathquill-tab-pane-selected {
visibility: visible;
margin: 0;
}
.mathquill-tab-pane ul {
margin: 0;
padding: 6px 12px;
overflow: hidden;
line-height: 1;
list-style: none;
}
.mathquill-tab-pane li {
overflow: hidden;
display: block;
float: left;
height: 1.5em;
margin: 0 -4px 3px;
padding: 4px 6px 0;
text-align: center;
background: #eee;
border-top: 1px solid #fff;
border-bottom: 1px solid #ccc;
}
.mathquill-tab-pane li.mathquill-button-spacer {
background: transparent;
border-color: transparent;
width: 10px;
}
.mathquill-tab-pane li a {
display: block;
font-size: 0.9em;
font-weight: bold;
width: 1.5em;
height: 1.4em;
}
.mathquill-tab-pane li a:hover {
background-color: #adcddf;
}
.mathquill-toolbar * {
cursor: default !important;
}
35 changes: 32 additions & 3 deletions src/cursor.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,32 @@ _.seek = function(target, pageX, pageY) {

return cursor;
};
_.writeLatex = function(latex) {
_.resolveNonItalicizedFunctions = function() {
var node = this.prev;
var count = 0;
var functions = ['ln', 'lg', 'log', 'span', 'proj', 'det', 'dim', 'min', 'max', 'mod', 'lcm', 'gcd', 'gcf', 'hcf', 'lim', 'sin', 'sinh', 'asin', 'arcsin', 'asinh', 'arcsinh', 'cos', 'cosh', 'acos', 'arccos', 'acosh', 'arccosh', 'tan', 'tanh', 'atan', 'arctan', 'atanh', 'arctanh', 'sec', 'sech', 'asec', 'arcsec', 'asech', 'arcsech', 'cosec', 'cosech', 'acosec', 'arccosec', 'acosech', 'arccosech', 'csc', 'csch', 'acsc', 'arccsc', 'acsch', 'arccsch', 'cotan', 'cotanh', 'acotan', 'arccotan', 'acotanh', 'arccotanh', 'cot', 'coth', 'acot', 'arccot', 'acoth', 'arccoth'];
var latex = '';
while (node && node.latex()) {
var raw = node.latex().replace(/ $/, '');
var single_char = raw.match(/^[a-z]$/);
if (single_char || latex && raw[0] == '\\' && window._.include(functions, raw.substring(1)) && window._.include(functions, (raw + latex).substring(1))) {
count++;
latex = raw.replace(/\\/, '') + latex;
if (!single_char || (!node.prev || !node.prev.latex().match(/^[a-z]$/)) && window._.include(functions, latex)) {
for(var i = 0; i < count; i++) {
this.selectLeft();
}
this.writeLatex("\\" + latex);
return;
}
}
else {
return;
}
node = node.prev;
}
};
_.writeLatex = function(latex, noMoveCursor) {
this.deleteSelection();
latex = ( latex && latex.match(/\\text\{([^{}]|\\\[{}])*\}|\\[\{\}\[\]]|[\(\)]|\\[a-z]*|[^\s]/ig) ) || 0;
(function writeLatexBlock(cursor) {
Expand Down Expand Up @@ -267,13 +292,17 @@ _.writeLatex = function(latex) {
else
cursor.insertCh(token);
});
cursor.insertAfter(cmd);
if (!noMoveCursor)
cursor.insertAfter(cmd);
}
}(this));
return this.hide();
};
_.write = function(ch) {
return this.show().insertCh(ch);
var ret = this.show().insertCh(ch);
if (this.root.toolbar)
this.resolveNonItalicizedFunctions();
return ret;
};
_.insertCh = function(ch) {
if (this.selection) {
Expand Down
6 changes: 4 additions & 2 deletions src/publicapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ $.fn.mathquill = function(cmd, latex) {
});
default:
var textbox = cmd === 'textbox',
editable = textbox || cmd === 'editable',
include_toolbar = cmd === 'editor',
editable = include_toolbar || textbox || cmd === 'editable',
RootBlock = textbox ? RootTextBlock : RootMathBlock;
return this.each(function() {
createRoot($(this), new RootBlock, textbox, editable);
createRoot($(this), new RootBlock, textbox, editable, include_toolbar);
});
}
};
Expand All @@ -59,6 +60,7 @@ $.fn.mathquill = function(cmd, latex) {
//elements according to their CSS class.
$(function() {
$('.mathquill-editable').mathquill('editable');
$('.mathquill-editor').mathquill('editor');
$('.mathquill-textbox').mathquill('textbox');
$('.mathquill-embedded-latex').mathquill();
});
Expand Down
93 changes: 91 additions & 2 deletions src/rootelements.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Root math elements with event delegation.
********************************************/

function createRoot(jQ, root, textbox, editable) {
function createRoot(jQ, root, textbox, editable, include_toolbar) {
var contents = jQ.contents().detach();

if (!textbox)
Expand All @@ -12,7 +12,7 @@ function createRoot(jQ, root, textbox, editable) {
block: root,
revert: function() {
jQ.empty().unbind('.mathquill')
.removeClass('mathquill-rendered-math mathquill-editable mathquill-textbox')
.removeClass('mathquill-rendered-math mathquill-editable mathquill-textbox mathquill-editor')
.append(contents);
}
});
Expand All @@ -29,6 +29,8 @@ function createRoot(jQ, root, textbox, editable) {
var textarea = root.textarea.children();
if (textbox)
jQ.addClass('mathquill-textbox');
if (include_toolbar)
addToolbar(root, jQ);

textarea.focus(function(e) {
if (!cursor.parent)
Expand Down Expand Up @@ -128,6 +130,93 @@ function createRoot(jQ, root, textbox, editable) {
var anticursor, blink = cursor.blink;
}

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: '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: 'Greek',
example: '&pi;',
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: 'Operators',
example: '&oplus;',
button_groups: [["wedge", "vee", "cup", "cap", "diamond", "bigtriangleup", "ominus", "uplus", "otimes", "oplus", "bigtriangledown", "sqcap", "triangleleft", "sqcup", "triangleright", "odot", "bigcirc", "dagger", "ddagger", "wr", "amalg"]
]},
{ name: 'Relationships',
example: '&le;',
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: 'Arrows',
example: '&hArr;',
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: 'Delimiters',
example: '{',
button_groups: [["lfloor", "rfloor", "lceil", "rceil", "slash", "opencurlybrace", "closecurlybrace"]
]},
{ name: 'Misc',
example: '&infin;',
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 = {
binomial: '<span style="font-size: 0.5em"><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>',
frac: '<span style="font-size: 0.55em" class="fraction"><span class="numerator"><var>n</var></span><span class="denominator"><var>m</var></span><span style="width:0"></span></span>',
sqrt: '<span style="font-size: 0.8em; padding-top: 3px"><span class="sqrt-prefix">&radic;</span><span class="sqrt-stem" style="border-top-width: 1.7142857142857144px;">&nbsp;</span></span>',
nthroot: '<span style="font-size: 0.7em"><sup class="nthroot"><var>n</var></sup><span><span class="sqrt-prefix">&radic;</span><span class="sqrt-stem" style="border-top-width: 1.7142857142857144px; ">&nbsp;</span></span></span>',
supscript: '<sup style="font-size: 0.6em">sup</sup>',
subscript: '<sub style="font-size: 0.6em; line-height: 3.5;">sub</sub>',
vector: '<span class="array" style="font-size: 0.6em"><span class=""><var>a</var><span> </span><var>b</var></span><span class=""><var>c</var><span> </span><var>d</var></span></span>'
}

var tabs = [];
var panes = [];
$.each(button_tabs, function(index, tab){
tabs.push('<li><a href="#' + tab.name + '_tab"><span>' + tab.example + '</span>' + tab.name + '</a></li>');
var buttons = [];
$.each(tab.button_groups, function(index, group) {
$.each(group, function(index, cmd) {
var obj = new LatexCmds[cmd](undefined, cmd);
buttons.push('<li><a class="mathquill-rendered-math" title="' + (cmd.match(/^[a-z]+$/) ? '\\' + cmd : cmd) + '">' +
(html_template_overrides[cmd] ? html_template_overrides[cmd] : '<span style="line-height: 1.5em">' + obj.html_template.join('') + '</span>') +
'</a></li>');
});
buttons.push('<li class="mathquill-button-spacer"></li>');
});
panes.push('<div class="mathquill-tab-pane" id="' + tab.name + '_tab"><ul>' + buttons.join('') + '</ul></div>');
});
root.toolbar = $('<div class="mathquill-toolbar"><ul class="mathquill-tab-bar">' + tabs.join('') + '</ul><div class="mathquill-toolbar-panes">' + panes.join('') + '</div></div>').prependTo(jQ);

jQ.find('.mathquill-tab-bar li a').mouseenter(function() {
jQ.find('.mathquill-tab-bar li').removeClass('mathquill-tab-selected');
jQ.find('.mathquill-tab-pane').removeClass('mathquill-tab-pane-selected');
$(this).parent().addClass('mathquill-tab-selected');
$(this.href.replace(/.*#/, '#')).addClass('mathquill-tab-pane-selected');
});
jQ.find('.mathquill-tab-bar li:first-child a').mouseenter();
jQ.find('a.mathquill-rendered-math').click(function(){
root.cursor.writeLatex(this.title, true);
jQ.focus();
});

return toolbar;
}

function RootMathBlock(){}
_ = RootMathBlock.prototype = new MathBlock;
_.latex = function() {
Expand Down

0 comments on commit 043c9f5

Please sign in to comment.