2 <!-- ***** BEGIN LICENSE BLOCK *****
3 - Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 - The contents of this file are subject to the Mozilla Public License Version
6 - 1.1 (the "License"); you may not use this file except in compliance with
7 - the License. You may obtain a copy of the License at
8 - http://www.mozilla.org/MPL/
10 - Software distributed under the License is distributed on an "AS IS" basis,
11 - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 - for the specific language governing rights and limitations under the
15 - The Original Code is Netscape's XBL Marquee Emulation code.
17 - The Initial Developer of the Original Code is
18 - Netscape Communications Corporation.
19 - Portions created by the Initial Developer are Copyright (C) 2002
20 - the Initial Developer. All Rights Reserved.
23 - Doron Rosenberg <doron@netscape.com>
24 - L. David Baron <dbaron@dbaron.org>
26 - Alternatively, the contents of this file may be used under the terms of
27 - either the GNU General Public License Version 2 or later (the "GPL"), or
28 - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 - in which case the provisions of the GPL or the LGPL are applicable instead
30 - of those above. If you wish to allow use of your version of this file only
31 - under the terms of either the GPL or the LGPL, and not to allow others to
32 - use your version of this file under the terms of the MPL, indicate your
33 - decision by deleting the provisions above and replace them with the notice
34 - and other provisions required by the LGPL or the GPL. If you do not delete
35 - the provisions above, a recipient may use your version of this file under
36 - the terms of any one of the MPL, the GPL or the LGPL.
38 - ***** END LICENSE BLOCK ***** -->
40 <bindings id="marqueeBindings"
41 xmlns="http://www.mozilla.org/xbl"
42 xmlns:html="http://www.w3.org/1999/xhtml"
43 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
44 xmlns:xbl="http://www.mozilla.org/xbl">
47 <binding id="marquee">
50 <stylesheet src="chrome://xbl-marquee/content/xbl-marquee.css"/>
54 <field name="_scrollAmount">6</field>
55 <property name="scrollAmount">
58 var val = parseInt(this.getAttribute("scrollamount"));
60 if (val <= 0 || isNaN(val))
61 return this._scrollAmount;
67 this.setAttribute("scrollamount", val);
71 <field name="_scrollDelay">85</field>
72 <property name="scrollDelay">
75 var val = parseInt(this.getAttribute("scrolldelay"));
77 if (val <= 0 || isNaN(val))
78 return this._scrollDelay;
84 this.setAttribute("scrolldelay", val);
88 <property name="trueSpeed">
91 if (!this.hasAttribute("truespeed"))
100 this.setAttribute("truespeed", "truespeed");
102 this.removeAttribute('truespeed');
107 <property name="direction">
109 return this.getAttribute("direction");
112 this.setAttribute("direction", val);
116 <field name="_direction">"left"</field>
118 <property name="behavior">
120 return this._behavior;
123 this.setAttribute("behavior", val);
127 <field name="_behavior">"scroll"</field>
129 <property name="loop">
132 var val = parseInt(this.getAttribute('loop'));
134 if (val < -1 || isNaN(val))
141 this.setAttribute("loop", val);
145 <field name="_loop">-1</field>
147 <property name="onstart">
149 return this.getAttribute("onstart");
152 this._setEventListener("start", val, true);
153 this.setAttribute("onstart", val);
157 <field name="_onstart"></field>
159 <property name="onfinish">
161 return this.getAttribute("onfinish");
164 this._setEventListener("finish", val, true);
165 this.setAttribute("onfinish", val);
169 <field name="_onfinish"></field>
171 <property name="onbounce">
173 return this.getAttribute("onbounce");
176 this._setEventListener("bounce", val, true);
177 this.setAttribute("onbounce", val);
181 <field name="_onbounce"></field>
183 <field name="dirsign">1</field>
184 <field name="startAt">0</field>
185 <field name="stopAt">0</field>
186 <field name="newPosition">0</field>
187 <field name="runId">0</field>
189 <field name="originalHeight">0</field>
190 <field name="startNewDirection">true</field>
192 <property name="outerDiv"
193 onget="return document.getAnonymousNodes(this)[0]"
196 <property name="innerDiv"
197 onget="return document.getAnonymousElementByAttribute(this, 'class', 'innerDiv');"
200 <property name="height"
201 onget="return this.getAttribute('height');"
202 onset="this.setAttribute('height', val);"
205 <property name="width"
206 onget="return this.getAttribute('width');"
207 onset="this.setAttribute('width', val);"
210 <!-- For sniffing purposes -->
211 <field name="nsMarqueeVersion">"0.9.7"</field>
213 <method name="_set_scrollDelay">
214 <parameter name="aValue"/>
217 if (aValue <= 0 || isNaN(aValue) || aValue == null)
221 if (this.trueSpeed == true)
222 this._scrollDelay = aValue;
224 this._scrollDelay = 60;
227 this._scrollDelay = aValue;
234 <method name="_set_scrollAmount">
235 <parameter name="aValue"/>
238 if (aValue < 0 || isNaN(aValue) || aValue == null)
241 this._scrollAmount = aValue;
247 <method name="_set_behavior">
248 <parameter name="aValue"/>
251 if (typeof aValue == 'string')
252 aValue = aValue.toLowerCase();
253 if (aValue != 'alternate' && aValue != 'slide' && aValue != 'scroll')
256 this._behavior = aValue;
262 <method name="_set_direction">
263 <parameter name="aValue"/>
266 if (typeof aValue == 'string')
267 aValue = aValue.toLowerCase();
268 if (aValue != 'left' && aValue != 'right' && aValue != 'up' && aValue != 'down')
271 if (aValue != this._direction)
272 this.startNewDirection = true;
273 this._direction = aValue;
279 <method name="_set_loop">
280 <parameter name="aValue"/>
283 if (!aValue || isNaN(aValue))
295 <method name="_setEventListener">
296 <parameter name="aName"/>
297 <parameter name="aValue"/>
298 <parameter name="aIgnoreNextCall"/>
301 if (this._ignoreNextCall)
302 return this._ignoreNextCall = false;
305 this._ignoreNextCall = true;
307 if (typeof this["_on" + aName] == 'function')
308 this.removeEventListener(aName, this["_on" + aName], false);
310 switch (typeof aValue)
313 this["_on" + aName] = aValue;
314 this.addEventListener(aName, this["_on" + aName], false);
318 if (!aIgnoreNextCall) {
320 this["_on" + aName] = new Function("event", aValue);
325 this.addEventListener(aName, this["_on" + aName], false);
328 this["_on" + aName] = aValue;
333 this["_on" + aName] = aValue;
337 this._ignoreNextCall = false;
338 throw new Error("Invalid argument for Marquee::on" + aName);
345 <method name="_fireEvent">
346 <parameter name="aName"/>
347 <parameter name="aBubbles"/>
348 <parameter name="aCancelable"/>
351 var e = document.createEvent("Events");
352 e.initEvent(aName, aBubbles, aCancelable);
353 this.dispatchEvent(e);
358 <method name="start">
361 if (this.runId == 0) {
363 var lambda = function myTimeOutFunction(){myThis._doMove(false);}
364 this.runId = window.setTimeout(lambda, this._scrollDelay - this._deltaStartStop);
365 this._deltaStartStop = 0;
374 if (this.runId != 0) {
375 this._deltaStartStop = Date.now()- this._lastMoveDate;
376 clearTimeout(this.runId);
384 <method name="_doMove">
385 <parameter name="aResetPosition"/>
388 this._lastMoveDate = Date.now();
390 //startNewDirection is true at first load and whenever the direction is changed
391 if (this.startNewDirection) {
392 this.startNewDirection = false; //we only want this to run once every scroll direction change
396 switch (this._direction)
399 var height = document.defaultView.getComputedStyle(this, "").height;
400 this.outerDiv.style.height = height;
401 if (this.originalHeight > this.outerDiv.offsetHeight)
402 corrvalue = this.originalHeight - this.outerDiv.offsetHeight;
403 this.innerDiv.style.padding = height + " 0";
405 this.startAt = (this._behavior == 'alternate') ? (this.originalHeight - corrvalue) : 0;
406 this.stopAt = (this._behavior == 'alternate' || this._behavior == 'slide') ?
407 (parseInt(height) + corrvalue) : (this.originalHeight + parseInt(height));
411 var height = document.defaultView.getComputedStyle(this, "").height;
412 this.outerDiv.style.height = height;
413 if (this.originalHeight > this.outerDiv.offsetHeight)
414 corrvalue = this.originalHeight - this.outerDiv.offsetHeight;
415 this.innerDiv.style.padding = height + " 0";
417 this.startAt = (this._behavior == 'alternate') ?
418 (parseInt(height) + corrvalue) : (this.originalHeight + parseInt(height));
419 this.stopAt = (this._behavior == 'alternate' || this._behavior == 'slide') ?
420 (this.originalHeight - corrvalue) : 0;
424 if (this.innerDiv.offsetWidth > this.outerDiv.offsetWidth)
425 corrvalue = this.innerDiv.offsetWidth - this.outerDiv.offsetWidth;
427 this.stopAt = (this._behavior == 'alternate' || this._behavior == 'slide') ?
428 (this.innerDiv.offsetWidth - corrvalue) : 0;
429 this.startAt = this.outerDiv.offsetWidth + ((this._behavior == 'alternate') ?
430 corrvalue : (this.innerDiv.offsetWidth + this.stopAt));
435 if (this.innerDiv.offsetWidth > this.outerDiv.offsetWidth)
436 corrvalue = this.innerDiv.offsetWidth - this.outerDiv.offsetWidth;
438 this.startAt = (this._behavior == 'alternate') ? (this.innerDiv.offsetWidth - corrvalue) : 0;
439 this.stopAt = this.outerDiv.offsetWidth +
440 ((this._behavior == 'alternate' || this._behavior == 'slide') ?
441 corrvalue : (this.innerDiv.offsetWidth + this.startAt));
444 if (aResetPosition) {
445 this.newPosition = this.startAt;
446 this._fireEvent("start", false, false);
450 this.newPosition = this.newPosition + (this.dirsign * this._scrollAmount);
452 if ((this.dirsign == 1 && this.newPosition > this.stopAt) ||
453 (this.dirsign == -1 && this.newPosition < this.stopAt))
455 switch (this._behavior)
459 this.startNewDirection = true;
462 const swap = {left: "right", down: "up", up: "down", right: "left"};
463 this._direction = swap[this._direction];
464 this.newPosition = this.stopAt;
466 if ((this._direction == "up") || (this._direction == "down"))
467 this.outerDiv.scrollTop = this.newPosition;
469 this.outerDiv.scrollLeft = this.newPosition;
472 this._fireEvent("bounce", false, true);
477 this.newPosition = this.startAt;
481 this.newPosition = this.startAt;
483 if ((this._direction == "up") || (this._direction == "down"))
484 this.outerDiv.scrollTop = this.newPosition;
486 this.outerDiv.scrollLeft = this.newPosition;
488 //dispatch start event, even when this._loop == 1, comp. with IE6
489 this._fireEvent("start", false, false);
494 else if (this._loop == 1) {
495 if ((this._direction == "up") || (this._direction == "down"))
496 this.outerDiv.scrollTop = this.stopAt;
498 this.outerDiv.scrollLeft = this.stopAt;
500 this._fireEvent("finish", false, true);
505 if ((this._direction == "up") || (this._direction == "down"))
506 this.outerDiv.scrollTop = this.newPosition;
508 this.outerDiv.scrollLeft = this.newPosition;
512 var lambda = function myTimeOutFunction(){myThis._doMove(false);}
513 this.runId = window.setTimeout(lambda, this._scrollDelay);
523 if ((this._direction != "up") && (this._direction != "down")) {
524 var width = window.getComputedStyle(this, "").width;
525 this.innerDiv.parentNode.style.margin = '0 ' + width;
527 //XXX Adding the margin sometimes causes the marquee to widen,
528 // see testcase from bug bug 364434:
529 // https://bugzilla.mozilla.org/attachment.cgi?id=249233
530 // Just add a fixed width with current marquee's width for now
531 if (width != window.getComputedStyle(this, "").width) {
532 var width = window.getComputedStyle(this, "").width;
533 this.outerDiv.style.width = width;
534 this.innerDiv.parentNode.style.margin = '0 ' + width;
538 // store the original height before we add padding
539 this.innerDiv.style.padding = 0;
540 this.originalHeight = this.innerDiv.offsetHeight;
550 // hack needed to fix js error, see bug 386470
552 var lambda = function myScopeFunction() { if (myThis.init) myThis.init(); }
554 this._set_direction(this.getAttribute('direction'));
555 this._set_behavior(this.getAttribute('behavior'));
556 this._set_scrollDelay(this.getAttribute('scrolldelay'));
557 this._set_scrollAmount(this.getAttribute('scrollamount'));
558 this._set_loop(this.getAttribute('loop'));
559 this._setEventListener("start", this.getAttribute("onstart"));
560 this._setEventListener("finish", this.getAttribute("onfinish"));
561 this._setEventListener("bounce", this.getAttribute("onbounce"));
562 this.startNewDirection = true;
564 // init needs to be run after the page has loaded in order to calculate
565 // the correct height/width
566 window.addEventListener("load", lambda, false);
572 <handler event="DOMAttrModified" phase="target">
574 var attrName = event.attrName.toLowerCase();
575 var oldValue = event.prevValue.toLowerCase();
576 var newValue = event.newValue.toLowerCase();
577 var attributeRemoval = false;
578 if (event.attrChange == event.REMOVAL) {
580 attributeRemoval = true;
583 if (oldValue != newValue) {
586 if (!this._set_loop(newValue)) {
587 if (attributeRemoval) {
593 throw new Error("Invalid argument for Marquee::loop");
595 if (this.rundId == 0)
599 if (!this._set_scrollAmount(newValue)) {
600 if (attributeRemoval)
601 this._scrollAmount = 6;
603 throw new Error("Invalid argument for Marquee::scrollAmount");
607 if (!this._set_scrollDelay(newValue)) {
608 if (attributeRemoval)
609 this._scrollDelay = 85;
611 throw new Error("Invalid argument for Marquee::scrollDelay");
617 //needed to update this._scrollDelay
619 var lambda = function() {myThis._set_scrollDelay(myThis.getAttribute('scrolldelay'));}
620 window.setTimeout(lambda, 0);
623 if (!this._set_behavior(newValue)) {
624 if (attributeRemoval)
625 this._behavior = "scroll";
627 throw new Error("Invalid argument for Marquee::behavior");
630 this.startNewDirection = true;
631 if ((oldValue == "slide" && this.newPosition == this.stopAt) ||
632 newValue == "alternate" || newValue == "slide") {
638 if (!this._set_direction(newValue)) {
639 if (attributeRemoval)
640 this._direction = "left";
642 throw new Error("Invalid argument for Marquee::direction");
647 this.startNewDirection = true;
650 this._setEventListener("start", newValue);
653 this._setEventListener("finish", newValue);
656 this._setEventListener("bounce", newValue);
666 <binding id="marquee-horizontal"
667 extends="chrome://xbl-marquee/content/xbl-marquee.xml#marquee"
668 inheritstyle="false">
670 <!-- White-space isn't allowed because a marquee could be
671 inside 'white-space: pre' -->
673 <html:div style="display: -moz-box; overflow: hidden; width: -moz-available;"
674 ><html:div style="display: -moz-box;"
675 ><html:div class="innerDiv" style="display: table; border-spacing: 0;"
686 <binding id="marquee-vertical"
687 extends="chrome://xbl-marquee/content/xbl-marquee.xml#marquee"
688 inheritstyle="false">
690 <!-- White-space isn't allowed because a marquee could be
691 inside 'white-space: pre' -->
693 <html:div style="overflow: hidden; width: -moz-available;"
694 ><html:div class="innerDiv"
702 <binding id="marquee-horizontal-editable"
703 inheritstyle="false">
705 <!-- White-space isn't allowed because a marquee could be
706 inside 'white-space: pre' -->
708 <html:div style="display: -moz-box; overflow: auto; width: -moz-available;"
715 <binding id="marquee-vertical-editable"
716 inheritstyle="false">
718 <!-- White-space isn't allowed because a marquee could be
719 inside 'white-space: pre' -->
721 <html:div style="overflow: auto; height: inherit; width: -moz-available;"
722 ><children/></html:div>