added jsMath files
[lambda.git] / jsMath / extensions / double-click.js
1 /*
2  *  extensions/double-click.js
3  *  
4  *  Part of the jsMath package for mathematics on the web.
5  *
6  *  This file allows users to double click on typeset mathematics
7  *  to view the TeX source for the given expression.  It will be loaded
8  *  automatically when needed, or can be loaded by
9  *  
10  *    jsMath.Extension.Require('double-click');
11  *
12  *  ---------------------------------------------------------------------
13  *
14  *  Copyright 2005-2008 by Davide P. Cervone
15  * 
16  *  Licensed under the Apache License, Version 2.0 (the "License");
17  *  you may not use this file except in compliance with the License.
18  *  You may obtain a copy of the License at
19  * 
20  *      http://www.apache.org/licenses/LICENSE-2.0
21  * 
22  *  Unless required by applicable law or agreed to in writing, software
23  *  distributed under the License is distributed on an "AS IS" BASIS,
24  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25  *  See the License for the specific language governing permissions and
26  *  limitations under the License.
27  */
28
29 /********************************************************************/
30
31 if (jsMath.Click && jsMath.Click.styles) 
32   {jsMath.Click.oldStyles = jsMath.Click.styles}
33
34 jsMath.Add(jsMath.Click,{
35
36   dragging: 0,
37   
38   styles: {
39     // Floating windows for displaying TeX source
40     '#jsMath_float': {
41        position: 'absolute', top: '0px', left: '0px', 'z-index': '101',
42       'max-width': '80%', width: 'auto', height: 'auto',
43        padding: '0px', margin: '0px', 'font-size': '100%'
44     },
45     '#jsMath_float .drag': {
46       'background-color': '#DDDDDD',
47        border: 'outset 1px', padding: '0px', margin: '0px',
48        width: 'auto', height: '12px', 'font-size': '1px'
49     },
50     '#jsMath_float .close': {
51       'background-color': '#E6E6E6',
52        border: 'inset 1px', margin: '1px 2px', padding: '0px',
53        width: '8px', height: '8px'
54     },
55     '#jsMath_float .source': {
56       'background-color': '#E2E2E2',
57        border: 'outset 1px', margin: '0px', padding: '8px 15px',
58        width: 'auto', height: 'auto',
59       'font-family': 'courier, fixed', 'font-size': '90%'
60     }
61   },
62   
63   /*
64    *  Create the hidden DIV used for the tex source window
65    */
66   Init: function () {
67     if (this.oldStyles) {jsMath.Insert(this.styles,this.oldStyles)}
68     jsMath.Setup.Styles(this.styles);
69     this.source = jsMath.Setup.DIV("float",{display:'none'});
70     this.source.innerHTML =
71         '<div class="drag"><div class="close"></div></div>'
72       + '<div class="source"><span></span></div>';
73     this.drag = this.source.firstChild;
74     this.tex  = this.drag.nextSibling.firstChild;
75     this.drag.firstChild.onclick = jsMath.Click.CloseSource;
76     this.drag.onmousedown = jsMath.Click.StartDragging;
77     this.drag.ondragstart = jsMath.Click.False;
78     this.drag.onselectstart = jsMath.Click.False;
79     this.source.onclick = jsMath.Click.CheckClose;
80   },
81   False: function () {return false},
82
83   /*
84    *  Handle a double-click on an equation
85    */
86   DblClick: function (data) {
87     var event = data[0]; var TeX = data[1];
88     var event = jsMath.Click.Event(event);
89
90     var source = jsMath.Click.source
91     var tex = jsMath.Click.tex;
92
93     source.style.visibility = 'hidden';
94     source.style.display = ''; source.style.width = '';
95     source.style.left = ''; source.style.top = '';
96     tex.innerHTML = '';
97
98     TeX = TeX.replace(/^\s+|\s+$/g,'');
99     TeX = TeX.replace(/&/g,'&amp;');
100     TeX = TeX.replace(/</g,'&lt;');
101     TeX = TeX.replace(/>/g,'&gt;');
102     TeX = TeX.replace(/\n/g,'<br/>');
103     tex.innerHTML = TeX;
104
105     var h = source.offsetHeight; var w;
106     if (jsMath.Browser.msieDivWidthBug) {
107       tex.className = 'source';          // Work around MSIE bug where
108       w = tex.offsetWidth + 5;           // DIV's don't collapse to
109       tex.className = '';                // their natural widths
110     } else {
111       w = source.offsetWidth;
112     }
113     w = Math.max(50,Math.min(w,.8*event.W,event.W-40));
114     var x = Math.floor(event.x-w/2); var y = Math.floor(event.y-h/2);
115     x = event.X + Math.max(Math.min(x,event.W-w-20),20);
116     y = event.Y + Math.max(Math.min(y,event.H-h-5),5);
117
118     source.style.left = x+'px'; source.style.top = y+'px';
119     source.style.width = w+'px';
120     source.style.visibility = '';
121     jsMath.Click.left = x + event.X; jsMath.Click.top = y + event.Y;
122     jsMath.Click.w = w; jsMath.Click.h = source.offsetHeight;
123
124     jsMath.Click.DeselectText(x,y);
125     return false;
126   },
127
128   /*
129    *  Get window width, height, and offsets plus
130    *  position of pointer relative to the window
131    */
132   Event: function (event) {
133     var W = jsMath.window.innerWidth  || jsMath.document.body.clientWidth;
134     var H = jsMath.window.innerHeight || jsMath.document.body.clientHeight;
135     var X = jsMath.window.pageXOffset; var Y = jsMath.window.pageYOffset;
136     if (X == null) {
137       X = jsMath.document.body.clientLeft;
138       Y = jsMath.document.body.clientTop;
139     }
140     var x = event.pageX; var y = event.pageY;
141     if (x == null) {
142       x = event.clientX; y = event.clientY;
143       if (jsMath.browser == 'MSIE' && jsMath.document.compatMode == 'CSS1Compat') {
144         X = jsMath.document.documentElement.scrollLeft;
145         Y = jsMath.document.documentElement.scrollTop;
146         W = jsMath.document.documentElement.clientWidth;
147         H = jsMath.document.documentElement.clientHeight;
148       } else {
149         X = jsMath.document.body.scrollLeft;
150         Y = jsMath.document.body.scrollTop;
151       }
152     } else {x -= X; y -= Y}
153
154     return {x: x, y: y, W: W, H: H, X: X, Y: Y};
155   },
156   
157   /*
158    *  Unselect whatever text is selected (since double-clicking
159    *  usually selects something)
160    */
161   DeselectText: function (x,y) {
162     if (jsMath.window.getSelection && jsMath.window.getSelection().removeAllRanges)
163       {jsMath.window.getSelection().removeAllRanges()}
164     else if (jsMath.document.getSelection && jsMath.document.getSelection().removeAllRanges)
165       {jsMath.document.getSelection().removeAllRanges()}
166     else if (jsMath.document.selection && jsMath.document.selection.empty)
167       {jsMath.document.selection.empty()}
168     else {
169       /* Hack to deselect the text in Opera and Safari */
170       if (jsMath.browser == 'MSIE') return;  // don't try it if MISE on Mac
171       jsMath.hiddenTop.innerHTML =
172         '<textarea style="visibility:hidden" rows="1" cols="1">a</textarea>';
173       jsMath.hiddenTop.firstChild.style.position = 'absolute';
174       jsMath.hiddenTop.firstChild.style.left = x+'px';
175       jsMath.hiddenTop.firstChild.style.top  = y+'px';
176       setTimeout(jsMath.Click.SelectHidden,1);
177     }
178   },
179   SelectHidden: function () {
180     jsMath.hiddenTop.firstChild.focus();
181     jsMath.hiddenTop.firstChild.select();
182     jsMath.hiddenTop.innerHTML = '';
183   },
184
185   /*
186    *  Close the TeX source window
187    */
188   CloseSource: function () {
189     jsMath.Click.tex.innerHTML = '';
190     jsMath.Click.source.style.display = 'none';
191     jsMath.Click.source.style.visibility = 'hidden';
192     jsMath.Click.StopDragging();
193     return false;
194   },
195   CheckClose: function (event) {
196     if (!event) {event = jsMath.window.event}
197     if (event.altKey) {jsMath.Click.CloseSource(); return false}
198   },
199   
200   /*
201    *  Set up for dragging the source panel
202    */
203   StartDragging: function (event) {
204     if (!event) {event = jsMath.window.event}
205     if (jsMath.Click.dragging) {jsMath.Click.StopDragging(event)}
206     var event = jsMath.Click.Event(event);
207     jsMath.Click.dragging = 1;
208     jsMath.Click.x = event.x + 2*event.X - jsMath.Click.left;
209     jsMath.Click.y = event.y + 2*event.Y - jsMath.Click.top;
210     jsMath.Click.oldonmousemove = jsMath.document.onmousemove;
211     jsMath.Click.oldonmouseup = jsMath.document.onmouseup;
212     jsMath.document.onmousemove = jsMath.Click.DragSource;
213     jsMath.document.onmouseup = jsMath.Click.StopDragging;
214     return false;
215   },
216   
217   /*
218    *  Stop dragging the source window
219    */
220   StopDragging: function (event) {
221     if (jsMath.Click.dragging) {
222       jsMath.document.onmousemove = jsMath.Click.oldonmousemove;
223       jsMath.document.onmouseup   = jsMath.Click.oldonmouseup;
224       jsMath.Click.oldonmousemove = null;
225       jsMath.Click.oldonmouseup   = null;
226       jsMath.Click.dragging = 0;
227     }
228     return false;
229   },
230   
231   /*
232    *  Move the source window (but stay within the browser window)
233    */
234   DragSource: function (event) {
235     if (!event) {event = jsMath.window.event}
236     if (jsMath.Browser.buttonCheck && !event.button) {return jsMath.Click.StopDragging(event)}
237     event = jsMath.Click.Event(event);
238     var x = event.x + event.X - jsMath.Click.x;
239     var y = event.y + event.Y - jsMath.Click.y;
240     x = Math.max(event.X,Math.min(event.W+event.X-jsMath.Click.w,x));
241     y = Math.max(event.Y,Math.min(event.H+event.Y-jsMath.Click.h,y));
242     jsMath.Click.source.style.left = x + 'px';
243     jsMath.Click.source.style.top  = y + 'px';
244     jsMath.Click.left = x + event.X; jsMath.Click.top = y + event.Y;
245     return false;
246   }
247
248 });
249
250 jsMath.Click.Init();