2 Scripts to create interactive scrollbars in SVG using ECMA script
3 Copyright (C) <2006> <Andreas Neumann>
4 Version 0.9.1, 2006-10-30
5 neumann@karto.baug.ethz.ch
7 http://www.carto.net/neumann/
13 current version: 0.9.1
20 added .show(), .hide() and .remove() methods
25 This ECMA script library is free software; you can redistribute it and/or
26 modify it under the terms of the GNU Lesser General Public
27 License as published by the Free Software Foundation; either
28 version 2.1 of the License, or (at your option) any later version.
30 This library is distributed in the hope that it will be useful,
31 but WITHOUT ANY WARRANTY; without even the implied warranty of
32 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
33 Lesser General Public License for more details.
35 You should have received a copy of the GNU Lesser General Public
36 License along with this library (lesser_gpl.txt); if not, write to the Free Software
37 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
41 original document site: http://www.carto.net/papers/svg/gui/scrollbar/
42 Please contact the author in case you want to use code or ideas commercially.
43 If you use this code, please include this copyright header, the included full
44 LGPL 2.1 text and read the terms provided in the LGPL 2.1 license
45 (http://www.gnu.org/copyleft/lesser.txt)
47 -------------------------------
49 Please report bugs and send improvements to neumann@karto.baug.ethz.ch
50 If you use this control, please link to the original (http://www.carto.net/papers/svg/gui/scrollbar/)
51 somewhere in the source-code-comment or the "about" of your project and give credits, thanks!
55 function scrollbar(id,parentNode,x,y,width,height,startValue,endValue,initialHeightPerc,initialOffset,scrollStep,scrollButtonLocations,scrollbarStyles,scrollerStyles,triangleStyles,highlightStyles,functionToCall) {
57 var createScrollbar = true;
58 if (arguments.length == nrArguments) {
59 //get constructor variables
61 this.parentNode = parentNode;
66 this.startValue = startValue;
67 this.endValue = endValue;
68 this.initialHeightPerc = initialHeightPerc;
69 this.initialOffset = initialOffset;
70 this.scrollStep = scrollStep; //this value indicates how much the slider will scroll when the arrow buttons are pressed, values are in percentage
71 this.scrollButtonLocations = scrollButtonLocations;
72 if (this.scrollButtonLocations != "bottom_bottom" && this.scrollButtonLocations != "top_bottom" && this.scrollButtonLocations && "top_top" && this.scrollButtonLocations != "none_none") {
73 createScrollbar = false;
74 alert("Error: parameter 'scrollButtonLocations' can only be of the following values: 'bottom_bottom' || 'top_top' || 'top_bottom' || 'none_none'.");
76 this.scrollbarStyles = scrollbarStyles;
77 this.scrollerStyles = scrollerStyles;
78 this.triangleStyles = triangleStyles;
79 this.highlightStyles = highlightStyles;
80 this.functionToCall = functionToCall;
81 //additional properties to be used later
82 this.horizOrVertical = "vertical"; //specifies wether scrollbar is horizontal or vertical
83 this.cellHeight = this.width; //the height or width of the buttons on top or bottom of the scrollbar
84 this.scrollStatus = false; //indicates whether scrolling is active
85 this.buttonScrollActive = false; //indicates whether the scrollbutton is currently being pressed
86 this.scrollbarScrollActive = false; //indicates whether the scrollbar is currently being pressed
89 createScrollbar = false;
90 alert("Error ("+id+"): wrong nr of arguments! You have to pass over "+nrArguments+" parameters.");
92 if (createScrollbar) {
94 var result = this.testParent();
97 this.timer = new Timer(this); //a Timer instance for calling the functionToCall
98 this.timerMs = 200; //a constant of this object that is used in conjunction with the timer - functionToCall is called after 200 ms
99 this.createScrollbar();
102 alert("could not create or reference 'parentNode' of scrollbar with id '"+this.id+"'");
106 alert("Could not create scrollbar with id '"+id+"' due to errors in the constructor parameters");
110 //test if parent group exists or create a new group at the end of the file
111 scrollbar.prototype.testParent = function() {
112 //test if of type object
113 var nodeValid = false;
114 this.parentGroup = document.createElementNS(svgNS,"g");
115 if (typeof(this.parentNode) == "object") {
116 if (this.parentNode.nodeName == "svg" || this.parentNode.nodeName == "g" || this.parentNode.nodeName == "svg:svg" || this.parentNode.nodeName == "svg:g") {
117 this.parentNode.appendChild(this.parentGroup);
121 else if (typeof(this.parentNode) == "string") {
122 //first test if button group exists
123 if (!document.getElementById(this.parentNode)) {
124 this.parentGroup.setAttributeNS(null,"id",this.parentNode);
125 document.documentElement.appendChild(this.parentGroup);
129 document.getElementById(this.parentNode).appendChild(this.parentGroup);
136 //create scrollbar geometry
137 scrollbar.prototype.createScrollbar = function() {
138 //first determine if vertical of horizontal
139 if (this.width > this.height) {
140 this.horizOrVertical = "horizontal";
141 this.cellHeight = this.height;
143 this.triangleFourth = Math.round(this.cellHeight / 4); //this is used to construct the triangles for the buttons
145 if (this.scrollButtonLocations != "none_none") {
146 this.scrollUpperRect = document.createElementNS(svgNS,"rect");
147 this.scrollLowerRect = document.createElementNS(svgNS,"rect");
148 this.scrollUpperTriangle = document.createElementNS(svgNS,"path");
149 this.scrollLowerTriangle = document.createElementNS(svgNS,"path");
152 if (this.horizOrVertical == "vertical") {
153 this.scrollUpperRectX = this.x;
154 this.scrollLowerRectX = this.x;
155 switch (this.scrollButtonLocations) {
157 this.scrollbarX = this.x;
158 this.scrollbarY = this.y + (2 * this.cellHeight);
159 this.scrollbarWidth = this.width;
160 this.scrollbarHeight = this.height - (2 * this.cellHeight);
161 this.scrollUpperRectY = this.y;
162 this.scrollLowerRectY = this.y + this.cellHeight;
164 case "bottom_bottom":
165 this.scrollbarX = this.x;
166 this.scrollbarY = this.y;
167 this.scrollbarWidth = this.width;
168 this.scrollbarHeight = this.height - (2 * this.cellHeight);
169 this.scrollUpperRectY = this.y + this.height - (2 * this.cellHeight);
170 this.scrollLowerRectY = this.y + this.height - this.cellHeight;
173 this.scrollbarX = this.x;
174 this.scrollbarY = this.y + this.cellHeight;
175 this.scrollbarWidth = this.width;
176 this.scrollbarHeight = this.height - (2 * this.cellHeight);
177 this.scrollUpperRectY = this.y;
178 this.scrollLowerRectY = this.y + this.height - this.cellHeight;
181 this.scrollbarX = this.x;
182 this.scrollbarY = this.y;
183 this.scrollbarWidth = this.width;
184 this.scrollbarHeight = this.height;
187 var myUpperTriPath = "M"+(this.scrollUpperRectX + this.cellHeight * 0.5)+" "+(this.scrollUpperRectY + this.triangleFourth)+" L"+(this.scrollUpperRectX + 3 * this.triangleFourth)+" "+(this.scrollUpperRectY + 3 * this.triangleFourth)+" L"+(this.scrollUpperRectX + this.triangleFourth)+" "+(this.scrollUpperRectY + 3 * this.triangleFourth)+" Z";
188 var myLowerTriPath = "M"+(this.scrollLowerRectX + this.cellHeight * 0.5)+" "+(this.scrollLowerRectY + 3 * this.triangleFourth)+" L"+(this.scrollLowerRectX + this.triangleFourth)+" "+(this.scrollLowerRectY + this.triangleFourth)+" L"+(this.scrollLowerRectX + this.triangleFourth * 3)+" "+(this.scrollLowerRectY + this.triangleFourth)+" Z";
191 if (this.horizOrVertical == "horizontal") {
192 this.scrollUpperRectY = this.y;
193 this.scrollLowerRectY = this.y;
194 switch (this.scrollButtonLocations) {
196 this.scrollbarX = this.x + (2 * this.cellHeight);
197 this.scrollbarY = this.y;
198 this.scrollbarWidth = this.width - (2 * this.cellHeight);
199 this.scrollbarHeight = this.height;
200 this.scrollUpperRectX = this.x;
201 this.scrollLowerRectX = this.x + this.cellHeight;
203 case "bottom_bottom":
204 this.scrollbarX = this.x;
205 this.scrollbarY = this.y;
206 this.scrollbarWidth = this.width - (2 * this.cellHeight);
207 this.scrollbarHeight = this.height;
208 this.scrollUpperRectX = this.x + this.width - (2 * this.cellHeight);
209 this.scrollLowerRectX = this.x + this.width - this.cellHeight;
212 this.scrollbarX = this.x + this.cellHeight;
213 this.scrollbarY = this.y;
214 this.scrollbarWidth = this.width - (2 * this.cellHeight);
215 this.scrollbarHeight = this.height;
216 this.scrollUpperRectX = this.x;
217 this.scrollLowerRectX = this.x + this.width - this.cellHeight;
220 this.scrollbarX = this.x;
221 this.scrollbarY = this.y;
222 this.scrollbarWidth = this.width;
223 this.scrollbarHeight = this.height;
226 var myUpperTriPath = "M"+(this.scrollUpperRectX + this.triangleFourth)+" "+(this.scrollUpperRectY + this.triangleFourth * 2)+" L"+(this.scrollUpperRectX + 3 * this.triangleFourth)+" "+(this.scrollUpperRectY + this.triangleFourth)+" L"+(this.scrollUpperRectX + 3 * this.triangleFourth)+" "+(this.scrollUpperRectY + 3 * this.triangleFourth)+" Z";
227 var myLowerTriPath = "M"+(this.scrollLowerRectX + this.triangleFourth * 3)+" "+(this.scrollLowerRectY + 2 * this.triangleFourth)+" L"+(this.scrollLowerRectX + this.triangleFourth)+" "+(this.scrollLowerRectY + this.triangleFourth * 3)+" L"+(this.scrollLowerRectX + this.triangleFourth)+" "+(this.scrollLowerRectY + this.triangleFourth)+" Z";
230 this.scrollbar = document.createElementNS(svgNS,"rect");
231 this.scrollbar.setAttributeNS(null,"x",this.scrollbarX);
232 this.scrollbar.setAttributeNS(null,"y",this.scrollbarY);
233 this.scrollbar.setAttributeNS(null,"width",this.scrollbarWidth);
234 this.scrollbar.setAttributeNS(null,"height",this.scrollbarHeight);
235 this.scrollbar.setAttributeNS(null,"id","scrollbar_"+this.id);
236 for (var attrib in this.scrollbarStyles) {
237 this.scrollbar.setAttributeNS(null,attrib,this.scrollbarStyles[attrib]);
239 this.scrollbar.addEventListener("mousedown",this,false);
240 this.parentGroup.appendChild(this.scrollbar);
241 //now create scroller
242 this.scroller = document.createElementNS(svgNS,"rect");
243 if (this.horizOrVertical == "vertical") {
244 this.scroller.setAttributeNS(null,"x",this.scrollbarX);
245 this.scrollerY = this.scrollbarY + (this.scrollbarHeight - this.scrollbarHeight * this.initialHeightPerc) * this.initialOffset;
246 this.scroller.setAttributeNS(null,"y",this.scrollerY);
247 this.scroller.setAttributeNS(null,"width",this.scrollbarWidth);
248 this.scrollerHeight = this.scrollbarHeight * this.initialHeightPerc;
249 this.scroller.setAttributeNS(null,"height",this.scrollerHeight);
251 if (this.horizOrVertical == "horizontal") {
252 this.scrollerX = this.scrollbarX + (this.scrollbarWidth - this.scrollbarWidth * this.initialHeightPerc) * this.initialOffset;
253 this.scroller.setAttributeNS(null,"x",this.scrollerX);
254 this.scroller.setAttributeNS(null,"y",this.scrollbarY);
255 this.scrollerWidth = this.scrollbarWidth * this.initialHeightPerc;
256 this.scroller.setAttributeNS(null,"width",this.scrollerWidth);
257 this.scroller.setAttributeNS(null,"height",this.scrollbarHeight);
259 for (var attrib in this.scrollerStyles) {
260 this.scroller.setAttributeNS(null,attrib,this.scrollerStyles[attrib]);
262 //need to add events here
263 this.scroller.setAttributeNS(null,"id","scroller_"+this.id);
264 this.scroller.addEventListener("mousedown",this,false);
265 this.parentGroup.appendChild(this.scroller);
266 //append rects and triangles
267 if (this.scrollButtonLocations != "none_none") {
269 for (var attrib in this.scrollerStyles) {
270 this.scrollUpperRect.setAttributeNS(null,attrib,this.scrollerStyles[attrib]);
272 this.scrollUpperRect.setAttributeNS(null,"x",this.scrollUpperRectX);
273 this.scrollUpperRect.setAttributeNS(null,"y",this.scrollUpperRectY);
274 this.scrollUpperRect.setAttributeNS(null,"width",this.cellHeight);
275 this.scrollUpperRect.setAttributeNS(null,"height",this.cellHeight);
276 this.scrollUpperRect.addEventListener("mousedown", this, false);
277 this.scrollUpperRect.setAttributeNS(null,"id","scrollUpperRect_"+this.id);
278 this.parentGroup.appendChild(this.scrollUpperRect);
280 for (var attrib in this.scrollerStyles) {
281 this.scrollLowerRect.setAttributeNS(null,attrib,this.scrollerStyles[attrib]);
283 this.scrollLowerRect.setAttributeNS(null,"x",this.scrollLowerRectX);
284 this.scrollLowerRect.setAttributeNS(null,"y",this.scrollLowerRectY);
285 this.scrollLowerRect.setAttributeNS(null,"width",this.cellHeight);
286 this.scrollLowerRect.setAttributeNS(null,"height",this.cellHeight);
287 this.scrollLowerRect.addEventListener("mousedown", this, false);
288 this.scrollLowerRect.setAttributeNS(null,"id","scrollLowerRect_"+this.id);
289 this.parentGroup.appendChild(this.scrollLowerRect);
291 this.scrollUpperTriangle.setAttributeNS(null,"d",myUpperTriPath);
292 this.scrollUpperTriangle.setAttributeNS(null,"pointer-events","none");
293 for (var attrib in this.triangleStyles) {
294 this.scrollUpperTriangle.setAttributeNS(null,attrib,this.triangleStyles[attrib]);
296 this.parentGroup.appendChild(this.scrollUpperTriangle);
298 this.scrollLowerTriangle.setAttributeNS(null,"d",myLowerTriPath);
299 this.scrollLowerTriangle.setAttributeNS(null,"pointer-events","none");
300 for (var attrib in this.triangleStyles) {
301 this.scrollLowerTriangle.setAttributeNS(null,attrib,this.triangleStyles[attrib]);
303 this.parentGroup.appendChild(this.scrollLowerTriangle);
307 scrollbar.prototype.handleEvent = function(evt) {
309 var callerId = el.getAttributeNS(null,"id");
310 if (evt.type == "mousedown") {
311 if (callerId == "scroller_"+this.id) {
312 //case the mouse went down on scroller
315 if (callerId == "scrollLowerRect_"+this.id || callerId == "scrollUpperRect_"+this.id) {
316 //this part is for scrolling per button
317 this.buttonScrollActive = true;
318 this.scrollDir = "down";
319 this.currentScrollButton = evt.target;
320 this.currentScrollTriangle = this.scrollLowerTriangle;
321 if (callerId == "scrollUpperRect_"+this.id) {
322 this.scrollDir = "up";
323 this.currentScrollTriangle = this.scrollUpperTriangle;
325 document.documentElement.addEventListener("mouseup",this,false);
326 //change appearance of button
327 for (var attrib in this.scrollerStyles) {
328 this.currentScrollButton.removeAttributeNS(null,attrib);
330 for (var attrib in this.highlightStyles) {
331 this.currentScrollButton.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
333 //change appearance of triangle
334 for (var attrib in this.triangleStyles) {
335 this.currentScrollTriangle.removeAttributeNS(null,attrib);
337 for (var attrib in this.scrollerStyles) {
338 this.currentScrollTriangle.setAttributeNS(null,attrib,this.scrollerStyles[attrib]);
340 this.scrollPercent(this.scrollDir,this.scrollStep);
341 this.timer.setTimeout("scrollPerButton",400);
343 if (callerId == "scrollbar_"+this.id) {
344 //this part is for scrolling when mouse is down on scrollbar
345 var coords = myMapApp.calcCoord(evt,this.scrollbar);
346 this.scrollbarScrollActive = true;
347 this.scrollDir = "down";
348 if (this.horizOrVertical == "vertical") {
349 if (coords.y < this.scrollerY) {
350 this.scrollDir = "up";
353 if (this.horizOrVertical == "horizontal") {
354 if (coords.x < this.scrollerX) {
355 this.scrollDir = "up";
358 document.documentElement.addEventListener("mouseup",this,false);
360 for (var attrib in this.scrollerStyles) {
361 this.scroller.removeAttributeNS(null,attrib);
363 for (var attrib in this.highlightStyles) {
364 this.scroller.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
366 this.scrollPercent(this.scrollDir,this.scrollStep*10);
367 this.timer.setTimeout("scrollPerScrollbar",400);
370 if (evt.type == "mousemove" && this.scrollStatus) {
371 //this is for scrolling per scroller
374 if (evt.type == "mouseup") {
375 //this is for finishing the different scroll modi
376 if (this.scrollStatus) {
377 //finishing scroll per scroller
380 if (this.buttonScrollActive) {
381 //finishing scroll per button
382 this.buttonScrollActive = false;
383 document.documentElement.removeEventListener("mouseup",this,false);
384 //change appearance of button
385 for (var attrib in this.highlightStyles) {
386 this.currentScrollButton.removeAttributeNS(null,attrib);
388 for (var attrib in this.scrollerStyles) {
389 this.currentScrollButton.setAttributeNS(null,attrib,this.scrollerStyles[attrib]);
391 //change appearance of triangle
392 for (var attrib in this.scrollerStyles) {
393 this.currentScrollTriangle.removeAttributeNS(null,attrib);
395 for (var attrib in this.triangleStyles) {
396 this.currentScrollTriangle.setAttributeNS(null,attrib,this.triangleStyles[attrib]);
398 this.currentScrollButton = undefined;
399 this.currentScrollTriangle = undefined;
401 if (this.scrollbarScrollActive) {
402 //finishing scroll per scrollbar
403 this.scrollbarScrollActive = false;
404 document.documentElement.removeEventListener("mouseup",this,false);
406 for (var attrib in this.highlightStyles) {
407 this.scroller.removeAttributeNS(null,attrib);
409 for (var attrib in this.scrollerStyles) {
410 this.scroller.setAttributeNS(null,attrib,this.scrollerStyles[attrib]);
416 scrollbar.prototype.scroll = function(evt) {
417 var coords = myMapApp.calcCoord(evt,this.scrollbar);
418 if (evt.type == "mousedown") {
419 document.documentElement.addEventListener("mousemove",this,false);
420 document.documentElement.addEventListener("mouseup",this,false);
421 this.scrollStatus = true;
422 this.panCoords = coords;
423 this.fireFunction("scrollStart");
425 for (var attrib in this.scrollerStyles) {
426 this.scroller.removeAttributeNS(null,attrib);
428 for (var attrib in this.highlightStyles) {
429 this.scroller.setAttributeNS(null,attrib,this.highlightStyles[attrib]);
432 if (evt.type == "mousemove") {
433 var diffX = coords.x - this.panCoords.x;
434 var diffY = coords.y - this.panCoords.y;
435 var scrollerDiff = false;
436 if (this.horizOrVertical == "vertical") {
437 var scrollerOrigY = this.scrollerY;
438 this.scrollerY += diffY;
439 if (this.scrollerY < this.scrollbarY) {
440 this.scrollerY = this.scrollbarY;
442 if ((this.scrollerY + this.scrollerHeight) > (this.scrollbarY + this.scrollbarHeight)) {
443 this.scrollerY = this.scrollbarY + this.scrollbarHeight - this.scrollerHeight;
445 this.scroller.setAttributeNS(null,"y",this.scrollerY);
446 if (scrollerOrigY != this.scrollerY) {
450 if (this.horizOrVertical == "horizontal") {
451 var scrollerOrigX = this.scrollerX;
452 this.scrollerX += diffX;
453 if (this.scrollerX < this.scrollbarX) {
454 this.scrollerX = this.scrollbarX;
456 if ((this.scrollerX + this.scrollerWidth) > (this.scrollbarX + this.scrollbarWidth)) {
457 this.scrollerX = this.scrollbarX + this.scrollbarWidth - this.scrollerWidth;
459 this.scroller.setAttributeNS(null,"x",this.scrollerX);
460 if (scrollerOrigX != this.scrollerX) {
465 this.fireFunction("scrollChange");
467 this.panCoords = coords;
469 if (evt.type == "mouseup") {
470 this.scrollStatus = false;
471 document.documentElement.removeEventListener("mousemove",this,false);
472 document.documentElement.removeEventListener("mouseup",this,false);
474 for (var attrib in this.highlightStyles) {
475 this.scroller.removeAttributeNS(null,attrib);
477 for (var attrib in this.scrollerStyles) {
478 this.scroller.setAttributeNS(null,attrib,this.scrollerStyles[attrib]);
480 this.fireFunction("scrollEnd");
484 scrollbar.prototype.scrollPercent = function(direction,increment) {
485 var currentValues = this.getValue();
486 if (direction == "up") {
487 increment = increment * -1;
489 var newPercent = currentValues.perc + increment;
490 if (newPercent < 0) {
493 if (newPercent > 1) {
496 this.scrollToPercent(newPercent);
499 scrollbar.prototype.scrollToPercent = function(percValue) {
500 if (percValue >= 0 && percValue <= 1) {
501 if (this.horizOrVertical == "vertical") {
502 var newY = this.scrollbarY + (this.scrollbarHeight - this.scrollerHeight) * percValue;
503 this.scrollerY = newY;
504 this.scroller.setAttributeNS(null,"y",newY);
506 if (this.horizOrVertical == "horizontal") {
507 var newX = this.scrollbarX + (this.scrollbarWidth - this.scrollerWidth) * percValue;
508 this.scrollerX = newX;
509 this.scroller.setAttributeNS(null,"x",newX);
511 this.fireFunction("scrolledStep");
514 alert("error in method '.scrollToPercent()' of scrollbar with id '"+this.id+"'. Value out of range. Value needs to be in range 0 <= value <= 1.");
518 scrollbar.prototype.scrollPerButton = function() {
519 if (this.buttonScrollActive) {
520 this.scrollPercent(this.scrollDir,this.scrollStep);
521 this.timer.setTimeout("scrollPerButton",150);
525 scrollbar.prototype.scrollPerScrollbar = function() {
526 if (this.scrollbarScrollActive) {
527 this.scrollPercent(this.scrollDir,this.scrollStep*5);
528 this.timer.setTimeout("scrollPerScrollbar",150);
532 scrollbar.prototype.scrollToValue = function(value) {
533 if ((value >= this.startValue && value <= this.endValue) || (value <= this.startValue && value >= this.endValue)) {
534 var percValue = value/(this.endValue - this.startValue);
535 this.scrollToPercent(percValue);
538 alert("Error in scrollbar with id '"+this.id+"': the provided value '"+value+"' is out of range. The valid range is between '"+this.startValue+"' and '"+this.endValue+"'");
542 scrollbar.prototype.getValue = function() {
544 if (this.horizOrVertical == "vertical") {
545 perc = (this.scrollerY - this.scrollbarY) / (this.scrollbarHeight - this.scrollerHeight);
547 if (this.horizOrVertical == "horizontal") {
548 perc = (this.scrollerX - this.scrollbarX) / (this.scrollbarWidth - this.scrollerWidth);
550 var abs = this.startValue + (this.endValue - this.startValue) * perc;
551 return {"abs":abs,"perc":perc};
554 scrollbar.prototype.fireFunction = function(changeType) {
555 var values = this.getValue();
556 if (typeof(this.functionToCall) == "function") {
557 this.functionToCall(this.id,changeType,values.abs,values.perc);
559 if (typeof(this.functionToCall) == "object") {
560 this.functionToCall.scrollbarChanged(this.id,changeType,values.abs,values.perc);
562 if (typeof(this.functionToCall) == undefined) {
567 scrollbar.prototype.hide = function() {
568 this.parentGroup.setAttributeNS(null,"display","none");
571 scrollbar.prototype.show = function() {
572 this.parentGroup.setAttributeNS(null,"display","inherit");
575 scrollbar.prototype.remove = function() {
576 this.parentGroup.parentNode.removeChild(this.parentGroup);