[cig-commits] r15760 - in doc/geodynamics.org/benchmarks/trunk: . js

luis at geodynamics.org luis at geodynamics.org
Mon Oct 5 15:40:53 PDT 2009


Author: luis
Date: 2009-10-05 15:40:52 -0700 (Mon, 05 Oct 2009)
New Revision: 15760

Added:
   doc/geodynamics.org/benchmarks/trunk/js/
   doc/geodynamics.org/benchmarks/trunk/js/textheworld6.user.js
Modified:
   doc/geodynamics.org/benchmarks/trunk/.gitignore
Log:
Javascript file responsible for rendering equations on a web page

Basically, this transforms the text [;EXPR;] into the HTML tag

  <img src="http://thewe.net/tex/EXPR" />

Modified: doc/geodynamics.org/benchmarks/trunk/.gitignore
===================================================================
--- doc/geodynamics.org/benchmarks/trunk/.gitignore	2009-10-05 22:40:44 UTC (rev 15759)
+++ doc/geodynamics.org/benchmarks/trunk/.gitignore	2009-10-05 22:40:52 UTC (rev 15760)
@@ -4,4 +4,3 @@
 
 # for now, ignore these
 /utils
-/js

Added: doc/geodynamics.org/benchmarks/trunk/js/textheworld6.user.js
===================================================================
--- doc/geodynamics.org/benchmarks/trunk/js/textheworld6.user.js	                        (rev 0)
+++ doc/geodynamics.org/benchmarks/trunk/js/textheworld6.user.js	2009-10-05 22:40:52 UTC (rev 15760)
@@ -0,0 +1,230 @@
+
+// ==UserScript==
+// @name           TeX the world
+// @namespace      http://thewe.net/tex
+// @description    TeX the world
+// @include        *
+// ==/UserScript==
+
+// regexp for splitting a matched string into parts
+var splitRegexp = /([\s\S]*?)\[;([\s\S]*?);\]([\s\S]*)/m;
+
+// regexp for quickly testing if a text node contains our format
+var matchRegexp = /\[;([\s\S]*?);\]/m;
+
+// regexp for finding all TeX formulas in textContent
+var matchRegexpGlobal = /\[;([\s\S]*?);\]/mg;
+
+// signature to be typed when clicking ctrl-shift-;
+var signature = '[To see formulas: http://thewe.net/tex]';
+
+// create an image element for a given expression
+function createTeXifiedImageElement(expr) {
+    var imgElement = document.createElement('img');
+    imgElement.src = "http://thewe.net/tex/" + expr;
+    imgElement.title = expr;
+    imgElement.alt = "[;" + expr + ";]";
+    imgElement.addEventListener('dblclick', unTeXifyImage, false);
+    return imgElement;
+}
+
+// TeXifies a range
+function TeXifyRange(startNode, startNodeOpen, endNode, endNodeClose) {
+    if (startNode == endNode) {
+        var textNode = startNode;
+        
+        if (textNode.parentNode.nodeName == 'TEXTAREA')
+            return;
+        
+        // infinite loop. it will break when there's nothing else matching the regexp.
+        while (textNode.nodeValue.match(matchRegexp)) {
+            textNodeSplit = splitRegexp.exec(textNode.nodeValue);
+            
+            // it matched. clear the text node and create the three
+            // nodes before it - text, img, text (according to regexp match).
+            // NOTE: the reason we clear the node value instead of using it for
+            // the right part of the expression it to keep the cursor located
+            // at the end of the node if the cursor is on it
+            textNode.nodeValue = '';
+            
+            // create the text node that should be after the image and insert it
+            var rightHalfNode = document.createTextNode(textNodeSplit[3]);
+            textNode.parentNode.insertBefore(rightHalfNode, textNode);        
+
+            // create the image and insert it
+            var imgElement = createTeXifiedImageElement(textNodeSplit[2]);
+            textNode.parentNode.insertBefore(imgElement, rightHalfNode);        
+
+            // create the text node that should be before the image and insert it
+            textNode.parentNode.insertBefore(document.createTextNode(textNodeSplit[1]), imgElement);
+            
+            // set the new textNode to be what was left. maybe there's more
+            // to match (for example "[;A = B;] thus [;A \subseteq B;]")
+            textNode = rightHalfNode;
+        }    
+    }
+    else {
+        // if the common ancestor is not the direct parent, stop immediately
+        if (startNode.parentNode != endNode.parentNode)
+            return;
+    
+        // verify that the only tags in the range are <br> and <wbr>
+        var node;
+        
+        for (node = startNode; node != endNode; node = node.nextSibling)
+          if (node.nodeType == 1 && node.tagName != 'BR' && node.tagName != 'WBR')
+                break;
+                
+        if (node != endNode)
+            return;
+
+        // create the range and TeXify it                        
+        var texRange = document.createRange();
+        texRange.setStart(startNode, startNodeOpen);
+        texRange.setEnd(endNode, endNodeClose);
+        
+        var tex = texRange.toString();
+        tex = tex.substring(2, tex.length - 2);
+        
+        texRange.detach();
+        
+        var deleteRange = document.createRange();
+        deleteRange.setStart(startNode, startNodeOpen);
+        deleteRange.setEndBefore(endNode);
+        
+        deleteRange.deleteContents();
+        deleteRange.insertNode(createTeXifiedImageElement(tex));
+        
+        var lastString = endNode.nodeValue.substring(endNodeClose);
+        endNode.parentNode.insertBefore(document.createTextNode(lastString), endNode);
+        endNode.nodeValue = '';
+        
+        deleteRange.detach();
+    }
+}
+
+// translates a list of indexes in a node's textContent into node/offset
+// pairs matching to the actual DOM tree
+textContentData = function(baseNode, relevantIndexes, riIndex, index, result) {
+    var relIndex = relevantIndexes[riIndex];
+    
+    // go through all children and check their textContent. in the meanwhile
+    // remember which index we are currently in, and then recurse until finding
+    // the actual node which contains the relevant index
+    
+    // implementation is based on the definition of textContent on
+    // http://developer.mozilla.org/en/docs/DOM:element.textContent#Notes
+    for (var child = baseNode.firstChild; child != null && riIndex < relevantIndexes.length; child = child.nextSibling) {
+        if (child.nodeType == 7 || child.nodeType == 8)
+            continue;
+            
+        var newIndex = index + child.textContent.length;
+        
+        while (newIndex > relIndex) {
+            if (child.nodeType == 3 || child.nodeType == 4) {
+                result.push({node: child, offset: relIndex - index});
+                riIndex++;
+            }
+            else {
+                result = textContentData(child, relevantIndexes, riIndex, index, result);
+                riIndex = result.length;
+            }
+
+            if (riIndex >= relevantIndexes.length)
+                break;
+
+            relIndex = relevantIndexes[riIndex];
+        }
+        
+        index = newIndex;
+    }
+    
+    return result;
+}
+
+// TeXify a single window/frame
+function TeXAFrame(win) {
+    try {
+        // register our keydown listener if this is a new frame
+        if (!win.document.body.hasAttribute('_texified')) {
+            win.document.addEventListener('keydown', onKeyDown, true);
+            win.document.body.setAttribute('_texified', true);
+        }
+    
+        var text = win.document.body.textContent;
+        var relevantIndexes = [];
+        
+        // calculate the relevant indexes to search for afterwards
+        // (the indexes of [; and ;])
+        while ((theMatch = matchRegexpGlobal.exec(text)) != null) {
+            relevantIndexes.push(theMatch.index);
+            relevantIndexes.push(theMatch.index + theMatch[0].length - 1);
+        }
+        
+        // get the mapping from these indexes to nodes/offset pairs
+        var data = textContentData(win.document.body, relevantIndexes, 0, 0, []);
+        
+        for (var i = 0; i < data.length; i += 2)
+            TeXifyRange(data[i].node, data[i].offset, data[i+1].node, data[i+1].offset + 1);
+    }
+    catch (e) { // security exception
+    }
+        
+    // do the same for all frames in this window/frame
+    for (var i = 0, n = win.frames.length; i < n; i++)
+        TeXAFrame(win.frames[i]);
+}
+
+// TeXify a node that was previously unTeXified
+function reTeXifyImage(event) {
+    var text = event.currentTarget.innerHTML;
+    var imgElement = createTeXifiedImageElement(text.substring(1, text.length - 1));
+    event.currentTarget.parentNode.replaceChild(imgElement, event.currentTarget);    
+}
+
+// switch an image that was TeXified back to text.
+// removes the ;s so that it doesn't get re-TeXified immediately
+// double-click will re-TeXify is, and also adding the ;s back will do the job.
+function unTeXifyImage(event) {
+    var imgElement = event.currentTarget;
+    
+    var newSpanElement = document.createElement('span');
+    newSpanElement.appendChild(document.createTextNode('[' + imgElement.title + ']'));
+    newSpanElement.addEventListener('dblclick', reTeXifyImage, false);
+    
+    var newOuterSpanElement = document.createElement('span');
+    newOuterSpanElement.appendChild(newSpanElement);
+
+    imgElement.parentNode.replaceChild(newOuterSpanElement, imgElement);    
+}
+
+// simulate the user typing a string
+function simulateTextTyping(target, str) {
+    for (var i = 0; i < str.length; i++) {
+        var keyEvent = document.createEvent("KeyboardEvent");
+        keyEvent.initKeyEvent("keypress", true, true, null, false, false, false, false, 0, str.charCodeAt(i));
+        target.dispatchEvent(keyEvent);
+    }
+}
+
+// handle the keydown event -- if the key was ctrl-shift-; write our signature
+function onKeyDown(event) {
+    // ctrl-shift-;
+    if (event.keyCode == 59 && (event.ctrlKey || event.metaKey) && event.shiftKey) { 
+        // we use typing simulation in order to it to work not only in "regular" html components but also in 
+        // high-tech javascript hacks like gmail and hotmail
+        simulateTextTyping(event.target, signature);
+    }
+}
+
+// TeXifies the world
+function TeXTheWorld() {
+    var startTime = new Date();
+    TeXAFrame(window.top);
+}
+
+if (self == top) { // run only in the top frame. we do our own frame parsing
+    TeXTheWorld(); // since we must handle dynamically created frames
+    setInterval(TeXTheWorld, 3000);
+}
+      



More information about the CIG-COMMITS mailing list