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);
94 return false;
95 }
97 /** Function to search for the element the user clicked on.
98 *
99 * This function searches for the element with the highest z-order, which encloses the point the user clicked on
100 * and which view box fits entierly into the currently visible part of the slide.
101 *
102 * @param elem Element to start the search from.
103 * @param pnt Point where the user clicked.
104 * @returns The element the user clicked on or null, if no element could be found.
105 */
106 obj.getAdHocViewBbox = function (elem, pnt)
107 {
108 var children = elem.childNodes;
110 for (var counter = 0; counter < children.length; counter++)
111 {
112 if (children[counter].getBBox)
113 {
114 var childPointList = obj.projectRect(children[counter].getBBox(), children[counter].getScreenCTM());
116 var viewBbox = document.documentElement.createSVGRect();
118 viewBbox.x = 0.0;
119 viewBbox.y = 0.0;
120 viewBbox.width = WIDTH;
121 viewBbox.height = HEIGHT;
123 var screenPointList = obj.projectRect(viewBbox, slides[activeSlide]["element"].getScreenCTM());
125 if (obj.pointsWithinRect([pnt], childPointList) && obj.pointsWithinRect(childPointList, screenPointList))
126 return children[counter];
128 child = obj.getAdHocViewBbox(children[counter], pnt);
130 if (child != null)
131 return child;
132 }
133 }
135 return null;
136 }
138 /** Function to project a rectangle using the projection matrix supplied.
139 *
140 * @param rect The rectangle to project.
141 * @param projectionMatrix The projection matrix.
142 * @returns A list of the four corners of the projected rectangle starting from the upper left corner and going counter-clockwise.
143 */
144 obj.projectRect = function (rect, projectionMatrix)
145 {
146 var pntUL = document.documentElement.createSVGPoint();
147 pntUL.x = rect.x;
148 pntUL.y = rect.y;
149 pntUL = pntUL.matrixTransform(projectionMatrix);
151 var pntLL = document.documentElement.createSVGPoint();
152 pntLL.x = rect.x;
153 pntLL.y = rect.y + rect.height;
154 pntLL = pntLL.matrixTransform(projectionMatrix);
156 var pntUR = document.documentElement.createSVGPoint();
157 pntUR.x = rect.x + rect.width;
158 pntUR.y = rect.y;
159 pntUR = pntUR.matrixTransform(projectionMatrix);
161 var pntLR = document.documentElement.createSVGPoint();
162 pntLR.x = rect.x + rect.width;
163 pntLR.y = rect.y + rect.height;
164 pntLR = pntLR.matrixTransform(projectionMatrix);
166 return [pntUL, pntLL, pntUR, pntLR];
167 }
169 /** Function to determine whether all the points supplied in a list are within a rectangle.
170 *
171 * @param pnts List of points to check.
172 * @param pointList List of points representing the four corners of the rectangle.
173 * @return True, if all points are within the rectangle; false, otherwise.
174 */
175 obj.pointsWithinRect = function (pnts, pointList)
176 {
177 var pntUL = pointList[0];
178 var pntLL = pointList[1];
179 var pntUR = pointList[2];
181 var matrixOrig = (new matrixSVG()).fromElements(pntUL.x, pntLL.x, pntUR.x, pntUL.y, pntLL.y, pntUR.y, 1, 1, 1);
182 var matrixProj = (new matrixSVG()).fromElements(0, 0, 1, 0, 1, 0, 1, 1, 1);
184 var matrixProjection = matrixProj.mult(matrixOrig.inv());
186 for (var blockCounter = 0; blockCounter < Math.ceil(pnts.length / 3.0); blockCounter++)
187 {
188 var subPnts = new Array();
190 for (var pntCounter = 0; pntCounter < 3.0; pntCounter++)
191 {
192 if (blockCounter * 3.0 + pntCounter < pnts.length)
193 subPnts[pntCounter] = pnts[blockCounter * 3.0 + pntCounter];
194 else
195 {
196 var tmpPnt = document.documentElement.createSVGPoint();
198 tmpPnt.x = 0.0;
199 tmpPnt.y = 0.0;
201 subPnts[pntCounter] = tmpPnt;
202 }
203 }
205 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);
206 var matrixTrans = matrixProjection.mult(matrixPnt);
208 for (var pntCounter = 0; pntCounter < 3.0; pntCounter++)
209 {
210 if (blockCounter * 3.0 + pntCounter < pnts.length)
211 {
212 if ((pntCounter == 0) && !((matrixTrans.e11 > 0.01) && (matrixTrans.e11 < 0.99) && (matrixTrans.e21 > 0.01) && (matrixTrans.e21 < 0.99)))
213 return false;
214 else if ((pntCounter == 1) && !((matrixTrans.e12 > 0.01) && (matrixTrans.e12 < 0.99) && (matrixTrans.e22 > 0.01) && (matrixTrans.e22 < 0.99)))
215 return false;
216 else if ((pntCounter == 2) && !((matrixTrans.e13 > 0.01) && (matrixTrans.e13 < 0.99) && (matrixTrans.e23 > 0.01) && (matrixTrans.e23 < 0.99)))
217 return false;
218 }
219 }
220 }
222 return true;
223 }
225 /** Event handler for mouse movements.
226 *
227 * @param e Event object.
228 */
229 obj.mousemove = function (e)
230 {
231 obj.click = false;
233 if (!obj.dragging_active || obj.dragging_working)
234 return false;
236 obj.dragging_working = true;
238 var p = obj.getCoords(e);
240 if (slides[activeSlide].viewGroup.transform.baseVal.numberOfItems < 1)
241 {
242 var matrix = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
243 }
244 else
245 {
246 var matrix = (new matrixSVG()).fromSVGMatrix(slides[activeSlide].viewGroup.transform.baseVal.consolidate().matrix);
247 }
249 matrix.e13 += p.x - obj.dragging_last.x;
250 matrix.e23 += p.y - obj.dragging_last.y;
252 slides[activeSlide]["viewGroup"].setAttribute("transform", matrix.toAttribute());
254 obj.dragging_last = p;
255 obj.dragging_working = false;
257 return false;
258 }
260 /** Event handler for mouse down.
261 *
262 * @param e Event object.
263 */
264 obj.mousedown = function (e)
265 {
266 if (obj.dragging_active)
267 return false;
269 var value = 0;
271 if (e.button)
272 value = e.button;
273 else if (e.which)
274 value = e.which;
276 if (value == 1)
277 {
278 obj.dragging_last = obj.getCoords(e);
279 obj.dragging_active = true;
280 obj.click = true;
281 }
283 return false;
284 }
286 /** Event handler for mouse up.
287 *
288 * @param e Event object.
289 */
290 obj.mouseup = function (e)
291 {
292 obj.dragging_active = false;
294 if (obj.click)
295 return obj.mouseclick(e);
296 else
297 return false;
298 }
300 /** Function to get the coordinates of a point corrected for the offset of the viewport.
301 *
302 * @param e Point.
303 * @returns Coordinates of the point corrected for the offset of the viewport.
304 */
305 obj.getCoords = function (e)
306 {
307 var svgPoint = document.documentElement.createSVGPoint();
308 svgPoint.x = e.clientX + window.pageXOffset;
309 svgPoint.y = e.clientY + window.pageYOffset;
311 return svgPoint;
312 }
314 /** Event handler for scrolling.
315 *
316 * @param e Event object.
317 */
318 obj.mousewheel = function(e)
319 {
320 var p = obj.projectCoords(obj.getCoords(e));
322 if (slides[activeSlide].viewGroup.transform.baseVal.numberOfItems < 1)
323 {
324 var matrix = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
325 }
326 else
327 {
328 var matrix = (new matrixSVG()).fromSVGMatrix(slides[activeSlide].viewGroup.transform.baseVal.consolidate().matrix);
329 }
331 if (e.wheelDelta)
332 { // IE Opera
333 delta = e.wheelDelta/120;
334 }
335 else if (e.detail)
336 { // MOZ
337 delta = -e.detail/3;
338 }
340 var widthOld = p.x * matrix.e11 + p.y * matrix.e12;
341 var heightOld = p.x * matrix.e21 + p.y * matrix.e22;
343 matrix.e11 *= (1.0 - delta / 20.0);
344 matrix.e12 *= (1.0 - delta / 20.0);
345 matrix.e21 *= (1.0 - delta / 20.0);
346 matrix.e22 *= (1.0 - delta / 20.0);
348 var widthNew = p.x * matrix.e11 + p.y * matrix.e12;
349 var heightNew = p.x * matrix.e21 + p.y * matrix.e22;
351 matrix.e13 += (widthOld - widthNew);
352 matrix.e23 += (heightOld - heightNew);
354 slides[activeSlide]["viewGroup"].setAttribute("transform", matrix.toAttribute());
356 return false;
357 }
359 /** Function to project a point to screen coordinates.
360 *
361 * @param Point.
362 * @returns The point projected to screen coordinates.
363 */
364 obj.projectCoords = function(pnt)
365 {
366 var matrix = slides[activeSlide]["element"].getScreenCTM();
368 if (slides[activeSlide]["viewGroup"])
369 matrix = slides[activeSlide]["viewGroup"].getScreenCTM();
371 pnt = pnt.matrixTransform(matrix.inverse());
372 return pnt;
373 }
375 /** Function to convert a rectangle into a point matrix.
376 *
377 * The function figures out a rectangle that encloses the rectangle given and has the same width/height ratio as the viewport of the presentation.
378 *
379 * @param rect Rectangle.
380 * @return The upper left, upper right and lower right corner of the rectangle in a point matrix.
381 */
382 obj.rectToMatrix = function(rect)
383 {
384 rectWidth = rect.getBBox().width;
385 rectHeight = rect.getBBox().height;
386 rectX = rect.getBBox().x;
387 rectY = rect.getBBox().y;
388 rectXcorr = 0;
389 rectYcorr = 0;
391 scaleX = WIDTH / rectWidth;
392 scaleY = HEIGHT / rectHeight;
394 if (scaleX > scaleY)
395 {
396 scaleX = scaleY;
397 rectXcorr -= (WIDTH / scaleX - rectWidth) / 2;
398 rectWidth = WIDTH / scaleX;
399 }
400 else
401 {
402 scaleY = scaleX;
403 rectYcorr -= (HEIGHT / scaleY - rectHeight) / 2;
404 rectHeight = HEIGHT / scaleY;
405 }
407 if (rect.transform.baseVal.numberOfItems < 1)
408 {
409 mRectTrans = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
410 }
411 else
412 {
413 mRectTrans = (new matrixSVG()).fromSVGMatrix(rect.transform.baseVal.consolidate().matrix);
414 }
416 newBasePoints = (new matrixSVG()).fromElements(rectX, rectX, rectX, rectY, rectY, rectY, 1, 1, 1);
417 newVectors = (new matrixSVG()).fromElements(rectXcorr, rectXcorr + rectWidth, rectXcorr + rectWidth, rectYcorr, rectYcorr, rectYcorr + rectHeight, 0, 0, 0);
419 return mRectTrans.mult(newBasePoints.add(newVectors));
420 }
422 /** Function to return a transformation matrix from a point matrix.
423 *
424 * @param mPoints The point matrix.
425 * @returns The transformation matrix.
426 */
427 obj.pointMatrixToTransformation = function(mPoints)
428 {
429 mPointsOld = (new matrixSVG()).fromElements(0, WIDTH, WIDTH, 0, 0, HEIGHT, 1, 1, 1);
431 return mPointsOld.mult(mPoints.inv());
432 }
433 }