Commit f0ce33c9 by Rocky Duan

make mathjax working with the editor

parent b04a6456
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
idPostfix = idPostfix || ""; idPostfix = idPostfix || "";
var hooks = this.hooks = new Markdown.HookCollection(); var hooks = this.hooks = new Markdown.HookCollection();
hooks.addNoop("onPreviewRefresh"); // called with no arguments after the preview has been refreshed hooks.addNoop("onPreviewPush"); // called with no arguments after the preview has been refreshed
hooks.addNoop("postBlockquoteCreation"); // called with the user's selection *after* the blockquote was created; should return the actual to-be-inserted text hooks.addNoop("postBlockquoteCreation"); // called with the user's selection *after* the blockquote was created; should return the actual to-be-inserted text
hooks.addFalse("insertImageDialog"); /* called with one parameter: a callback to be called with the URL of the image. If the application creates hooks.addFalse("insertImageDialog"); /* called with one parameter: a callback to be called with the URL of the image. If the application creates
* its own image insertion dialog, this hook should return true, and the callback should be called with the chosen * its own image insertion dialog, this hook should return true, and the callback should be called with the chosen
...@@ -72,7 +72,7 @@ ...@@ -72,7 +72,7 @@
panels = new PanelCollection(idPostfix); panels = new PanelCollection(idPostfix);
var commandManager = new CommandManager(hooks); var commandManager = new CommandManager(hooks);
var previewManager = new PreviewManager(markdownConverter, panels, function () { hooks.onPreviewRefresh(); }); var previewManager = new PreviewManager(markdownConverter, panels, function (text, previewSet) { hooks.onPreviewPush(text, previewSet); });
var undoManager, uiManager; var undoManager, uiManager;
if (!/\?noundo/.test(doc.location.href)) { if (!/\?noundo/.test(doc.location.href)) {
...@@ -769,7 +769,7 @@ ...@@ -769,7 +769,7 @@
this.init(); this.init();
}; };
function PreviewManager(converter, panels, previewRefreshCallback) { function PreviewManager(converter, panels, previewPushCallback) {
var managerObj = this; var managerObj = this;
var timeout; var timeout;
...@@ -928,8 +928,7 @@ ...@@ -928,8 +928,7 @@
var emptyTop = position.getTop(panels.input) - getDocScrollTop(); var emptyTop = position.getTop(panels.input) - getDocScrollTop();
if (panels.preview) { if (panels.preview) {
previewSet(text); previewPushCallback(text, previewSet);
previewRefreshCallback();
} }
setPanelScrollTops(); setPanelScrollTops();
...@@ -2157,4 +2156,4 @@ ...@@ -2157,4 +2156,4 @@
} }
})(); })();
\ No newline at end of file
/*!
* Cross-Browser Split 1.1.1
* Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
* Available under the MIT License
* ECMAScript compliant, uniform cross-browser split method
*/
/**
* Splits a string into an array of strings using a regex or string separator. Matches of the
* separator are not included in the result array. However, if `separator` is a regex that contains
* capturing groups, backreferences are spliced into the result each time `separator` is matched.
* Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably
* cross-browser.
* @param {String} str String to split.
* @param {RegExp|String} separator Regex or string to use for separating the string.
* @param {Number} [limit] Maximum number of items to include in the result array.
* @returns {Array} Array of substrings.
* @example
*
* // Basic use
* split('a b c d', ' ');
* // -> ['a', 'b', 'c', 'd']
*
* // With limit
* split('a b c d', ' ', 2);
* // -> ['a', 'b']
*
* // Backreferences in result array
* split('..word1 word2..', /([a-z]+)(\d+)/i);
* // -> ['..', 'word', '1', ' ', 'word', '2', '..']
*/
var _split; // instead of split for a less common name; avoid conflict
// Avoid running twice; that would break the `nativeSplit` reference
_split = _split || function (undef) {
var nativeSplit = String.prototype.split,
compliantExecNpcg = /()??/.exec("")[1] === undef, // NPCG: nonparticipating capturing group
self;
self = function (str, separator, limit) {
// If `separator` is not a regex, use `nativeSplit`
if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
return nativeSplit.call(str, separator, limit);
}
var output = [],
flags = (separator.ignoreCase ? "i" : "") +
(separator.multiline ? "m" : "") +
(separator.extended ? "x" : "") + // Proposed for ES6
(separator.sticky ? "y" : ""), // Firefox 3+
lastLastIndex = 0,
// Make `global` and avoid `lastIndex` issues by working with a copy
separator = new RegExp(separator.source, flags + "g"),
separator2, match, lastIndex, lastLength;
str += ""; // Type-convert
if (!compliantExecNpcg) {
// Doesn't need flags gy, but they don't hurt
separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
}
/* Values for `limit`, per the spec:
* If undefined: 4294967295 // Math.pow(2, 32) - 1
* If 0, Infinity, or NaN: 0
* If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
* If negative number: 4294967296 - Math.floor(Math.abs(limit))
* If other: Type-convert, then use the above rules
*/
limit = limit === undef ?
-1 >>> 0 : // Math.pow(2, 32) - 1
limit >>> 0; // ToUint32(limit)
while (match = separator.exec(str)) {
// `separator.lastIndex` is not reliable cross-browser
lastIndex = match.index + match[0].length;
if (lastIndex > lastLastIndex) {
output.push(str.slice(lastLastIndex, match.index));
// Fix browsers whose `exec` methods don't consistently return `undefined` for
// nonparticipating capturing groups
if (!compliantExecNpcg && match.length > 1) {
match[0].replace(separator2, function () {
for (var i = 1; i < arguments.length - 2; i++) {
if (arguments[i] === undef) {
match[i] = undef;
}
}
});
}
if (match.length > 1 && match.index < str.length) {
Array.prototype.push.apply(output, match.slice(1));
}
lastLength = match[0].length;
lastLastIndex = lastIndex;
if (output.length >= limit) {
break;
}
}
if (separator.lastIndex === match.index) {
separator.lastIndex++; // Avoid an infinite loop
}
}
if (lastLastIndex === str.length) {
if (lastLength || !separator.test("")) {
output.push("");
}
} else {
output.push(str.slice(lastLastIndex));
}
return output.length > limit ? output.slice(0, limit) : output;
};
// For convenience
String.prototype.split = function (separator, limit) {
return self(this, separator, limit);
};
return self;
}();
# Mostly adapted from math.stackexchange.com: http://cdn.sstatic.net/js/mathjax-editing-new.js
$ -> $ ->
if Markdown? HUB = MathJax.Hub
mathRenderer = new MathJaxDelayRenderer()
removeMath = (text) -> text class MathJaxProcessor
inlineMark = "$"
math = null
blocks = null
MATHSPLIT = /// (
\$\$? # normal inline or display delimiter
| \\(?:begin|end)\{[a-z]*\*?\} # \begin{} \end{} style
| \\[\\{}$]
| [{}]
| (?:\n\s*)+ # only treat as math when there's single new line
| @@\d+@@ # delimiter similar to the one used internally
) ///i
CODESPAN = ///
(^|[^\\]) # match beginning or any previous character other than escape delimiter ('/')
(`+) # code span starts
([^\n]*?[^`\n]) # code content
\2 # code span ends
(?!`)
///gm
###HUB.Queue ->
console.log "initializing"
renderReady = true
HUB.processUpdateTime = 50
HUB.Config
"HTML-CSS":
EqnChunk: 10
EqnChunkFactor: 1
SVG:
EqnChunk: 10
EqnChunkFactor: 1
###
@processMath: (start, last, preProcess) =>
block = blocks.slice(start, last + 1).join("").replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
if HUB.Browser.isMSIE
block = block.replace /(%[^\n]*)\n/g, "$1<br/>\n"
blocks[i] = "" for i in [start+1..last]
blocks[start] = "@@#{math.length}@@"
block = preProcess(block) if preProcess
math.push block
@removeMath: (text) =>
replaceMath = (text) -> text math = []
start = end = last = null
braces = 0
updateMathJax = -> hasCodeSpans = /`/.test text
console.log "updating" if hasCodeSpans
#mathRenderer.render text = text.replace(/~/g, "~T").replace CODESPAN, ($0) -> # replace dollar sign in code span temporarily
# element: $("#wmd-preview") $0.replace /\$/g, "~D"
MathJax.Hub.Queue(["Typeset", MathJax.Hub, "wmd-preview"]) deTilde = (text) ->
text.replace /~([TD])/g, ($0, $1) ->
{T: "~", D: "$"}[$1]
else
deTilde = (text) -> text
blocks = _split(text.replace(/\r\n?/g, "\n"), MATHSPLIT)
for current in [1...blocks.length] by 2
block = blocks[current]
if block.charAt(0) == "@"
blocks[current] = "@@#{math.length}@@"
math.push block
else if start
if block == end
if braces
last = current
else
@processMath(start, current, deTilde)
start = end = last = null
else if block.match /\n.*\n/
if last
current = last
@processMath(start, current, deTilde)
start = end = last = null
braces = 0
else if block == "{"
++braces
else if block == "}" and braces
--braces
else
if block == inlineMark or block == "$$"
start = current
end = block
braces = 0
else if block.substr(1, 5) == "begin"
start = current
end = "\\end" + block.substr(6)
braces = 0
if last
@processMath(start, last, deTilde)
start = end = last = null
deTilde(blocks.join(""))
@replaceMath: (text) =>
text = text.replace /@@(\d+)@@/g, ($0, $1) => math[$1]
math = null
text
@updateMathJax: =>
HUB.Queue(["Typeset", HUB, "wmd-preview"])
###
if not HUB.Cancel? #and 1 == 2
HUB.cancelTypeset = false
CANCELMESSAGE = "MathJax Canceled"
HOOKS = [
{
name: "HTML-CSS Jax Config"
engine: -> window["MathJax"].OutputJax["HTML-CSS"]
},
{
name: "SVG Jax Config"
engine: -> window["MathJax"].OutputJax["SVG"]
},
{
name: "TeX Jax Config"
engine: -> window["MathJax"].InputJax.TeX
},
]
for hook in HOOKS
do (hook) ->
HUB.Register.StartupHook hook.name, ->
engine = hook.engine()
engine.Augment
Translate: (script, state) ->
console.log "translating"
if HUB.cancelTypeset or state.cancelled
throw Error(CANCELMESSAGE)
engine.Translate.call(engine, script, state)
prevProcessError = HUB.processError
HUB.processError = (error, state, type) ->
if error.message != CANCELMESSAGE
return prevProcessError.call(HUB, error, state, type)
else
console.log "handling message"
MathJax.Message.Clear(0, 0)
state.jaxIds = []
state.jax = {}
state.scripts = []
state.i = state.j = 0
state.cancelled = true
return null
HUB.Cancel = ->
this.cancelTypeset = true
###
if Markdown?
converter = Markdown.getSanitizingConverter() converter = Markdown.getSanitizingConverter()
editor = new Markdown.Editor(converter) editor = new Markdown.Editor(converter)
converter.hooks.chain "preConversion", removeMath converter.hooks.chain "preConversion", MathJaxProcessor.removeMath
converter.hooks.chain "postConversion", replaceMath converter.hooks.chain "postConversion", MathJaxProcessor.replaceMath
editor.hooks.chain "onPreviewRefresh", updateMathJax delayRenderer = new MathJaxDelayRenderer()
editor.hooks.chain "onPreviewPush", (text, previewSet) ->
delayRenderer.render
text: text
previewSetter: previewSet
editor.run() editor.run()
getTime = ->
new Date().getTime()
class @MathJaxDelayRenderer
maxDelay: 3000
mathjaxRunning: false
elapsedTime: 0
mathjaxDelay: 0
mathjaxTimeout: undefined
bufferId: "mathjax_delay_buffer"
constructor: (params) ->
params = params || {}
@maxDelay = params["maxDelay"] || @maxDelay
@bufferId = params["buffer"] || @bufferId
if not $("##{@bufferId}").length
$("<div>").attr("id", @bufferId).css("display", "none").appendTo($("body"))
# render: (params) ->
# params:
# elem: jquery element to be rendered
# text: text to be rendered & put into the element;
# if blank, then just render the current text in the element
# preprocessor: pre-process the text before rendering using MathJax
# if text is blank, it will pre-process the html in the element
# previewSetter: if provided, will pass text back to it instead of
# directly setting the element
render: (params) ->
elem = params["element"]
previewSetter = params["previewSetter"]
text = params["text"]
if not text?
text = $(elem).html()
preprocessor = params["preprocessor"]
buffer = $("##{@bufferId}")
if params["delay"] == false
if preprocessor?
text = preprocessor(text)
$(elem).html(text)
MathJax.Hub.Queue ["Typeset", MathJax.Hub, $(elem).attr("id")]
else
if @mathjaxTimeout
window.clearTimeout(@mathjaxTimeout)
@mathjaxTimeout = undefined
delay = Math.min @elapsedTime + @mathjaxDelay, @maxDelay
renderer = =>
if @mathjaxRunning
return
prevTime = getTime()
if preprocessor?
text = preprocessor(text)
buffer.html(text)
curTime = getTime()
@elapsedTime = curTime - prevTime
if MathJax
prevTime = getTime()
@mathjaxRunning = true
MathJax.Hub.Queue ["Typeset", MathJax.Hub, buffer.attr("id")], =>
@mathjaxRunning = false
curTime = getTime()
@mathjaxDelay = curTime - prevTime
if previewSetter
previewSetter($(buffer).html())
else
$(elem).html($(buffer).html())
else
@mathjaxDelay = 0
@mathjaxTimeout = window.setTimeout(renderer, delay)
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of
MathJax extension libraries --> MathJax extension libraries -->
<script type="text/javascript" src="/static/js/vendor/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-MML-AM_HTMLorMML-full"></script> <script type="text/javascript" src="/static/js/vendor/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-MML-AM_HTMLorMML-full"></script>
<script type="text/javascript" src="${static.url('js/vendor/split.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/Markdown.Converter.js')}"></script> <script type="text/javascript" src="${static.url('js/vendor/Markdown.Converter.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/Markdown.Sanitizer.js')}"></script> <script type="text/javascript" src="${static.url('js/vendor/Markdown.Sanitizer.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/Markdown.Editor.js')}"></script> <script type="text/javascript" src="${static.url('js/vendor/Markdown.Editor.js')}"></script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment