4 * Part of the jsMath package for mathematics on the web.
6 * This file is a plugin that checks if a page contains any math
7 * that must be processed by jsMath, and only loads jsMath.js
10 * You can control the items to look for via the variables
12 * jsMath.Autoload.findTeXstrings
13 * jsMath.Autoload.findLaTeXstrings
14 * jsMath.Autoload.findCustomStrings
15 * jsMath.Autoload.findCustomSettings
17 * which control whether to look for TeX strings that will be converted
18 * by jsMath.ConvertTeX(), or LaTeX strings that will be converted by
19 * jsMath.ConvertLaTeX(). By default, the first is true and the second
20 * and third are false. The findCustomStrings can be used to specify your
21 * own delimiters for in-line and display mathematics, e.g.
23 * jsMath.Autoload.findCustomStrings = [
24 * '[math],'[/math]', // start and end in-line math
25 * '[display]','[/display]' // start and end display math
28 * Finally, findCustomSettings can be set to an object reference whose
29 * name:value pairs control the individual search settings for tex2math.
30 * (See the plugins/tex2math.js file for more details).
32 * If any math strings are found, jsMath.js will be loaded automatically,
33 * but not loaded otherwise. If any of the last four are set and TeX math
34 * strings are found, then plugins/tex2ath.js will be loaded
35 * automatically. jsMath.Autoload.needsJsMath will be set to true or
36 * false depending on whether jsMath needed to be loaded.
38 * The value of jsMath.Autoload.checkElement controls the element to be
39 * searched by the autoload plug-in. If unset, the complete document will
40 * be searched. If set to a string, the element with that name will be
41 * searched. If set to a DOM object, that object and its children will
44 * Finally, there are two additional parameters that control files to
45 * be loaded after jsMath.js, should it be needed. These are
47 * jsMath.Autoload.loadFonts
48 * jsMath.Autoload.loadFiles
50 * If jsMath.js is loaded, the fonts contained in the loadFonts array
51 * will be loaded, and the JavaScript files listed in the loadFiles array
52 * will be run. Relative URL's are loaded based from the URL containing
55 * The autoload plugin can be loaded in the document HEAD or in the BODY.
56 * If it is loaded in the HEAD, you will need to call jsMath.Autoload.Check()
57 * at the end of the BODY (say in the window.onload handler) in order to
58 * get it to check the page for math that needs to be tagged, otherwise load
59 * the file at the bottom of the BODY and it will run the check automatically.
61 * You can call jsMath.Autoload.Run() after the check has been performed
62 * in order to call the appropriate tex2math routines for the given Autoload
63 * settings. You can call jsMath.Autoload.Run() even when jsMath isn't loaded.
65 * ---------------------------------------------------------------------
67 * Copyright 2004-2006 by Davide P. Cervone
69 * Licensed under the Apache License, Version 2.0 (the "License");
70 * you may not use this file except in compliance with the License.
71 * You may obtain a copy of the License at
73 * http://www.apache.org/licenses/LICENSE-2.0
75 * Unless required by applicable law or agreed to in writing, software
76 * distributed under the License is distributed on an "AS IS" BASIS,
77 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
78 * See the License for the specific language governing permissions and
79 * limitations under the License.
82 /*************************************************************************/
85 * Make sure jsMath.Autoload is available
87 if (!window.jsMath) {window.jsMath = {}}
88 if (jsMath.Autoload == null) {jsMath.Autoload = {}}
89 jsMath.Add = function (dst,src) {for (var id in src) {dst[id] = src[id]}};
90 jsMath.document = document; // tex2math needs this
92 jsMath.Add(jsMath.Autoload,{
96 request: null, // XMLHttpRequest object (if we can get it)
97 iframe: null, // the hidden iframe (if not)
98 operaXMLHttpRequestBug: (window.opera != null), // is Opera browser
101 * Get XMLHttpRequest object, if possible, and look up the URL root
102 * (MSIE can't use xmlReuest to load local files, so avoid that)
106 if (window.XMLHttpRequest) {
107 try {this.request = new XMLHttpRequest} catch (err) {}
108 // MSIE and FireFox3 can't use xmlRequest on local files,
109 // but we don't have jsMath.browser yet to tell, so use this check
110 if (this.request && window.location.protocol == "file:") {
112 this.request.open("GET",jsMath.Autoload.root+"plugins/autoload.js",false);
113 this.request.send(null);
116 // Firefox3 has window.postMessage for inter-window communication.
117 // It can be used to handle the new file:// security model,
118 // so set up the listener.
119 if (window.postMessage && window.addEventListener) {
121 window.addEventListener("message",jsMath.Autoload.Post.Listener,false);
126 if (!this.request && window.ActiveXObject && !this.mustPost) {
127 var xml = ["MSXML2.XMLHTTP.6.0","MSXML2.XMLHTTP.5.0","MSXML2.XMLHTTP.4.0",
128 "MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"];
129 for (var i = 0; i < xml.length && !this.request; i++) {
130 try {this.request = new ActiveXObject(xml[i])} catch (err) {}
136 * Load an external JavaScript file
138 Load: function (url) {
139 if (this.request && !(this.operaXMLHttpRequestBug && url == 'jsMath.js')) {
140 setTimeout(function () {jsMath.Autoload.Script.xmlLoad(url)},1);
147 * Load an external JavaScript file via XMLHttpRequest
149 xmlLoad: function (url) {
151 this.request.open("GET",jsMath.Autoload.root+url,false);
152 this.request.send(null);
154 throw Error("autoload: can't load the file '"+url+"'\n"
155 + "Message: "+err.message);
157 if (this.request.status && this.request.status >= 400) {
158 throw Error("autoload: can't load the file '"+url+"'\n"
159 + "Error status: "+this.request.status);
161 window.eval(this.request.responseText);
166 * Load an external JavaScript file via jsMath-autoload.html
168 startLoad: function (url) {
169 this.iframe = document.createElement('iframe');
170 this.iframe.style.visibility = 'hidden';
171 this.iframe.style.position = 'absolute';
172 this.iframe.style.width = '0px';
173 this.iframe.style.height = '0px';
174 if (document.body.firstChild) {
175 document.body.insertBefore(this.iframe,document.body.firstChild);
177 document.body.appendChild(this.iframe);
179 this.url = url; setTimeout('jsMath.Autoload.Script.setURL()',100);
181 endLoad: function () {setTimeout('jsMath.Autoload.Script.AfterLoad()',1)},
184 * Use location.replace() to avoid browsers placing the file in
185 * the history (and messing up the BACK button action). Not
186 * completely effective in Firefox 1.0.x. Safari won't handle
187 * replace() if the document is local (not sure if that's really
188 * the issue, but that's the only time I see it).
190 setURL: function () {
192 this.iframe.src = jsMath.Autoload.Post.startLoad(this.url,this.iframe);
194 var url = jsMath.Autoload.root+"jsMath-autoload.html";
195 var doc = this.iframe.contentDocument;
196 if (!doc && this.iframe.contentWindow) {doc = this.iframe.contentWindow.document}
197 if (navigator.vendor == "Apple Computer, Inc." &&
198 document.location.protocol == 'file:') {doc = null}
199 if (doc) {doc.location.replace(url)} else {this.iframe.src = url}
204 * Queue items that need to be postponed until jsMath has run
207 Push: function (name,data) {this.queue[this.queue.length] = [name,data]},
208 RunStack: function () {
209 if (this.tex2math) {jsMath.Autoload.Check2(); return}
210 for (var i = 0; i < this.queue.length; i++) {
211 var name = this.queue[i][0];
212 var data = this.queue[i][1];
213 if (data.length == 1) {jsMath[name](data[0])}
214 else {jsMath[name](data[0],data[1],data[2],data[3])}
219 AfterLoad: function () {jsMath.Autoload.Script.RunStack()},
222 * Look up the jsMath root directory, if it is not already supplied
225 if (jsMath.Autoload.root) return;
226 var script = document.getElementsByTagName('script');
228 for (var i = 0; i < script.length; i++) {
229 var src = script[i].src;
230 if (src && src.match('(^|/|\\\\)plugins/autoload.js$')) {
231 jsMath.Autoload.root = src.replace(/plugins\/autoload.js$/,'');
241 * Handle window.postMessage() events in Firefox3
244 window: null, // iframe we are ing to
246 Listener: function (event) {
247 if (event.source != jsMath.Autoload.Post.window) return;
248 var domain = event.origin.replace(/^file:\/\//,'');
249 var ddomain = document.domain.replace(/^file:\/\//,'');
250 if (domain == null || domain == "" || domain == "null") {domain = "localhost"}
251 if (ddomain == null || ddomain == "" || ddomain == "null") {ddomain = "localhost"}
252 if (domain != ddomain || event.data.substr(0,6) != "jsMAL:") return;
253 var type = event.data.substr(6,3).replace(/ /g,'');
254 var message = event.data.substr(10);
255 if (jsMath.Autoload.Post.Commands[type]) (jsMath.Autoload.Post.Commands[type])(message);
260 * Commands that can be performed by the listener
263 SCR: function (message) {window.eval(message)},
264 ERR: function (message) {jsMath.Autoload.Script.endLoad()},
265 END: function (message) {jsMath.Autoload.Script.endLoad()}
268 startLoad: function (url,iframe) {
269 this.window = iframe.contentWindow;
270 return jsMath.Autoload.root+"jsMath-loader-post.html?autoload="+url;
273 endLoad: function () {
279 /**************************************************************/
282 * Load tex2math first (so we can call its search functions
283 * to look to see if anything needs to be turned into math)
284 * if it is needed, otherwise go on to the second check.
287 if (this.checked) return; this.checked = 1;
288 if ((this.findTeXstrings || this.findLaTeXstrings ||
289 this.findCustomStrings || this.findCustomSettings) &&
290 (!jsMath.tex2math || !jsMath.tex2math.loaded)) {
291 this.Script.tex2math = 1;
292 this.Script.Load('plugins/tex2math.js');
294 if (!jsMath.tex2math) {jsMath.tex2math = {}}
298 ReCheck: function () {
299 if (jsMath.loaded) return;
302 this.Script.queue = [];
307 * Once tex2math is loaded, use it to check for math that
308 * needs to be tagged for jsMath, and load jsMath if it is needed
310 Check2: function () {
311 this.Script.tex2math = 0; this.needsJsMath = 0;
312 if (this.checkElement == null) {this.checkElement = null}
314 if (this.findTeXstrings) {jsMath.tex2math.ConvertTeX(this.checkElement)}
315 if (this.findLaTeXstrings) {jsMath.tex2math.ConvertLaTeX(this.checkElement)}
316 if (this.findCustomSettings) {jsMath.tex2math.Convert(this.checkElement,this.findCustomSettings)}
317 if (this.findCustomStrings) {
318 var s = this.findCustomStrings;
319 jsMath.tex2math.CustomSearch(s[0],s[1],s[2],s[3]);
320 jsMath.tex2math.ConvertCustom(this.checkElement);
323 this.needsJsMath = this.areMathElements(this.checkElement);
324 if (this.needsJsMath) {
327 jsMath.Process = function () {};
328 jsMath.ProcessBeforeShowing = function () {};
329 jsMath.ProcessElement = function () {};
330 jsMath.ConvertTeX = function () {};
331 jsMath.ConvertTeX2 = function () {};
332 jsMath.ConvertLaTeX = function () {};
333 jsMath.ConvertCustom = function () {};
334 jsMath.CustomSearch = function () {};
335 jsMath.Macro = function () {};
336 jsMath.Synchronize = function (code,data) {
337 if (typeof(code) == 'string') {eval(code)} else {code(data)}
339 jsMath.Autoload.Script.RunStack(); // perform pending commands
340 jsMath.Autoload.setMessage();
345 * A callback used in the tex2math searches to signal that
346 * some math has been found.
348 tex2mathCallback: function () {
349 jsMath.Autoload.needsJsMath = 1;
354 * jsMath.Autoload.Run() is now longer needed
356 Run: function (data) {},
359 * Look to see if there are SPAN or DIV elements of class "math".
361 areMathElements: function (obj) {
362 if (!obj) {obj = document}
363 if (typeof(obj) == 'string') {obj = document.getElementById(obj)}
364 if (!obj.getElementsByTagName) {return false}
365 var math = obj.getElementsByTagName('div');
366 for (var k = 0; k < math.length; k++)
367 {if (math[k].className.match(/(^| )math( |$)/)) {return true}}
368 math = obj.getElementsByTagName('span');
369 for (var k = 0; k < math.length; k++)
370 {if (math[k].className.match(/(^| )math( |$)/)) {return true}}
375 * When math tags are found, load the jsMath.js file,
376 * and afterward, load any auxiliary files or fonts,
377 * and then do any pending commands.
379 LoadJsMath: function () {
380 if (this.loading) return;
381 if (jsMath.loaded) {this.afterLoad(); return}
384 this.setMessage('Loading jsMath...');
385 this.Script.AfterLoad = this.afterLoad;
386 this.Script.Load('jsMath.js');
388 alert("Can't determine URL for jsMath.js");
391 afterLoad: function () {
392 jsMath.Autoload.loading = 0;
394 // Handle MSIE bug where jsMath.window both is and is not the actual window
396 if (jsMath.tex2math.window) {jsMath.tex2math.window.jsMath = jsMath}
397 if (jsMath.browser == 'MSIE') {window.onscroll = jsMath.window.onscroll};
398 var fonts = jsMath.Autoload.loadFonts;
400 if (typeof(fonts) != 'object') {fonts = [fonts]}
401 for (var i = 0; i < fonts.length; i++) {jsMath.Font.Load(fonts[i])}
403 var files = jsMath.Autoload.loadFiles;
405 if (typeof(files) != 'object') {files = [files]}
406 for (var i = 0; i < files.length; i++) {jsMath.Setup.Script(files[i])}
408 var macros = jsMath.Autoload.macros;
410 for (var id in macros) {
411 if (typeof(macros[id]) == 'string') {
412 jsMath.Macro(id,macros[id]);
414 jsMath.Macro(id,macros[id][0],macros[id][1]);
418 jsMath.Synchronize(function () {jsMath.Autoload.Script.RunStack()});
419 jsMath.Autoload.setMessage();
423 * Display a message in a small box at the bottom of the screen
425 setMessage: function (message) {
427 this.div = document.createElement('div');
428 if (!document.body.hasChildNodes) {document.body.appendChild(this.div)}
429 else {document.body.insertBefore(this.div,document.body.firstChild)}
431 position:'fixed', bottom:'1px', left:'2px',
432 backgroundColor:'#E6E6E6', border:'solid 1px #959595',
433 margin:'0px', padding:'1px 8px', zIndex:102,
434 color:'black', fontSize:'75%', width:'auto'
436 for (var id in style) {this.div.style[id] = style[id]}
437 this.div.id = "jsMath_message";
438 this.div.appendChild(jsMath.document.createTextNode(message));
439 } else if (this.div) {
440 this.div.firstChild.nodeValue = "";
441 this.div.style.visibility = 'hidden';
446 * Queue these so we can do them after jsMath has been loaded
449 Process: function (data) {jsMath.Autoload.Script.Push('Process',[data])},
450 ProcessBeforeShowing: function (data) {jsMath.Autoload.Script.Push('ProcessBeforeShowing',[data])},
451 ProcessElement: function (data) {jsMath.Autoload.Script.Push('ProcessElement',[data])},
452 ConvertTeX: function (data) {jsMath.Autoload.Script.Push('ConvertTeX',[data])},
453 ConvertTeX2: function (data) {jsMath.Autoload.Script.Push('ConvertTeX2',[data])},
454 ConvertLaTeX: function (data) {jsMath.Autoload.Script.Push('ConvertLaTeX',[data])},
455 ConvertCustom: function (data) {jsMath.Autoload.Script.Push('ConvertCustom',[data])},
456 CustomSearch: function (d1,d2,d3,d4) {jsMath.Autoload.Script.Push('CustomSearch',[d1,d2,d3,d4])},
457 Synchronize: function (data) {jsMath.Autoload.Script.Push('Synchronize',[data])},
458 Macro: function (cs,def,params) {jsMath.Autoload.Script.Push('Macro',[cs,def,params])}
461 InitStubs: function () {jsMath.Add(jsMath,jsMath.Autoload.stubs)}
469 if (jsMath.Autoload.findTeXstrings == null) {jsMath.Autoload.findTeXstrings = 0}
470 if (jsMath.Autoload.findLaTeXstrings == null) {jsMath.Autoload.findLaTeXstrings = 0}
472 jsMath.Autoload.Script.Init();
473 jsMath.Autoload.InitStubs();
474 if (document.body && !jsMath.Autoload.delayCheck) {jsMath.Autoload.Check()}