1 // Copyright 2008, 2009 Hannes Hochreiner
2 // This program is free software: you can redistribute it and/or modify
3 // it under the terms of the GNU General Public License as published by
4 // the Free Software Foundation, either version 3 of the License, or
5 // (at your option) any later version.
6 //
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License for more details.
11 //
12 // You should have received a copy of the GNU General Public License
13 // along with this program. If not, see http://www.gnu.org/licenses/.
15 // Add event listener for initialisation.
16 document.addEventListener("DOMContentLoaded", jessyInk_core_mouseHandler_zoomControl_init, false);
18 /** Initialisation function.
19 *
20 * This function looks for the objects of the appropriate sub-type and hands them to another function that will add the required methods.
21 */
22 function jessyInk_core_mouseHandler_zoomControl_init()
23 {
24 var elems = document.getElementsByTagNameNS("https://launchpad.net/jessyink", "mousehandler");
26 for (var counter = 0; counter < elems.length; counter++)
27 {
28 if (elems[counter].getAttributeNS("https://launchpad.net/jessyink", "subtype") == "jessyInk_core_mouseHandler_zoomControl")
29 jessyInk_core_mouseHandler_zoomControl(elems[counter]);
30 }
31 }
33 /** Function to initialise an object.
34 *
35 * @param obj Object to be initialised.
36 */
37 function jessyInk_core_mouseHandler_zoomControl(obj)
38 {
39 // Last dragging position.
40 obj.dragging_last;
41 // Flag to indicate whether dragging is active currently.
42 obj.dragging_active = false;
43 // Flag to indicate whether dragging is working currently.
44 obj.dragging_working = false;
45 // Flag to indicate whether the user clicked.
46 obj.click = false;
48 /** Function supplying a custom mouse handler.
49 *
50 * @returns A dictionary containing the new mouse handler functions.
51 */
52 obj.getMouseHandler = function ()
53 {
54 var handlerDictio = new Object();
56 handlerDictio[SLIDE_MODE] = new Object();
57 handlerDictio[SLIDE_MODE][MOUSE_DOWN] = obj.mousedown;
58 handlerDictio[SLIDE_MODE][MOUSE_MOVE] = obj.mousemove;
59 handlerDictio[SLIDE_MODE][MOUSE_UP] = obj.mouseup;
60 handlerDictio[SLIDE_MODE][MOUSE_WHEEL] = obj.mousewheel;
62 return handlerDictio;
63 }
65 /** Event handler for mouse clicks.
66 *
67 * @param e Event object.
68 */
69 obj.mouseclick = function (e)
70 {
71 var elem = obj.getAdHocViewBbox(slides[activeSlide]["viewGroup"], obj.getCoords(e));
73 processingEffect = true;
75 effectArray = new Array();
77 effectArray[0] = new Object();
78 effectArray[0]["effect"] = "view";
79 effectArray[0]["dir"] = 1;
80 effectArray[0]["element"] = slides[activeSlide]["viewGroup"];
81 effectArray[0]["options"] = new Object();
82 effectArray[0]["options"]["length"] = 200;
84 if (elem == null)
85 effectArray[0]["options"]["matrixNew"] = (new matrixSVG()).fromSVGElements(1, 0, 0, 1, 0, 0);
86 else
87 effectArray[0]["options"]["matrixNew"] = obj.pointMatrixToTransformation(obj.rectToMatrix(elem)).mult((new matrixSVG()).fromSVGMatrix(slides[activeSlide].viewGroup.getScreenCTM()).inv().mult((new matrixSVG()).fromSVGMatrix(elem.parentNode.getScreenCTM())).inv());
89 transCounter = 0;
90 startTime = (new Date()).getTime();
91 lastFrameTime = null;
92 effect(1);
93 }
95 /** Function to search for the element the user clicked on.
96 *
97 * This function searches for the element with the highest z-order, which encloses the point the user clicked on
98 * and which view box fits entierly into the currently visible part of the slide.
99 *
100 * @param elem Element to start the search from.
101 * @param pnt Point where the user clicked.
102 * @returns The element the user clicked on or null, if no element could be found.
103 */
104 obj.getAdHocViewBbox = function (elem, pnt)
105 {
106 var children = elem.childNodes;
108 for (var counter = 0; counter < children.length; counter++)
109 {
110 if (children[counter].getBBox)
111 {
112 var childPointList = obj.projectRect(children[counter].getBBox(), children[counter].getScreenCTM());
114 var viewBbox = document.documentElement.createSVGRect();
116 viewBbox.x = 0.0;
117 viewBbox.y = 0.0;
118 viewBbox.width = WIDTH;
119 viewBbox.height = HEIGHT;
121 var screenPointList = obj.projectRect(viewBbox, slides[activeSlide]["element"].getScreenCTM());
123 if (obj.pointsWithinRect([pnt], childPointList) && obj.pointsWithinRect(childPointList, screenPointList))
124 return children[counter];
126 child = obj.getAdHocViewBbox(children[counter], pnt);
128 if (child != null)
129 return child;
130 }
131 }
133 return null;
134 }
136 /** Function to project a rectangle using the projection matrix supplied.
137 *
138 * @param rect The rectangle to project.
139 * @param projectionMatrix The projection matrix.
140 * @returns A list of the four corners of the projected rectangle starting from the upper left corner and going counter-clockwise.
141 */
142 obj.projectRect = function (rect, projectionMatrix)
143 {
144 var pntUL = document.documentElement.createSVGPoint();
145 pntUL.x = rect.x;
146 pntUL.y = rect.y;
147 pntUL = pntUL.matrixTransform(projectionMatrix);
149 var pntLL = document.documentElement.createSVGPoint();
150 pntLL.x = rect.x;
151 pntLL.y = rect.y + rect.height;
152 pntLL = pntLL.matrixTransform(projectionMatrix);
154 var pntUR = document.documentElement.createSVGPoint();
155 pntUR.x = rect.x + rect.width;
156 pntUR.y = rect.y;
157 pntUR = pntUR.matrixTransform(projectionMatrix);
159 var pntLR = document.documentElement.createSVGPoint();
160 pntLR.x = rect.x + rect.width;
161 pntLR.y = rect.y + rect.height;
162 pntLR = pntLR.matrixTransform(projectionMatrix);
164 return [pntUL, pntLL, pntUR, pntLR];
165 }
167 /** Function to determine whether all the points supplied in a list are within a rectangle.
168 *
169 * @param pnts List of points to check.
170 * @param pointList List of points representing the four corners of the rectangle.
171 * @return True, if all points are within the rectangle; false, otherwise.
172 */
173 obj.pointsWithinRect = function (pnts, pointList)
174 {
175 var pntUL = pointList[0];
176 var pntLL = pointList[1];
177 var pntUR = pointList[2];
179 var matrixOrig = (new matrixSVG()).fromElements(pntUL.x, pntLL.x, pntUR.x, pntUL.y, pntLL.y, pntUR.y, 1, 1, 1);
180 var matrixProj = (new matrixSVG()).fromElements(0, 0, 1, 0, 1, 0, 1, 1, 1);
182 var matrixProjection = matrixProj.mult(matrixOrig.inv());
184 for (var blockCounter = 0; blockCounter < Math.ceil(pnts.length / 3.0); blockCounter++)
185 {
186 var subPnts = new Array();
188 for (var pntCounter = 0; pntCounter < 3.0; pntCounter++)
189 {
190 if (blockCounter * 3.0 + pntCounter < pnts.length)
191 subPnts[pntCounter] = pnts[blockCounter * 3.0 + pntCounter];
192 else
193 {
194 var tmpPnt = document.documentElement.createSVGPoint();
196 tmpPnt.x = 0.0;
197 tmpPnt.y = 0.0;
199 subPnts[pntCounter] = tmpPnt;
200 }
201 }
203 var matrixPnt = (new matrixSVG).fromElements(subPnts[0].x, subPnts[1].x, subPnts[2].x, subPnts[0].y, subPnts[1].y, subPnts[2].y, 1, 1, 1);
204 var matrixTrans = matrixProjection.mult(matrixPnt);
206 for (var pntCounter = 0; pntCounter < 3.0; pntCounter++)
207 {
208 if (blockCounter * 3.0 + pntCounter < pnts.length)
209 {
210 if ((pntCounter == 0) && !((matrixTrans.e11 > 0.01) && (matrixTrans.e11 < 0.99) && (matrixTrans.e21 > 0.01) && (matrixTrans.e21 < 0.99)))
211 return false;
212 else if ((pntCounter == 1) && !((matrixTrans.e12 > 0.01) && (matrixTrans.e12 < 0.99) && (matrixTrans.e22 > 0.01) && (matrixTrans.e22 < 0.99)))
213 return false;
214 else if ((pntCounter == 2) && !((matrixTrans.e13 > 0.01) && (matrixTrans.e13 < 0.99) && (matrixTrans.e23 > 0.01) && (matrixTrans.e23 < 0.99)))
215 return false;
216 }
217 }
218 }
220 return true;
221 }
223 /** Event handler for mouse movements.
224 *
225 * @param e Event object.
226 */
227 obj.mousemove = function (e)
228 {
229 obj.click = false;
231 if (!obj.dragging_active || obj.dragging_working)
232 return false;
234 obj.dragging_working = true;
236 var p = obj.getCoords(e);
238 if (slides[activeSlide].viewGroup.transform.baseVal.numberOfItems < 1)
239 {
240 var matrix = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
241 }
242 else
243 {
244 var matrix = (new matrixSVG()).fromSVGMatrix(slides[activeSlide].viewGroup.transform.baseVal.consolidate().matrix);
245 }
247 matrix.e13 += p.x - obj.dragging_last.x;
248 matrix.e23 += p.y - obj.dragging_last.y;
250 slides[activeSlide]["viewGroup"].setAttribute("transform", matrix.toAttribute());
252 obj.dragging_last = p;
253 obj.dragging_working = false;
255 return false;
256 }
258 /** Event handler for mouse down.
259 *
260 * @param e Event object.
261 */
262 obj.mousedown = function (e)
263 {
264 if (obj.dragging_active)
265 return false;
267 var value = 0;
269 if (e.button)
270 value = e.button;
271 else if (e.which)
272 value = e.which;
274 if (value == 1)
275 {
276 obj.dragging_last = obj.getCoords(e);
277 obj.dragging_active = true;
278 obj.click = true;
279 }
281 return false;
282 }
284 /** Event handler for mouse up.
285 *
286 * @param e Event object.
287 */
288 obj.mouseup = function (e)
289 {
290 obj.dragging_active = false;
292 if (obj.click)
293 return obj.mouseclick(e);
294 else
295 return false;
296 }
298 /** Function to get the coordinates of a point corrected for the offset of the viewport.
299 *
300 * @param e Point.
301 * @returns Coordinates of the point corrected for the offset of the viewport.
302 */
303 obj.getCoords = function (e)
304 {
305 var svgPoint = document.documentElement.createSVGPoint();
306 svgPoint.x = e.clientX + window.pageXOffset;
307 svgPoint.y = e.clientY + window.pageYOffset;
309 // The following is needed for Google Chrome, but causes problems
310 // with Firefox, as viewport is not implemented in Firefox 3.5.
311 try
312 {
313 svgPoint.x += document.rootElement.viewport.x;
314 svgPoint.y += document.rootElement.viewport.y;
315 }
316 catch (ex)
317 {
318 }
320 return svgPoint;
321 }
323 /** Event handler for scrolling.
324 *
325 * @param e Event object.
326 */
327 obj.mousewheel = function(e)
328 {
329 var p = obj.projectCoords(obj.getCoords(e));
331 if (slides[activeSlide].viewGroup.transform.baseVal.numberOfItems < 1)
332 {
333 var matrix = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
334 }
335 else
336 {
337 var matrix = (new matrixSVG()).fromSVGMatrix(slides[activeSlide].viewGroup.transform.baseVal.consolidate().matrix);
338 }
340 if (e.wheelDelta)
341 { // IE Opera
342 delta = e.wheelDelta/120;
343 }
344 else if (e.detail)
345 { // MOZ
346 delta = -e.detail/3;
347 }
349 var widthOld = p.x * matrix.e11 + p.y * matrix.e12;
350 var heightOld = p.x * matrix.e21 + p.y * matrix.e22;
352 matrix.e11 *= (1.0 - delta / 20.0);
353 matrix.e12 *= (1.0 - delta / 20.0);
354 matrix.e21 *= (1.0 - delta / 20.0);
355 matrix.e22 *= (1.0 - delta / 20.0);
357 var widthNew = p.x * matrix.e11 + p.y * matrix.e12;
358 var heightNew = p.x * matrix.e21 + p.y * matrix.e22;
360 matrix.e13 += (widthOld - widthNew);
361 matrix.e23 += (heightOld - heightNew);
363 slides[activeSlide]["viewGroup"].setAttribute("transform", matrix.toAttribute());
365 return false;
366 }
368 /** Function to project a point to screen coordinates.
369 *
370 * @param Point.
371 * @returns The point projected to screen coordinates.
372 */
373 obj.projectCoords = function(pnt)
374 {
375 var matrix = slides[activeSlide]["element"].getScreenCTM();
377 if (slides[activeSlide]["viewGroup"])
378 matrix = slides[activeSlide]["viewGroup"].getScreenCTM();
380 pnt = pnt.matrixTransform(matrix.inverse());
381 return pnt;
382 }
384 /** Function to convert a rectangle into a point matrix.
385 *
386 * The function figures out a rectangle that encloses the rectangle given and has the same width/height ratio as the viewport of the presentation.
387 *
388 * @param rect Rectangle.
389 * @return The upper left, upper right and lower right corner of the rectangle in a point matrix.
390 */
391 obj.rectToMatrix = function(rect)
392 {
393 rectWidth = rect.getBBox().width;
394 rectHeight = rect.getBBox().height;
395 rectX = rect.getBBox().x;
396 rectY = rect.getBBox().y;
397 rectXcorr = 0;
398 rectYcorr = 0;
400 scaleX = WIDTH / rectWidth;
401 scaleY = HEIGHT / rectHeight;
403 if (scaleX > scaleY)
404 {
405 scaleX = scaleY;
406 rectXcorr -= (WIDTH / scaleX - rectWidth) / 2;
407 rectWidth = WIDTH / scaleX;
408 }
409 else
410 {
411 scaleY = scaleX;
412 rectYcorr -= (HEIGHT / scaleY - rectHeight) / 2;
413 rectHeight = HEIGHT / scaleY;
414 }
416 if (rect.transform.baseVal.numberOfItems < 1)
417 {
418 mRectTrans = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
419 }
420 else
421 {
422 mRectTrans = (new matrixSVG()).fromSVGMatrix(rect.transform.baseVal.consolidate().matrix);
423 }
425 newBasePoints = (new matrixSVG()).fromElements(rectX, rectX, rectX, rectY, rectY, rectY, 1, 1, 1);
426 newVectors = (new matrixSVG()).fromElements(rectXcorr, rectXcorr + rectWidth, rectXcorr + rectWidth, rectYcorr, rectYcorr, rectYcorr + rectHeight, 0, 0, 0);
428 return mRectTrans.mult(newBasePoints.add(newVectors));
429 }
431 /** Function to return a transformation matrix from a point matrix.
432 *
433 * @param mPoints The point matrix.
434 * @returns The transformation matrix.
435 */
436 obj.pointMatrixToTransformation = function(mPoints)
437 {
438 mPointsOld = (new matrixSVG()).fromElements(0, WIDTH, WIDTH, 0, 0, HEIGHT, 1, 1, 1);
440 return mPointsOld.mult(mPoints.inv());
441 }
442 }