2 Scripts to create interactive comboboxes in SVG using ECMA script
3 Copyright (C) <2006> <Andreas Neumann>
4 Version 1.2.1, 2006-10-03
5 neumann@karto.baug.ethz.ch
7 http://www.carto.net/neumann/
13 current version: 1.2.1
20 decoupled styling from this file, fixes event handling so people can scroll outside scroller if mouse is down (events now added to root element)
23 changed internally "groupId" to "id" to make it consistent with other GUI elements, introduced "parentId" as a new constructor parameter for the same reason, introduced new methods "resize()" and "moveTo()"
26 corrected a slight bug regarding DOM hierarchy of created elements
31 This ECMA script library is free software; you can redistribute it and/or
32 modify it under the terms of the GNU Lesser General Public
33 License as published by the Free Software Foundation; either
34 version 2.1 of the License, or (at your option) any later version.
36 This library is distributed in the hope that it will be useful,
37 but WITHOUT ANY WARRANTY; without even the implied warranty of
38 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39 Lesser General Public License for more details.
41 You should have received a copy of the GNU Lesser General Public
42 License along with this library (lesser_gpl.txt); if not, write to the Free Software
43 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
47 original document site: http://www.carto.net/papers/svg/gui/combobox/
48 Please contact the author in case you want to use code or ideas commercially.
49 If you use this code, please include this copyright header, the included full
50 LGPL 2.1 text and read the terms provided in the LGPL 2.1 license
51 (http://www.gnu.org/copyleft/lesser.txt)
53 -------------------------------
55 If you wish, you can modify parameters (such as "look and feel") in the function "combobox" (constructor).
56 You can adapt colors, fonts, cellpaddings, etc.
58 -------------------------------
60 Please report bugs and send improvements to neumann@karto.baug.ethz.ch
61 If you use this control, please link to the original (http://www.carto.net/papers/svg/gui/combobox/)
62 somewhere in the source-code-comment or the "about" of your project and give credits, thanks!
66 function combobox(id,parentNode,elementsArray,width,xOffset,yOffset,cellHeight,textPadding,heightNrElements,multiple,offsetValue,textStyles,boxStyles,scrollbarStyles,smallrectStyles,highlightStyles,triangleStyles,functionToCall) {
68 var createCombobox= true;
69 if (arguments.length == nrArguments) {
70 //get constructor variables
72 this.parentNode = parentNode;
73 this.elementsArray = elementsArray;
75 this.xOffset = xOffset;
76 this.yOffset = yOffset;
77 this.cellHeight = cellHeight; //cellHeight in viewBox coordinates
78 this.textPadding = textPadding; //this is relative to the left of the cell
79 this.heightNrElements = heightNrElements;
80 //check if heightNrElements is bigger than elements in the array
81 if (this.heightNrElements > this.elementsArray.length) {
82 this.heightNrElements = this.elementsArray.length;
84 this.multiple = multiple;
85 this.curLowerIndex = offsetValue;
86 //check if curLowIndex is within range
87 if (this.curLowerIndex > (this.elementsArray.length - this.heightNrElements)) {
88 this.curLowerIndex = this.elementsArray.length - this.heightNrElements;
90 this.textStyles = textStyles; //array of literals containing presentation attributes for text
91 if (!this.textStyles["font-size"]) {
92 this.textStyles["font-size"] = "11";
94 this.boxStyles = boxStyles; //array of literals containing presentation attributes for the sel box
95 if (!this.boxStyles["fill"]) {
96 this.boxStyles["fill"] = "white"; //this fill value is also used for boxes under indiv. text elements (unselected status)
98 if (!this.boxStyles["stroke"]) {
99 this.boxStyles["stroke"] = "dimgray";
101 if (!this.boxStyles["stroke-width"]) {
102 this.boxStyles["stroke-width"] = 1;
104 this.scrollbarStyles = scrollbarStyles; //array of literals containing presentation attributes for the scrollbar
105 this.smallrectStyles = smallrectStyles; //array of literals containing presentation attributes for the small rectangle next to selectbox
106 this.highlightStyles = highlightStyles; //array of literals containing presentation attributes for the highlighted rectangles
107 this.triangleStyles = triangleStyles; //array of literals containing presentation attributes for the triangle in the selectionList, also applies to fill of scroller
108 this.functionToCall = functionToCall;
110 //calculate other values
111 this.triangleFourth = Math.round(this.cellHeight / 4); //this is only used internally
112 this.textPadding = textPadding; //this is relative to the left of the cell
113 this.textPaddingVertical = this.cellHeight - (this.cellHeight - this.textStyles["font-size"]) * 0.7; //this is relative to the top of the cell
114 this.scrollerMinHeight = this.cellHeight * 0.5; //minimal height of the scroller rect
117 this.scrollStep = 0; //y-value to go for one element
118 this.scrollerHeight = 0; //height of dragable scroller bar
119 this.scrollActive = false; //determines if scrolling per up/down button is active
120 this.panY = false; //stores the y value of event
121 this.scrollCumulus = 0; //if value is less then a scrollstep we need to accumulate scroll values
122 this.scrollDir = ""; //later holds "up" and "down"
123 this.exists = true; //true means it exists, gets value false if method "removeSelectionList" is called
124 this.mouseDownStatus = false; //status that specifies if mouse is down or up on individual elements
125 this.pressedKeys = ""; //stores key events (pressed char values)
126 this.keyTimer = undefined;
127 this.svgRootHasEventListener = false; //status variable to define if svg root has key and mouseover listener or not
130 createCombobox = false;
131 alert("Error ("+id+"): wrong nr of arguments! You have to pass over "+nrArguments+" parameters.");
133 if (createCombobox) {
135 this.timer = new Timer(this); //a Timer instance for calling the functionToCall
136 this.timerMs = 200; //a constant of this object that is used in conjunction with the timer - functionToCall is called after 200 ms
139 var result = this.testParent();
141 //this.parentGroup = document.getElementById(this.id);
142 this.createOrUpdateCombobox();
145 alert("could not create or reference 'parentNode' of combobox with id '"+this.id+"'");
149 alert("Could not create combobox with id '"+id+"' due to errors in the constructor parameters");
153 //test if parent group exists or create a new group at the end of the file
154 combobox.prototype.testParent = function() {
155 //test if of type object
156 var nodeValid = false;
157 if (typeof(this.parentNode) == "object") {
158 if (this.parentNode.nodeName == "svg" || this.parentNode.nodeName == "g") {
159 this.parentGroup = this.parentNode;
163 else if (typeof(this.parentNode) == "string") {
164 //first test if Windows group exists
165 if (!document.getElementById(this.parentNode)) {
166 this.parentGroup = document.createElementNS(svgNS,"g");
167 this.parentGroup.setAttributeNS(null,"id",this.parentNode);
168 document.documentElement.appendChild(this.parentGroup);
172 this.parentGroup = document.getElementById(this.parentNode);
179 combobox.prototype.createOrUpdateCombobox = function() {
180 //check if there are more elements in the array than heightNrElements
181 if (this.heightNrElements < this.elementsArray.length) {
182 nrSelections = this.heightNrElements;
185 nrSelections = this.elementsArray.length;
188 var comboboxHeight = this.cellHeight * nrSelections;
189 //rectangle below (only fill, stroke is added at a separate rect)
191 if (!this.rectBelowBox) {
192 this.rectBelowBox = document.createElementNS(svgNS,"rect");
193 this.rectBelowBox.setAttributeNS(null,"stroke","none");
196 this.rectBelowBox.setAttributeNS(null,"x",this.xOffset);
197 this.rectBelowBox.setAttributeNS(null,"y",this.yOffset);
198 this.rectBelowBox.setAttributeNS(null,"width",this.width - this.cellHeight);
199 this.rectBelowBox.setAttributeNS(null,"height",comboboxHeight);
200 for (var attrib in this.boxStyles) {
201 if (attrib != "stroke" && attrib != "stroke-width") {
202 this.rectBelowBox.setAttributeNS(null,attrib,this.boxStyles[attrib]);
206 this.parentGroup.appendChild(this.rectBelowBox);
208 //create temporary group for indiv. combo elements
209 var comboElementsGroupCreated = false;
210 if (this.comboElementsGroup) {
211 this.parentGroup.removeChild(this.comboElementsGroup);
212 comboElementsGroupCreated = true;
214 this.comboElementsGroup = document.createElementNS(svgNS,"g");
215 if (comboElementsGroupCreated) {
216 this.parentGroup.insertBefore(this.comboElementsGroup,this.rectAboveBox);
219 this.parentGroup.appendChild(this.comboElementsGroup);
222 //create rectangle above combobox (only stroke)
224 if (!this.rectAboveBox) {
225 this.rectAboveBox = document.createElementNS(svgNS,"rect");
226 this.rectAboveBox.setAttributeNS(null,"fill","none");
229 this.rectAboveBox.setAttributeNS(null,"x",this.xOffset);
230 this.rectAboveBox.setAttributeNS(null,"y",this.yOffset);
231 this.rectAboveBox.setAttributeNS(null,"width",this.width - this.cellHeight);
232 this.rectAboveBox.setAttributeNS(null,"height",comboboxHeight);
233 for (var attrib in this.boxStyles) {
234 if (attrib != "fill") {
235 this.rectAboveBox.setAttributeNS(null,attrib,this.boxStyles[attrib]);
239 this.parentGroup.appendChild(this.rectAboveBox);
241 //build textElements from array
242 //hold currentSelection Index to unroll list at new offset
243 for (var i=0;i<nrSelections;i++) {
244 //add rectangles to capture events
245 var node = document.createElementNS(svgNS,"rect");
246 node.setAttributeNS(null,"x",this.xOffset + this.textPadding / 2);
247 node.setAttributeNS(null,"y",this.yOffset + this.cellHeight * i);
248 node.setAttributeNS(null,"width",this.width - this.cellHeight - this.textPadding);
249 node.setAttributeNS(null,"height",this.cellHeight);
250 if (this.elementsArray[this.curLowerIndex + i].value) {
251 for (var attrib in this.highlightStyles) {
252 node.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
256 node.setAttributeNS(null,"fill",this.boxStyles["fill"]);
258 node.setAttributeNS(null,"id","selHighlightSelection_" + this.id + "_" + (i + this.curLowerIndex));
260 node.addEventListener("mousedown", this, false);
261 node.addEventListener("mouseover", this, false);
262 node.addEventListener("mouseup", this, false);
263 this.comboElementsGroup.appendChild(node);
265 var node = document.createElementNS(svgNS,"text");
266 node.setAttributeNS(null,"id","selTexts_" + this.id + "_" + (i + this.curLowerIndex));
267 node.setAttributeNS(null,"x",this.xOffset + this.textPadding);
268 node.setAttributeNS(null,"y",this.yOffset + this.textPaddingVertical + this.cellHeight * i);
269 node.setAttributeNS(null,"pointer-events","none");
271 for (var attrib in this.textStyles) {
272 value = this.textStyles[attrib];
273 if (attrib == "font-size") {
276 node.setAttributeNS(null,attrib,value);
278 var selectionText = document.createTextNode(this.elementsArray[this.curLowerIndex + i].key);
279 node.appendChild(selectionText);
280 this.comboElementsGroup.appendChild(node);
284 if (this.heightNrElements < this.elementsArray.length) {
285 //calculate scrollstep
286 this.scrollerHeight = (this.heightNrElements / this.elementsArray.length) * (comboboxHeight - 2 * this.cellHeight);
287 if (this.scrollerHeight < this.scrollerMinHeight) {
288 this.scrollerHeight = this.scrollerMinHeight;
290 this.scrollStep = (comboboxHeight - 2 * this.cellHeight - this.scrollerHeight) / (this.elementsArray.length - this.heightNrElements);
293 if (!this.scrollbarRect) {
294 this.scrollbarRect = document.createElementNS(svgNS,"rect");
295 this.scrollbarRect.addEventListener("mousedown", this, false);
296 this.scrollbarRect.setAttributeNS(null,"id","selScrollbarRect_"+this.id);
299 this.scrollbarRect.setAttributeNS(null,"x",this.xOffset + this.width - this.cellHeight);
300 this.scrollbarRect.setAttributeNS(null,"y",this.yOffset + this.cellHeight);
301 this.scrollbarRect.setAttributeNS(null,"width",this.cellHeight);
302 this.scrollbarRect.setAttributeNS(null,"height",comboboxHeight - this.cellHeight * 2);
303 for (var attrib in this.scrollbarStyles) {
304 this.scrollbarRect.setAttributeNS(null,attrib,this.scrollbarStyles[attrib]);
307 this.parentGroup.appendChild(this.scrollbarRect);
311 if (!this.scrollUpperRect) {
312 this.scrollUpperRect = document.createElementNS(svgNS,"rect");
313 this.scrollUpperRect.setAttributeNS(null,"id","selScrollUpperRect_"+this.id);
314 this.scrollUpperRect.addEventListener("mousedown", this, false);
315 this.scrollUpperRect.addEventListener("mouseup", this, false);
318 this.scrollUpperRect.setAttributeNS(null,"x",this.xOffset + this.width - this.cellHeight);
319 this.scrollUpperRect.setAttributeNS(null,"y",this.yOffset);
320 this.scrollUpperRect.setAttributeNS(null,"width",this.cellHeight);
321 this.scrollUpperRect.setAttributeNS(null,"height",this.cellHeight);
322 for (var attrib in this.smallrectStyles) {
323 this.scrollUpperRect.setAttributeNS(null,attrib,this.smallrectStyles[attrib]);
326 this.parentGroup.appendChild(this.scrollUpperRect);
330 if (!this.upperTriangle) {
331 this.upperTriangle = document.createElementNS(svgNS,"path");
332 this.upperTriangle.setAttributeNS(null,"pointer-events","none");
335 var myPath = "M"+(this.xOffset + this.width - 3 * this.triangleFourth)+" "+(this.yOffset + 3 * this.triangleFourth)+" L"+(this.xOffset + this.width - this.triangleFourth)+" "+(this.yOffset + 3 * this.triangleFourth)+" L"+(this.xOffset + this.width - 2 * this.triangleFourth)+" "+(this.yOffset + this.triangleFourth)+" Z";
336 this.upperTriangle.setAttributeNS(null,"d",myPath);
337 for (var attrib in this.triangleStyles) {
338 this.upperTriangle.setAttributeNS(null,attrib,this.triangleStyles[attrib]);
341 this.parentGroup.appendChild(this.upperTriangle);
345 if (!this.scrollLowerRect) {
346 this.scrollLowerRect = document.createElementNS(svgNS,"rect");
347 this.scrollLowerRect.setAttributeNS(null,"id","selScrollLowerRect_" + this.id);
348 this.scrollLowerRect.addEventListener("mousedown", this, false);
349 this.scrollLowerRect.addEventListener("mouseup", this, false);
352 this.scrollLowerRect.setAttributeNS(null,"x",this.xOffset + this.width - this.cellHeight);
353 this.scrollLowerRect.setAttributeNS(null,"y",this.yOffset + comboboxHeight - this.cellHeight);
354 this.scrollLowerRect.setAttributeNS(null,"width",this.cellHeight);
355 this.scrollLowerRect.setAttributeNS(null,"height",this.cellHeight);
356 for (var attrib in this.smallrectStyles) {
357 this.scrollLowerRect.setAttributeNS(null,attrib,this.smallrectStyles[attrib]);
360 this.parentGroup.appendChild(this.scrollLowerRect);
364 if (!this.lowerTriangle) {
365 this.lowerTriangle = document.createElementNS(svgNS,"path");
366 this.lowerTriangle.setAttributeNS(null,"pointer-events","none");
369 var myPath = "M"+(this.xOffset + this.width - 3 * this.triangleFourth)+" "+(this.yOffset + comboboxHeight - this.cellHeight + this.triangleFourth)+" L"+(this.xOffset + this.width - this.triangleFourth)+" "+(this.yOffset + comboboxHeight - this.cellHeight + this.triangleFourth)+" L"+(this.xOffset + this.width - 2 * this.triangleFourth)+" "+(this.yOffset + comboboxHeight - this.cellHeight + 3 * this.triangleFourth)+" Z";
370 this.lowerTriangle.setAttributeNS(null,"d",myPath);
371 for (var attrib in this.triangleStyles) {
372 this.lowerTriangle.setAttributeNS(null,attrib,this.triangleStyles[attrib]);
375 this.parentGroup.appendChild(this.lowerTriangle);
379 if (!this.scroller) {
380 this.scroller = document.createElementNS(svgNS,"rect");
381 this.scroller.setAttributeNS(null,"pointer-events","none");
382 this.scroller.setAttributeNS(null,"id","selScroller_"+this.id);
385 this.scroller.setAttributeNS(null,"x",this.xOffset + this.width - this.cellHeight);
386 this.scroller.setAttributeNS(null,"y",this.yOffset + this.cellHeight + this.scrollStep * this.curLowerIndex);
387 this.scroller.setAttributeNS(null,"width",this.cellHeight);
388 this.scroller.setAttributeNS(null,"height",this.scrollerHeight);
389 for (var attrib in this.smallrectStyles) {
390 this.scroller.setAttributeNS(null,attrib,this.smallrectStyles[attrib]);
393 this.parentGroup.appendChild(this.scroller);
398 combobox.prototype.handleEvent = function(evt) {
399 var el = evt.currentTarget;
400 var callerId = el.getAttributeNS(null,"id");
401 var myRegExp = new RegExp(this.id);
402 if (evt.type == "mouseover") {
403 if (myRegExp.test(callerId)) {
404 if (callerId.match(/\bselHighlightSelection_/)) {
405 if (this.mouseDownStatus) {
406 this.selectOrUnselect(evt);
407 evt.stopPropagation();
410 if (!this.svgRootHasEventListener) {
411 document.documentElement.addEventListener("mouseover",this,false);
412 document.documentElement.addEventListener("keypress",this,false);
413 this.svgRootHasEventListener = true;
418 if (!myRegExp.test(evt.target.getAttributeNS(null,"id"))) {
419 //case that the event comes from the documentRoot element
420 //but not from the combobox itself
421 this.mouseDownStatus = false;
422 document.documentElement.removeEventListener("mouseover",this,false);
423 document.documentElement.removeEventListener("keypress",this,false);
424 this.svgRootHasEventListener = false;
427 if (evt.type == "mousedown") {
428 if (myRegExp.test(callerId)) {
429 if (callerId.match(/\bselHighlightSelection_/)) {
430 this.selectOrUnselect(evt);
431 this.mouseDownStatus = true;
432 document.documentElement.addEventListener("mouseup",this,false);
433 evt.stopPropagation();
436 if (callerId.match(/\bselScrollUpperRect_|\bselScrollLowerRect/)) {
437 this.scrollDir = "down";
438 this.scrollActive = true;
439 document.documentElement.addEventListener("mouseup",this,false);
440 if (callerId.match(/UpperRect/)) {
441 this.scrollDir = "up";
443 this.scroll(this.scrollDir,1,true); //1 is one element to scroll, true says that we should move scrollbar
444 this.timer.setTimeout("scrollPerButton",400)
446 if (callerId.match(/\bselScrollbarRect_/)) {
447 this.scrollBarMove(evt);
450 if (evt.type == "mouseup") {
451 if (myRegExp.test(callerId)) {
452 if (callerId.match(/\bselHighlightSelection_/)) {
453 this.mouseDownStatus = false;
454 if (this.panStatus) {
455 this.scrollBarMove(evt);
457 if (this.scrollActive) {
458 this.scrollActive = false;
459 document.documentElement.removeEventListener("mouseup",this,false);
461 evt.stopPropagation();
463 if (callerId.match(/\bselScrollUpperRect_|\bselScrollLowerRect/)) {
464 this.scrollActive = false;
467 if (el.nodeName == "svg") {
468 if (this.panStatus) {
470 this.scrollBarMove(evt);
472 if (this.scrollActive) {
473 this.scrollActive = false;
474 document.documentElement.removeEventListener("mouseup",this,false);
478 if (evt.type == "mousemove") {
479 if (el.nodeName == "svg") {
480 this.scrollBarMove(evt);
482 } //add keypress event
483 if (evt.type == "keypress") {
485 var character = String.fromCharCode(evt.charCode).toLowerCase();
488 //case opera and others
489 var character = String.fromCharCode(evt.keyCode).toLowerCase();
490 if (evt.keyCode == 0 || evt.keyCode == 16 || evt.keyCode == 17 || evt.keyCode == 18) {
496 this.pressedKeys += character;
498 if (character.length > 0) {
500 this.timer.clearTimeout(this.keyTimer);
502 this.keyTimer = this.timer.setTimeout("resetPressedKeys",1000);
508 combobox.prototype.moveTo = function(moveX,moveY) {
509 this.xOffset = moveX;
510 this.yOffset = moveY;
511 this.createOrUpdateCombobox();
514 combobox.prototype.resize = function(newWidth) {
515 this.width = newWidth;
516 this.createOrUpdateCombobox();
519 combobox.prototype.resetPressedKeys = function() {
520 this.keyTimer = undefined;
521 this.pressedKeys = "";
524 combobox.prototype.selectOrUnselect = function(evt) {
525 var mySelEl = evt.target;
526 var result = mySelEl.getAttributeNS(null,"id").split("_");
527 var idNr = result[2]; //the numerical id in the array
529 //multiple selections are allowed
530 if (evt.type == "mousedown") {
532 if (this.elementsArray[idNr].value) {
533 mySelEl.setAttributeNS(null,"fill",this.boxStyles["fill"]);
534 if (mySelEl.hasAttributeNS(null,"fill-opacity")) {
535 mySelEl.removeAttributeNS(null,"fill-opacity");
537 this.elementsArray[idNr].value = false;
540 for (var attrib in this.highlightStyles) {
541 mySelEl.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
543 this.elementsArray[idNr].value = true;
548 var selectedArray = new Array();
549 for (var i=0;i<this.elementsArray.length;i++) {
550 //see how many and where elements are already selected
551 if (this.elementsArray[i].value) {
552 selectedArray.push(i);
555 if (selectedArray.length == 0) {
556 //if no element was selected we select the given element
557 for (var attrib in this.highlightStyles) {
558 mySelEl.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
560 this.elementsArray[idNr].value = true;
563 //now we need to calculate the distances to other
564 var distance = this.elementsArray.length;
565 var nearestElement = 0;
566 for (var i=0;i<selectedArray.length;i++) {
567 var tempDist = Math.abs(idNr - selectedArray[i]);
568 if (tempDist < distance) {
570 nearestElement = selectedArray[i];
573 if (nearestElement < idNr) {
574 var lowerIndex = nearestElement;
575 var upperIndex = idNr;
578 var lowerIndex = idNr;
579 var upperIndex = nearestElement;
581 for (var i=lowerIndex;i<=upperIndex;i++) {
582 var mySelEl = document.getElementById("selHighlightSelection_" + this.id + "_" + i);
583 for (var attrib in this.highlightStyles) {
584 mySelEl.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
586 this.elementsArray[i].value = true;
591 this.deselectAll(false);
592 for (var attrib in this.highlightStyles) {
593 mySelEl.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
595 this.elementsArray[idNr].value = true;
599 if (evt.type == "mouseover") {
600 for (var attrib in this.highlightStyles) {
601 mySelEl.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
603 this.elementsArray[idNr].value = true;
607 //only one selection is allowed at the time
608 this.deselectAll(false);
609 for (var attrib in this.highlightStyles) {
610 mySelEl.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
612 this.elementsArray[idNr].value = true;
614 if (this.functionToCall) {
615 this.timer.setTimeout("fireFunction",this.timerMs);
619 combobox.prototype.deselectAll = function(fireFunction) {
620 for (var i=0;i<this.elementsArray.length;i++) {
621 if (this.elementsArray[i].value) {
622 this.elementsArray[i].value = false;
623 if (i >= this.curLowerIndex && i < (this.curLowerIndex + this.heightNrElements)) {
624 var mySelEl = document.getElementById("selHighlightSelection_" + this.id + "_" + i);
625 mySelEl.setAttributeNS(null,"fill",this.boxStyles["fill"]);
626 if (mySelEl.hasAttributeNS(null,"fill-opacity")) {
627 mySelEl.removeAttributeNS(null,"fill-opacity");
633 this.timer.setTimeout("fireFunction",this.timerMs);
637 combobox.prototype.selectAll = function(fireFunction) {
638 for (var i=0;i<this.elementsArray.length;i++) {
639 if (!this.elementsArray[i].value) {
640 this.elementsArray[i].value = true;
641 if (i >= this.curLowerIndex && i < (this.curLowerIndex + this.heightNrElements)) {
642 var mySelEl = document.getElementById("selHighlightSelection_" + this.id + "_" + i);
643 for (var attrib in this.highlightStyles) {
644 mySelEl.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
650 this.timer.setTimeout("fireFunction",this.timerMs);
654 combobox.prototype.invertSelection = function(fireFunction) {
655 for (var i=0;i<this.elementsArray.length;i++) {
656 if (this.elementsArray[i].value) {
657 this.elementsArray[i].value = false;
658 if (i >= this.curLowerIndex && i < (this.curLowerIndex + this.heightNrElements)) {
659 var mySelEl = document.getElementById("selHighlightSelection_" + this.id + "_" + i);
660 mySelEl.setAttributeNS(null,"fill",this.boxStyles["fill"]);
661 if (mySelEl.hasAttributeNS(null,"fill-opacity")) {
662 mySelEl.removeAttributeNS(null,"fill-opacity");
667 this.elementsArray[i].value = true;
668 if (i >= this.curLowerIndex && i < (this.curLowerIndex + this.heightNrElements)) {
669 var mySelEl = document.getElementById("selHighlightSelection_" + this.id + "_" + i);
670 for (var attrib in this.highlightStyles) {
671 mySelEl.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
677 this.timer.setTimeout("fireFunction",this.timerMs);
681 combobox.prototype.fireFunction = function() {
682 var selectedArray = new Array();
683 var selectedArrayIndizes = new Array();
684 for (var i=0;i<this.elementsArray.length;i++) {
685 //see how many and where elements are already selected
686 if (this.elementsArray[i].value) {
687 selectedArray.push(this.elementsArray[i].key);
688 selectedArrayIndizes.push(i);
691 if (typeof(this.functionToCall) == "function") {
692 this.functionToCall(this.id,selectedArray,selectedArrayIndizes);
694 if (typeof(this.functionToCall) == "object") {
695 this.functionToCall.getComboboxVals(this.id,selectedArray,selectedArrayIndizes);
697 if (typeof(this.functionToCall) == undefined) {
702 combobox.prototype.scrollPerButton = function() {
703 if (this.scrollActive == true) {
704 this.scroll(this.scrollDir,1,true); //1 is one element to scroll, true says that we should move scrollbar
705 this.timer.setTimeout("scrollPerButton",100)
709 combobox.prototype.scroll = function(scrollDir,scrollNr,scrollBool) {
710 if (scrollNr < this.heightNrElements) {
711 if ((this.curLowerIndex > 0) && (scrollDir == "up")) {
712 if (scrollNr > this.curLowerIndex) {
713 scrollNr = this.curLowerIndex;
715 //decrement current index
716 this.curLowerIndex = this.curLowerIndex - scrollNr;
718 if (scrollBool == true) {
719 this.scroller.setAttributeNS(null,"y",parseFloat(this.scroller.getAttributeNS(null,"y"))+ this.scrollStep * -1);
721 //add upper rect elements
722 for (var i=0;i<scrollNr;i++) {
723 var node = document.createElementNS(svgNS,"rect");
724 node.setAttributeNS(null,"x",this.xOffset + this.textPadding / 2);
725 node.setAttributeNS(null,"y",this.yOffset + this.cellHeight * i);
726 node.setAttributeNS(null,"width",this.width - this.cellHeight - this.textPadding);
727 node.setAttributeNS(null,"height",this.cellHeight);
728 if (this.elementsArray[this.curLowerIndex + i].value) {
729 for (var attrib in this.highlightStyles) {
730 node.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
734 node.setAttributeNS(null,"fill",this.boxStyles["fill"]);
735 if (node.hasAttributeNS(null,"fill-opacity")) {
736 node.removeAttributeNS(null,"fill-opacity");
739 node.setAttributeNS(null,"id","selHighlightSelection_" + this.id + "_" + (i + this.curLowerIndex));
741 node.addEventListener("mousedown", this, false);
742 node.addEventListener("mouseover", this, false);
743 node.addEventListener("mouseup", this, false);
744 this.comboElementsGroup.appendChild(node);
746 var node = document.createElementNS(svgNS,"text");
747 node.setAttributeNS(null,"id","selTexts_" + this.id + "_" + (i + this.curLowerIndex));
748 node.setAttributeNS(null,"x",this.xOffset + this.textPadding);
749 node.setAttributeNS(null,"y",this.yOffset + this.textPaddingVertical + this.cellHeight * i);
750 node.setAttributeNS(null,"pointer-events","none");
752 for (var attrib in this.textStyles) {
753 value = this.textStyles[attrib];
754 if (attrib == "font-size") {
757 node.setAttributeNS(null,attrib,value);
759 var selectionText = document.createTextNode(this.elementsArray[this.curLowerIndex + i].key);
760 node.appendChild(selectionText);
761 this.comboElementsGroup.appendChild(node);
763 //move middle elements
764 for (var j=i;j<this.heightNrElements;j++) {
765 var node = document.getElementById("selTexts_" + this.id + "_" + (j + this.curLowerIndex));
766 node.setAttributeNS(null,"y",parseFloat(node.getAttributeNS(null,"y")) + (this.cellHeight * scrollNr));
767 var node = document.getElementById("selHighlightSelection_" + this.id + "_" + (j + this.curLowerIndex));
768 node.setAttributeNS(null,"y",parseFloat(node.getAttributeNS(null,"y")) + (this.cellHeight * scrollNr));
770 //remove lower elements
771 for (var k=j;k<(j+scrollNr);k++) {
772 var node = document.getElementById("selTexts_" + this.id + "_" + (k + this.curLowerIndex));
773 this.comboElementsGroup.removeChild(node);
774 var node = document.getElementById("selHighlightSelection_" + this.id +"_" + (k + this.curLowerIndex));
775 this.comboElementsGroup.removeChild(node);
778 else if ((this.curLowerIndex < (this.elementsArray.length - this.heightNrElements)) && (scrollDir == "down")) {
780 if (scrollBool == true) {
781 this.scroller.setAttributeNS(null,"y",parseFloat(this.scroller.getAttributeNS(null,"y")) + this.scrollStep);
783 //remove most upper element
784 for (var i=0;i<scrollNr;i++) {
785 var node = document.getElementById("selTexts_" + this.id + "_" + (this.curLowerIndex + i));
786 this.comboElementsGroup.removeChild(node);
787 var node = document.getElementById("selHighlightSelection_" + this.id + "_" + (this.curLowerIndex + i));
788 this.comboElementsGroup.removeChild(node);
790 //move middle elements
791 for (var j=i;j<this.heightNrElements;j++) {
792 var node = document.getElementById("selTexts_" + this.id + "_" + (j + this.curLowerIndex));
793 node.setAttributeNS(null,"y",parseFloat(node.getAttributeNS(null,"y")) - (this.cellHeight * scrollNr));
794 var node = document.getElementById("selHighlightSelection_" + this.id + "_" + (j + this.curLowerIndex));
795 node.setAttributeNS(null,"y",parseFloat(node.getAttributeNS(null,"y")) - (this.cellHeight * scrollNr));
797 //add most lower element
798 for (var k=j;k<(j+scrollNr);k++) {
799 var node = document.createElementNS(svgNS,"rect");
800 node.setAttributeNS(null,"x",this.xOffset + this.textPadding / 2);
801 node.setAttributeNS(null,"y",this.yOffset + this.cellHeight * (k - scrollNr));
802 node.setAttributeNS(null,"width",this.width - this.cellHeight - this.textPadding);
803 node.setAttributeNS(null,"height",this.cellHeight);
804 if (this.elementsArray[this.curLowerIndex + k].value) {
805 for (var attrib in this.highlightStyles) {
806 node.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
810 node.setAttributeNS(null,"fill",this.boxStyles["fill"]);
811 if (node.hasAttributeNS(null,"fill-opacity")) {
812 node.removeAttributeNS(null,"fill-opacity");
815 node.setAttribute("id","selHighlightSelection_" + this.id + "_" + (k + this.curLowerIndex));
817 node.addEventListener("mousedown", this, false);
818 node.addEventListener("mouseover", this, false);
819 node.addEventListener("mouseup", this, false);
820 this.comboElementsGroup.appendChild(node);
822 var node = document.createElementNS(svgNS,"text");
823 node.setAttributeNS(null,"id","selTexts_" + this.id + "_" + (k + this.curLowerIndex));
824 node.setAttributeNS(null,"x",this.xOffset + this.textPadding);
825 node.setAttributeNS(null,"y",this.yOffset + this.textPaddingVertical + this.cellHeight * (k - scrollNr));
826 node.setAttributeNS(null,"pointer-events","none");
828 for (var attrib in this.textStyles) {
829 value = this.textStyles[attrib];
830 if (attrib == "font-size") {
833 node.setAttributeNS(null,attrib,value);
835 var selectionText = document.createTextNode(this.elementsArray[this.curLowerIndex + k].key);
836 node.appendChild(selectionText);
837 this.comboElementsGroup.appendChild(node);
839 //increment current index
840 this.curLowerIndex = this.curLowerIndex + scrollNr;
844 //remove lower elements
845 for (var i=0;i<this.heightNrElements;i++) {
846 var node = document.getElementById("selTexts_" + this.id + "_" + (i + this.curLowerIndex));
847 this.comboElementsGroup.removeChild(node);
848 var node = document.getElementById("selHighlightSelection_" + this.id +"_" + (i + this.curLowerIndex));
849 this.comboElementsGroup.removeChild(node);
851 if (scrollDir == "down") {
852 this.curLowerIndex = this.curLowerIndex + scrollNr;
855 this.curLowerIndex = this.curLowerIndex - scrollNr;
857 for (var i=0;i<this.heightNrElements;i++) {
858 var node = document.createElementNS(svgNS,"rect");
859 node.setAttributeNS(null,"x",this.xOffset + this.textPadding / 2);
860 node.setAttributeNS(null,"y",this.yOffset + this.cellHeight * i);
861 node.setAttributeNS(null,"width",this.width - this.cellHeight - this.textPadding);
862 node.setAttributeNS(null,"height",this.cellHeight);
863 if (this.elementsArray[this.curLowerIndex + i].value) {
864 for (var attrib in this.highlightStyles) {
865 node.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
869 node.setAttributeNS(null,"fill",this.boxStyles["fill"]);
870 if (node.hasAttributeNS(null,"fill-opacity")) {
871 node.removeAttributeNS(null,"fill-opacity");
874 node.setAttributeNS(null,"id","selHighlightSelection_" + this.id + "_" + (i + this.curLowerIndex));
876 node.addEventListener("mousedown", this, false);
877 node.addEventListener("mouseover", this, false);
878 node.addEventListener("mouseup", this, false);
879 this.comboElementsGroup.appendChild(node);
881 var node = document.createElementNS(svgNS,"text");
882 node.setAttributeNS(null,"id","selTexts_" + this.id + "_" + (i + this.curLowerIndex));
883 node.setAttributeNS(null,"x",this.xOffset + this.textPadding);
884 node.setAttributeNS(null,"y",this.yOffset + this.textPaddingVertical + this.cellHeight * i);
885 node.setAttributeNS(null,"pointer-events","none");
887 for (var attrib in this.textStyles) {
888 value = this.textStyles[attrib];
889 if (attrib == "font-size") {
892 node.setAttributeNS(null,attrib,value);
894 var selectionText = document.createTextNode(this.elementsArray[this.curLowerIndex + i].key);
895 node.appendChild(selectionText);
896 this.comboElementsGroup.appendChild(node);
901 //event listener for Scrollbar element
902 combobox.prototype.scrollBarMove = function(evt) {
903 var scrollerMinY = parseFloat(this.scrollbarRect.getAttributeNS(null,"y"));
904 var scrollerMaxY = parseFloat(this.scrollbarRect.getAttributeNS(null,"y")) + parseFloat(this.scrollbarRect.getAttributeNS(null,"height")) - parseFloat(this.scroller.getAttributeNS(null,"height"));
905 if (evt.type == "mousedown") {
906 this.panStatus = true;
907 //add event listeners to root element
908 var svgroot = document.documentElement;
909 svgroot.addEventListener("mousemove", this, false);
910 svgroot.addEventListener("mouseup", this, false);
911 for (var attrib in this.triangleStyles) {
912 this.scroller.setAttributeNS(null,attrib,this.triangleStyles[attrib]);
914 var coordPoint = myMapApp.calcCoord(evt,this.parentGroup);
915 this.panY = coordPoint.y;
916 var oldY = parseFloat(this.scroller.getAttributeNS(null,"y"));
917 var newY = this.panY - parseFloat(this.scroller.getAttributeNS(null,"height")) / 2;
918 if (newY < scrollerMinY) {
920 //maybe recalculate this.panY ??
922 if (newY > scrollerMaxY) {
925 var panDiffY = newY - oldY;
926 var scrollDir = "down";
927 this.scrollCumulus = 0;
928 if(Math.abs(panDiffY) > this.scrollStep) {
929 var scrollNr = Math.abs(Math.round(panDiffY / this.scrollStep));
931 this.scrollCumulus = panDiffY - this.scrollStep * scrollNr;
934 this.scrollCumulus = panDiffY + this.scrollStep * scrollNr;
937 newY = oldY + panDiffY;
938 this.scroller.setAttributeNS(null,"y",newY);
939 this.scroll(scrollDir,scrollNr,false);
942 if (evt.type == "mouseup") {
943 if (this.panStatus == true) {
944 var newY = parseFloat(this.scroller.getAttributeNS(null,"y"));
945 for (var attrib in this.smallrectStyles) {
946 this.scroller.setAttributeNS(null,attrib,this.smallrectStyles[attrib]);
948 this.scroller.setAttributeNS(null,"y",this.yOffset + this.cellHeight + this.scrollStep * this.curLowerIndex);
950 var svgroot = document.documentElement;
951 svgroot.removeEventListener("mousemove", this, false);
952 svgroot.removeEventListener("mouseup", this, false);
953 this.panStatus = false;
955 if (evt.type == "mousemove") {
956 if (this.panStatus == true) {
957 var coordPoint = myMapApp.calcCoord(evt,this.parentGroup);
958 var panNewEvtY = coordPoint.y;
959 var panDiffY = panNewEvtY - this.panY;
960 var oldY = parseFloat(this.scroller.getAttributeNS(null,"y"));
961 var newY = oldY + panDiffY;
962 if (newY < scrollerMinY) {
965 if (newY > scrollerMaxY) {
968 var panDiffY = newY - oldY;
969 this.scrollCumulus += panDiffY;
970 var scrollDir = "down";
972 if(Math.abs(this.scrollCumulus) >= this.scrollStep) {
973 scrollNr = Math.abs(Math.round(this.scrollCumulus / this.scrollStep));
974 if (this.scrollCumulus > 0) {
975 this.scrollCumulus = this.scrollCumulus - this.scrollStep * scrollNr;
978 this.scrollCumulus = this.scrollCumulus + this.scrollStep * scrollNr;
981 this.scroll(scrollDir,scrollNr,false);
984 if (Math.abs(this.scrollCumulus) > this.scrollStep) {
988 this.scrollCumulus = this.scrollCumulus + this.scrollStep;
991 this.scrollCumulus = this.scrollCumulus - this.scrollStep;
993 panDiffY = this.scrollCumulus;
994 this.scroll(scrollDir,scrollNr,false);
997 if (newY == scrollerMinY && this.curLowerIndex != 0) {
998 this.scroll("up",1,false);
1000 else if (newY == scrollerMaxY && this.curLowerIndex != (this.elementsArray.length - this.heightNrElements)) {
1001 this.scroll("down",1,false);
1005 newY = oldY + panDiffY;
1006 this.scroller.setAttributeNS(null,"y",newY);
1007 this.panY = panNewEvtY;
1012 combobox.prototype.scrollToKey = function() {
1013 for (var i=0;i<this.elementsArray.length;i++) {
1014 if (this.elementsArray[i].key.toLowerCase().substr(0,this.pressedKeys.length) == this.pressedKeys) {
1015 this.curLowerIndex = i;
1016 if (this.curLowerIndex > (this.elementsArray.length - this.heightNrElements)) {
1017 this.curLowerIndex = this.elementsArray.length - this.heightNrElements;
1019 this.createOrUpdateCombobox();
1025 combobox.prototype.elementExists = function(elementName) {
1027 for (i=0;i<this.elementsArray.length;i++) {
1028 if (this.elementsArray[i].key == elementName) {
1035 combobox.prototype.selectElementsByName = function(elementNames,fireFunction) {
1036 var idsToSelect = new Array();
1037 for (var i=0;i<elementNames.length;i++) {
1038 var indexNr = this.elementExists(elementNames[i].key);
1039 if (indexNr != -1) {
1040 idsToSelect.push({index:indexNr,value:elementNames[i].value});
1043 if (idsToSelect.length > 0) {
1044 this.selectElementsByPosition(idsToSelect,fireFunction);
1048 combobox.prototype.selectElementsByPosition = function(positionArray,fireFunction) {
1049 for (var i=0;i<positionArray.length;i++) {
1050 if (positionArray[i].index < this.elementsArray.length) {
1051 if (positionArray[i].index >= this.curLowerIndex && positionArray[i].index < (this.curLowerIndex + this.heightNrElements)) {
1052 var node = document.getElementById("selHighlightSelection_" + this.id + "_" + positionArray[i].index);
1053 if (positionArray[i].value) {
1054 for (var attrib in this.highlightStyles) {
1055 node.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
1059 node.setAttributeNS(null,"fill",this.boxStyles["fill"]);
1060 if (node.hasAttributeNS(null,"fill-opacity")) {
1061 node.removeAttributeNS(null,"fill-opacity");
1065 this.elementsArray[positionArray[i].index].value = positionArray[i].value;
1069 this.timer.setTimeout("fireFunction",this.timerMs);
1073 combobox.prototype.sortList = function(direction) {
1074 //direction: asc|desc, for ascending or descending
1075 var mySelElementString = this.elementsArray[this.activeSelection];
1076 this.elementsArray.sort(function mySort(a,b) {
1077 if (a.key > b.key) {
1084 if (direction == "desc") {
1085 this.elementsArray.reverse();
1087 this.createOrUpdateCombobox();
1090 combobox.prototype.deleteElementsByName = function(elementNames,fireFunction) {
1091 for (var i=0;i<elementNames.length;i++) {
1092 existsPosition = this.elementExists(elementNames[i]);
1093 if (existsPosition != -1) {
1094 var tempArray = new Array;
1095 tempArray = tempArray.concat(this.elementsArray.slice(0,existsPosition),this.elementsArray.slice(existsPosition + 1,this.elementsArray.length));
1096 this.elementsArray = tempArray;
1097 if (existsPosition < this.curLowerIndex) {
1098 this.curLowerIndex--;
1102 this.createOrUpdateCombobox();
1104 this.timer.setTimeout("fireFunction",this.timerMs);
1108 combobox.prototype.deleteElementByPosition = function(elementPosition,fireFunction) {
1109 if (elementPosition < this.elementsArray.length) {
1110 var tempArray = new Array;
1111 tempArray = tempArray.concat(this.elementsArray.slice(0,elementPosition),this.elementsArray.slice(elementPosition + 1,this.elementsArray.length));
1112 this.elementsArray = tempArray;
1113 if (elementPosition < this.curLowerIndex) {
1114 this.curLowerIndex -= 1;
1117 this.createOrUpdateCombobox();
1119 this.timer.setTimeout("fireFunction",this.timerMs);
1123 combobox.prototype.addElementAtPosition = function(element,position,fireFunction) {
1124 if (position > this.elementsArray.length) {
1125 this.elementsArray.push(element);
1126 position = this.elementsArray.length - 1;
1129 var tempArray = new Array;
1130 tempArray = tempArray.concat(this.elementsArray.slice(0,position),element,this.elementsArray.slice(position,this.elementsArray.length));
1131 this.elementsArray = tempArray;
1133 if (position < this.curLowerIndex) {
1134 this.curLowerIndex += 1;
1136 this.createOrUpdateCombobox();
1138 this.timer.setTimeout("fireFunction",this.timerMs);
1142 combobox.prototype.addElementsAlphabetically = function(elementsArr,direction,fireFunction) {
1143 this.elementsArray = this.elementsArray.concat(elementsArr);
1144 this.sortList(direction);
1146 this.timer.setTimeout("fireFunction",this.timerMs);
1150 combobox.prototype.getCurrentSelections = function() {
1151 var selectedArray = new Array();
1152 for (var i=0;i<this.elementsArray.length;i++) {
1153 //see how many and where elements are already selected
1154 if (this.elementsArray[i].value) {
1155 selectedArray.push(this.elementsArray[i].key);
1158 return selectedArray;
1161 combobox.prototype.getCurrentSelectionsIndex = function() {
1162 var selectedArrayIndizes = new Array();
1163 for (var i=0;i<this.elementsArray.length;i++) {
1164 //see how many and where elements are already selected
1165 if (this.elementsArray[i].value) {
1166 selectedArrayIndizes.push(i);
1169 return selectedArrayIndizes;
1172 combobox.prototype.removeCombobox = function() {
1173 //remove all Elements of combobox
1174 this.exists = false;
1175 while (this.parentGroup.hasChildNodes()) {
1176 this.parentGroup.removeChild(this.parentGroup.firstChild);