Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / LayoutTests / svg / carto.net / resources / mapApp.js
blobccdaef2f812e4a47edbc5e78f0663532d4439776
1 /*
2 Scripts for creating SVG apps, converting clientX/Y to viewBox coordinates
3 and for displaying tooltips
5 Copyright (C) <2006> <Andreas Neumann>
6 Version 1.2, 2006-10-06
7 neumann@karto.baug.ethz.ch
8 http://www.carto.net/
9 http://www.carto.net/neumann/
11 Credits:
12 * thanks to Kevin Lindsey for his many examples and providing the ViewBox class
14 ----
16 Documentation: http://www.carto.net/papers/svg/gui/mapApp/
18 ----
20 current version: 1.2
22 version history:
23 1.0 (2006-06-01)
24 initial version
25 Was programmed earlier, but now documented
27 1.1 (2006-06-15)
28 added properties this.innerWidth, this.innerHeight (wrapper around different behaviour of viewers), added method ".adjustViewBox()" to adjust the viewBox to the this.innerWidth and this.innerHeight of the UA's window
30 1.2 (2006-10-06)
31 added two new constructor parameter "adjustVBonWindowResize" and "resizeCallbackFunction". If the first parameter is set to true, the viewBox of this mapApp will always adjust itself to the innerWidth and innerHeight of the browser window or frame containing the SVG application
32 the "resizeCallbackFunction" can be of type "function", later potentially also of type "object". This function is called every time the mapApp was resized (browser/UA window was resized). It isn't called the first time when the mapApp was initialized
33 added a new way to detect resize events in Firefox which didn't implement the SVGResize event so far
34 added several arrays to hold GUI references
36 -------
39 This ECMA script library is free software; you can redistribute it and/or
40 modify it under the terms of the GNU Lesser General Public
41 License as published by the Free Software Foundation; either
42 version 2.1 of the License, or (at your option) any later version.
44 This library is distributed in the hope that it will be useful,
45 but WITHOUT ANY WARRANTY; without even the implied warranty of
46 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
47 Lesser General Public License for more details.
49 You should have received a copy of the GNU Lesser General Public
50 License along with this library (lesser_gpl.txt); if not, write to the Free Software
51 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
53 ----
55 original document site: http://www.carto.net/papers/svg/gui/mapApp/
56 Please contact the author in case you want to use code or ideas commercially.
57 If you use this code, please include this copyright header, the included full
58 LGPL 2.1 text and read the terms provided in the LGPL 2.1 license
59 (http://www.gnu.org/copyleft/lesser.txt)
61 -------------------------------
63 Please report bugs and send improvements to neumann@karto.baug.ethz.ch
64 If you use this code, please link to the original (http://www.carto.net/papers/svg/gui/mapApp/)
65 somewhere in the source-code-comment or the "about" of your project and give credits, thanks!
69 //this mapApp object helps to convert clientX/clientY coordinates to the coordinates of the group where the element is within
70 //normally one can just use .getScreenCTM(), but ASV3 does not implement it, 95% of the code in this function is for ASV3!!!
71 //credits: Kevin Lindsey for his example at http://www.kevlindev.com/gui/utilities/viewbox/ViewBox.js
72 function mapApp(adjustVBonWindowResize,resizeCallbackFunction) {
73 this.adjustVBonWindowResize = adjustVBonWindowResize;
74 this.resizeCallbackFunction = resizeCallbackFunction;
75 this.initialized = false;
76 if (!document.documentElement.getScreenCTM) {
77 //add zoom and pan event event to document element
78 //this part is only required for viewers not supporting document.documentElement.getScreenCTM() (e.g. ASV3)
79 document.documentElement.addEventListener("SVGScroll",this,false);
80 document.documentElement.addEventListener("SVGZoom",this,false);
82 //add SVGResize event, note that because FF does not yet support the SVGResize event, there is a workaround
83 try {
84 //browsers with native SVG support
85 window.addEventListener("resize",this,false);
87 catch(er) {
88 //SVG UAs, like Batik and ASV/Iex
89 document.documentElement.addEventListener("SVGResize",this,false);
91 //determine the browser main version
92 this.navigator = "Batik";
93 if (window.navigator) {
94 if (window.navigator.appName.match(/Adobe/gi)) {
95 this.navigator = "Adobe";
97 if (window.navigator.appName.match(/Netscape/gi)) {
98 this.navigator = "Mozilla";
100 if (window.navigator.appName.match(/Opera/gi)) {
101 this.navigator = "Opera";
103 if (window.navigator.appName.match(/Safari/gi)) {
104 this.navigator = "Safari";
107 //we need to call this once to initialize this.innerWidth/this.innerHeight
108 this.resetFactors();
109 //per default, tooltips are disabled
110 this.tooltipsEnabled = false;
111 //create new arrays to hold GUI references
112 this.Windows = new Array();
113 this.checkBoxes = new Array();
114 this.radioButtonGroups = new Array();
115 this.tabgroups = new Array();
116 this.textboxes = new Array();
117 this.buttons = new Array();
118 this.selectionLists = new Array();
119 this.comboboxes = new Array();
120 this.sliders = new Array();
121 this.scrollbars = new Array();
122 this.colourPickers = new Array();
125 mapApp.prototype.handleEvent = function(evt) {
126 if (evt.type == "SVGResize" || evt.type == "resize" || evt.type == "SVGScroll" || evt.type == "SVGZoom") {
127 this.resetFactors();
129 if ((evt.type == "mouseover" || evt.type == "mouseout" || evt.type == "mousemove") && this.tooltipsEnabled) {
130 this.displayTooltip(evt);
134 mapApp.prototype.resetFactors = function() {
135 //set inner width and height
136 if (window.innerWidth) {
137 this.innerWidth = window.innerWidth;
138 this.innerHeight = window.innerHeight;
140 else {
141 var viewPort = document.documentElement.viewport;
142 this.innerWidth = viewPort.width;
143 this.innerHeight = viewPort.height;
145 if (this.adjustVBonWindowResize) {
146 this.adjustViewBox();
148 //this code is for ASV3
149 if (!document.documentElement.getScreenCTM) {
150 var svgroot = document.documentElement;
151 this.viewBox = new ViewBox(svgroot);
152 var trans = svgroot.currentTranslate;
153 var scale = svgroot.currentScale;
154 this.m = this.viewBox.getTM();
155 //undo effects of zoom and pan
156 this.m = this.m.scale( 1/scale );
157 this.m = this.m.translate(-trans.x, -trans.y);
159 if (this.resizeCallbackFunction && this.initialized) {
160 if (typeof(this.resizeCallbackFunction) == "function") {
161 this.resizeCallbackFunction();
164 this.initialized = true;
167 //set viewBox of document.documentElement to innerWidth and innerHeight
168 mapApp.prototype.adjustViewBox = function() {
169 document.documentElement.setAttributeNS(null,"viewBox","0 0 "+this.innerWidth+" "+this.innerHeight);
172 mapApp.prototype.calcCoord = function(evt,ctmNode) {
173 var svgPoint = document.documentElement.createSVGPoint();
174 svgPoint.x = evt.clientX;
175 svgPoint.y = evt.clientY;
176 if (!document.documentElement.getScreenCTM) {
177 //undo the effect of transformations
178 if (ctmNode) {
179 var matrix = getTransformToRootElement(ctmNode);
181 else {
182 var matrix = getTransformToRootElement(evt.target);
184 svgPoint = svgPoint.matrixTransform(matrix.inverse().multiply(this.m));
186 else {
187 //case getScreenCTM is available
188 if (ctmNode) {
189 var matrix = ctmNode.getScreenCTM();
191 else {
192 var matrix = evt.target.getScreenCTM();
194 svgPoint = svgPoint.matrixTransform(matrix.inverse());
196 //undo the effect of viewBox and zoomin/scroll
197 return svgPoint;
200 mapApp.prototype.calcInvCoord = function(svgPoint) {
201 if (!document.documentElement.getScreenCTM) {
202 var matrix = getTransformToRootElement(document.documentElement);
204 else {
205 var matrix = document.documentElement.getScreenCTM();
207 svgPoint = svgPoint.matrixTransform(matrix);
208 return svgPoint;
211 //initialize tootlips
212 mapApp.prototype.initTooltips = function(groupId,tooltipTextAttribs,tooltipRectAttribs,xOffset,yOffset,padding) {
213 var nrArguments = 6;
214 if (arguments.length == nrArguments) {
215 this.toolTipGroup = document.getElementById(groupId);
216 this.tooltipTextAttribs = tooltipTextAttribs;
217 if (!this.tooltipTextAttribs["font-size"]) {
218 this.tooltipTextAttribs["font-size"] = 12;
220 this.tooltipRectAttribs = tooltipRectAttribs;
221 this.xOffset = xOffset;
222 this.yOffset = yOffset;
223 this.padding = padding;
224 if (!this.toolTipGroup) {
225 alert("Error: could not find tooltip group with id '"+groupId+"'. Please specify a correct tooltip parent group id!");
227 else {
228 //set tooltip group to invisible
229 this.toolTipGroup.setAttributeNS(null,"visibility","hidden");
230 this.toolTipGroup.setAttributeNS(null,"pointer-events","none");
231 this.tooltipsEnabled = true;
232 //create tooltip text element
233 this.tooltipText = document.createElementNS(svgNS,"text");
234 for (var attrib in this.tooltipTextAttribs) {
235 value = this.tooltipTextAttribs[attrib];
236 if (attrib == "font-size") {
237 value += "px";
239 this.tooltipText.setAttributeNS(null,attrib,value);
241 //create textnode
242 var textNode = document.createTextNode("Tooltip");
243 this.tooltipText.appendChild(textNode);
244 this.toolTipGroup.appendChild(this.tooltipText);
245 var bbox = this.tooltipText.getBBox();
246 this.tooltipRect = document.createElementNS(svgNS,"rect");
247 this.tooltipRect.setAttributeNS(null,"x",bbox.x-this.padding);
248 this.tooltipRect.setAttributeNS(null,"y",bbox.y-this.padding);
249 this.tooltipRect.setAttributeNS(null,"width",bbox.width+this.padding*2);
250 this.tooltipRect.setAttributeNS(null,"height",bbox.height+this.padding*2);
251 for (var attrib in this.tooltipRectAttribs) {
252 this.tooltipRect.setAttributeNS(null,attrib,this.tooltipRectAttribs[attrib]);
254 this.toolTipGroup.insertBefore(this.tooltipRect,this.tooltipText);
257 else {
258 alert("Error in method 'initTooltips': wrong nr of arguments! You have to pass over "+nrArguments+" parameters.");
262 mapApp.prototype.addTooltip = function(tooltipNode,tooltipTextvalue,followmouse,checkForUpdates,targetOrCurrentTarget,childAttrib) {
263 var nrArguments = 6;
264 if (arguments.length == nrArguments) {
265 //get reference
266 if (typeof(tooltipNode) == "string") {
267 tooltipNode = document.getElementById(tooltipNode);
269 //check if tooltip attribute present or create one
270 if (!tooltipNode.hasAttributeNS(attribNS,"tooltip")) {
271 if (tooltipTextvalue) {
272 tooltipNode.setAttributeNS(attribNS,"tooltip",tooltipTextvalue);
274 else {
275 tooltipNode.setAttributeNS(attribNS,"tooltip","Tooltip");
278 //see if we need updates
279 if (checkForUpdates) {
280 tooltipNode.setAttributeNS(attribNS,"tooltipUpdates","true");
282 //see if we have to use evt.target
283 if (targetOrCurrentTarget == "target") {
284 tooltipNode.setAttributeNS(attribNS,"tooltipParent","true");
286 //add childAttrib
287 if (childAttrib) {
288 tooltipNode.setAttributeNS(attribNS,"tooltipAttrib",childAttrib);
290 //add event listeners
291 tooltipNode.addEventListener("mouseover",this,false);
292 tooltipNode.addEventListener("mouseout",this,false);
293 if (followmouse) {
294 tooltipNode.addEventListener("mousemove",this,false);
297 else {
298 alert("Error in method 'addTooltip()': wrong nr of arguments! You have to pass over "+nrArguments+" parameters.");
302 mapApp.prototype.displayTooltip = function(evt) {
303 var curEl = evt.currentTarget;
304 var coords = this.calcCoord(evt,this.toolTipGroup.parentNode);
305 if (evt.type == "mouseover") {
306 this.toolTipGroup.setAttributeNS(null,"visibility","visible");
307 this.toolTipGroup.setAttributeNS(null,"transform","translate("+(coords.x+this.xOffset)+","+(coords.y+this.yOffset)+")");
308 this.updateTooltip(evt);
310 if (evt.type == "mouseout") {
311 this.toolTipGroup.setAttributeNS(null,"visibility","hidden");
313 if (evt.type == "mousemove") {
314 this.toolTipGroup.setAttributeNS(null,"transform","translate("+(coords.x+this.xOffset)+","+(coords.y+this.yOffset)+")");
315 if (curEl.hasAttributeNS(attribNS,"tooltipUpdates")) {
316 this.updateTooltip(evt);
321 mapApp.prototype.updateTooltip = function(evt) {
322 var el = evt.currentTarget;
323 if (el.hasAttributeNS(attribNS,"tooltipParent")) {
324 var attribName = "tooltip";
325 if (el.hasAttributeNS(attribNS,"tooltipAttrib")) {
326 attribName = el.getAttributeNS(attribNS,"tooltipAttrib");
328 el = evt.target;
329 var myText = el.getAttributeNS(attribNS,attribName);
331 else {
332 var myText = el.getAttributeNS(attribNS,"tooltip");
334 var textArray = myText.split("\\n");
335 while(this.tooltipText.hasChildNodes()) {
336 this.tooltipText.removeChild(this.tooltipText.lastChild);
338 for (var i=0;i<textArray.length;i++) {
339 var tspanEl = document.createElementNS(svgNS,"tspan");
340 tspanEl.setAttributeNS(null,"x",0);
341 var dy = this.tooltipTextAttribs["font-size"];
342 if (i == 0) {
343 var dy = 0;
345 tspanEl.setAttributeNS(null,"dy",dy);
346 var textNode = document.createTextNode(textArray[i]);
347 tspanEl.appendChild(textNode);
348 this.tooltipText.appendChild(tspanEl);
350 // set text and rect attributes
351 var bbox = this.tooltipText.getBBox();
352 this.tooltipRect.setAttributeNS(null,"x",bbox.x-this.padding);
353 this.tooltipRect.setAttributeNS(null,"y",bbox.y-this.padding);
354 this.tooltipRect.setAttributeNS(null,"width",bbox.width+this.padding*2);
355 this.tooltipRect.setAttributeNS(null,"height",bbox.height+this.padding*2);
358 mapApp.prototype.enableTooltips = function() {
359 this.tooltipsEnabled = true;
362 mapApp.prototype.disableTooltips = function() {
363 this.tooltipsEnabled = false;
364 this.toolTipGroup.setAttributeNS(null,"visibility","hidden");
367 /*************************************************************************/
369 /*****
371 * ViewBox.js
373 * copyright 2002, Kevin Lindsey
375 *****/
377 ViewBox.VERSION = "1.0";
380 /*****
382 * constructor
384 *****/
385 function ViewBox(svgNode) {
386 if ( arguments.length > 0 ) {
387 this.init(svgNode);
392 /*****
394 * init
396 *****/
397 ViewBox.prototype.init = function(svgNode) {
398 var viewBox = svgNode.getAttributeNS(null, "viewBox");
399 var preserveAspectRatio = svgNode.getAttributeNS(null, "preserveAspectRatio");
401 if ( viewBox != "" ) {
402 var params = viewBox.split(/\s*,\s*|\s+/);
404 this.x = parseFloat( params[0] );
405 this.y = parseFloat( params[1] );
406 this.width = parseFloat( params[2] );
407 this.height = parseFloat( params[3] );
408 } else {
409 this.x = 0;
410 this.y = 0;
411 this.width = innerWidth;
412 this.height = innerHeight;
415 this.setPAR(preserveAspectRatio);
416 var dummy = this.getTM(); //to initialize this.windowWidth/this.windowHeight
420 /*****
422 * getTM
424 *****/
425 ViewBox.prototype.getTM = function() {
426 var svgRoot = document.documentElement;
427 var matrix = document.documentElement.createSVGMatrix();
428 //case width/height contains percent
429 this.windowWidth = svgRoot.getAttributeNS(null,"width");
430 if (this.windowWidth.match(/%/) || this.windowWidth == null) {
431 if (this.windowWidth == null) {
432 if (window.innerWidth) {
433 this.windowWidth = window.innerWidth;
435 else {
436 this.windowWidth = svgRoot.viewport.width;
439 else {
440 var factor = parseFloat(this.windowWidth.replace(/%/,""))/100;
441 if (window.innerWidth) {
442 this.windowWidth = window.innerWidth * factor;
444 else {
445 this.windowWidth = svgRoot.viewport.width * factor;
449 else {
450 this.windowWidth = parseFloat(this.windowWidth);
452 this.windowHeight = svgRoot.getAttributeNS(null,"height");
453 if (this.windowHeight.match(/%/) || this.windowHeight == null) {
454 if (this.windowHeight == null) {
455 if (window.innerHeight) {
456 this.windowHeight = window.innerHeight;
458 else {
459 this.windowHeight = svgRoot.viewport.height;
462 else {
463 var factor = parseFloat(this.windowHeight.replace(/%/,""))/100;
464 if (window.innerHeight) {
465 this.windowHeight = window.innerHeight * factor;
467 else {
468 this.windowHeight = svgRoot.viewport.height * factor;
472 else {
473 this.windowHeight = parseFloat(this.windowHeight);
475 var x_ratio = this.width / this.windowWidth;
476 var y_ratio = this.height / this.windowHeight;
478 matrix = matrix.translate(this.x, this.y);
479 if ( this.alignX == "none" ) {
480 matrix = matrix.scaleNonUniform( x_ratio, y_ratio );
481 } else {
482 if ( x_ratio < y_ratio && this.meetOrSlice == "meet" ||
483 x_ratio > y_ratio && this.meetOrSlice == "slice" )
485 var x_trans = 0;
486 var x_diff = this.windowWidth*y_ratio - this.width;
488 if ( this.alignX == "Mid" )
489 x_trans = -x_diff/2;
490 else if ( this.alignX == "Max" )
491 x_trans = -x_diff;
493 matrix = matrix.translate(x_trans, 0);
494 matrix = matrix.scale( y_ratio );
496 else if ( x_ratio > y_ratio && this.meetOrSlice == "meet" ||
497 x_ratio < y_ratio && this.meetOrSlice == "slice" )
499 var y_trans = 0;
500 var y_diff = this.windowHeight*x_ratio - this.height;
502 if ( this.alignY == "Mid" )
503 y_trans = -y_diff/2;
504 else if ( this.alignY == "Max" )
505 y_trans = -y_diff;
507 matrix = matrix.translate(0, y_trans);
508 matrix = matrix.scale( x_ratio );
510 else
512 // x_ratio == y_ratio so, there is no need to translate
513 // We can scale by either value
514 matrix = matrix.scale( x_ratio );
518 return matrix;
522 /*****
524 * get/set methods
526 *****/
528 /*****
530 * setPAR
532 *****/
533 ViewBox.prototype.setPAR = function(PAR) {
534 // NOTE: This function needs to use default values when encountering
535 // unrecognized values
536 if ( PAR ) {
537 var params = PAR.split(/\s+/);
538 var align = params[0];
540 if ( align == "none" ) {
541 this.alignX = "none";
542 this.alignY = "none";
543 } else {
544 this.alignX = align.substring(1,4);
545 this.alignY = align.substring(5,9);
548 if ( params.length == 2 ) {
549 this.meetOrSlice = params[1];
550 } else {
551 this.meetOrSlice = "meet";
553 } else {
554 this.align = "xMidYMid";
555 this.alignX = "Mid";
556 this.alignY = "Mid";
557 this.meetOrSlice = "meet";