<?xml version="1.0"?> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <bindings id="marqueeBindings" xmlns="http://www.mozilla.org/xbl" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:xbl="http://www.mozilla.org/xbl"> <binding id="marquee" bindToUntrustedContent="true"> <resources> <stylesheet src="chrome://xbl-marquee/content/xbl-marquee.css"/> </resources> <implementation> <property name="scrollAmount" exposeToUntrustedContent="true"> <getter> <![CDATA[ this._mutationActor(this._mutationObserver.takeRecords()); return this._scrollAmount; ]]> </getter> <setter> <![CDATA[ var val = parseInt(val); if (val < 0) { return; } if (isNaN(val)) { val = 0; } this.setAttribute("scrollamount", val); ]]> </setter> </property> <property name="scrollDelay" exposeToUntrustedContent="true"> <getter> <![CDATA[ this._mutationActor(this._mutationObserver.takeRecords()); var val = parseInt(this.getAttribute("scrolldelay")); if (val <= 0 || isNaN(val)) { return this._scrollDelay; } return val; ]]> </getter> <setter> var val = parseInt(val); if (val > 0 ) { this.setAttribute("scrolldelay", val); } </setter> </property> <property name="trueSpeed" exposeToUntrustedContent="true"> <getter> <![CDATA[ if (!this.hasAttribute("truespeed")) { return false; } return true; ]]> </getter> <setter> <![CDATA[ if (val) { this.setAttribute("truespeed", ""); } else { this.removeAttribute('truespeed'); } ]]> </setter> </property> <property name="direction" exposeToUntrustedContent="true"> <getter> this._mutationActor(this._mutationObserver.takeRecords()); return this._direction; </getter> <setter> <![CDATA[ if (typeof val == 'string') { val = val.toLowerCase(); } else { return; } if (val != 'left' && val != 'right' && val != 'up' && val != 'down') { val = 'left'; } this.setAttribute("direction", val); ]]> </setter> </property> <property name="behavior" exposeToUntrustedContent="true"> <getter> this._mutationActor(this._mutationObserver.takeRecords()); return this._behavior; </getter> <setter> if (typeof val == 'string') { val = val.toLowerCase(); } if (val == "alternate" || val == "slide" || val == 'scroll') { this.setAttribute("behavior", val); } </setter> </property> <property name="loop" exposeToUntrustedContent="true"> <getter> <![CDATA[ this._mutationActor(this._mutationObserver.takeRecords()); return this._loop; ]]> </getter> <setter> <![CDATA[ var val = parseInt(val); if (val == -1 || val > 0) { this.setAttribute("loop", val); } ]]> </setter> </property> <property name="onstart" exposeToUntrustedContent="true"> <getter> return this.getAttribute("onstart"); </getter> <setter> this._setEventListener("start", val, true); this.setAttribute("onstart", val); </setter> </property> <property name="onfinish" exposeToUntrustedContent="true"> <getter> return this.getAttribute("onfinish"); </getter> <setter> this._setEventListener("finish", val, true); this.setAttribute("onfinish", val); </setter> </property> <property name="onbounce" exposeToUntrustedContent="true"> <getter> return this.getAttribute("onbounce"); </getter> <setter> this._setEventListener("bounce", val, true); this.setAttribute("onbounce", val); </setter> </property> <property name="outerDiv" onget="return document.getAnonymousNodes(this)[0]" /> <property name="innerDiv" onget="return document.getAnonymousElementByAttribute(this, 'class', 'innerDiv');" /> <property name="height" exposeToUntrustedContent="true" onget="return this.getAttribute('height');" onset="this.setAttribute('height', val);" /> <property name="width" exposeToUntrustedContent="true" onget="return this.getAttribute('width');" onset="this.setAttribute('width', val);" /> <method name="_set_scrollDelay"> <parameter name="aValue"/> <body> <![CDATA[ aValue = parseInt(aValue); if (aValue <= 0) { return; } else if (isNaN(aValue)) { this._scrollDelay = 85; } else if (aValue < 60) { if (this.trueSpeed == true) { this._scrollDelay = aValue; } else { this._scrollDelay = 60; } } else { this._scrollDelay = aValue; } ]]> </body> </method> <method name="_set_scrollAmount"> <parameter name="aValue"/> <body> <![CDATA[ aValue = parseInt(aValue); if (isNaN(aValue)) { this._scrollAmount = 6; } else if (aValue < 0) { return; } else { this._scrollAmount = aValue; } ]]> </body> </method> <method name="_set_behavior"> <parameter name="aValue"/> <body> <![CDATA[ if (typeof aValue == 'string') { aValue = aValue.toLowerCase(); } if (aValue != 'alternate' && aValue != 'slide' && aValue != 'scroll') { this._behavior = 'scroll'; } else { this._behavior = aValue; } ]]> </body> </method> <method name="_set_direction"> <parameter name="aValue"/> <body> <![CDATA[ if (typeof aValue == 'string') { aValue = aValue.toLowerCase(); } if (aValue != 'left' && aValue != 'right' && aValue != 'up' && aValue != 'down') { aValue = 'left'; } if (aValue != this._direction) { this.startNewDirection = true; } this._direction = aValue; ]]> </body> </method> <method name="_set_loop"> <parameter name="aValue"/> <body> <![CDATA[ var aValue = parseInt(aValue); if (aValue == 0) { return; } if (isNaN(aValue) || aValue <= -1) { aValue = -1; } this._loop = aValue; ]]> </body> </method> <method name="_setEventListener"> <parameter name="aName"/> <parameter name="aValue"/> <parameter name="aIgnoreNextCall"/> <body> <![CDATA[ // _setEventListener is only used for setting the attribute event // handlers, which we want to ignore if our document is sandboxed // without the allow-scripts keyword. if (document.hasScriptsBlockedBySandbox) { return true; } // attribute event handlers should only be added if the // document's CSP allows it. if (!document.inlineScriptAllowedByCSP) { return true; } if (this._ignoreNextCall) { return this._ignoreNextCall = false; } if (aIgnoreNextCall) { this._ignoreNextCall = true; } if (typeof this["_on" + aName] == 'function') { this.removeEventListener(aName, this["_on" + aName], false); } switch (typeof aValue) { case "function": this["_on" + aName] = aValue; this.addEventListener(aName, this["_on" + aName], false); break; case "string": if (!aIgnoreNextCall) { try { // Function Xrays make this simple and safe. \o/ this["_on" + aName] = new window.Function("event", aValue); } catch(e) { return false; } this.addEventListener(aName, this["_on" + aName], false); } else { this["_on" + aName] = aValue; } break; case "object": this["_on" + aName] = aValue; break; default: this._ignoreNextCall = false; throw new Error("Invalid argument for Marquee::on" + aName); } return true; ]]> </body> </method> <method name="_fireEvent"> <parameter name="aName"/> <parameter name="aBubbles"/> <parameter name="aCancelable"/> <body> <![CDATA[ var e = document.createEvent("Events"); e.initEvent(aName, aBubbles, aCancelable); this.dispatchEvent(e); ]]> </body> </method> <method name="start" exposeToUntrustedContent="true"> <body> <![CDATA[ if (this.runId == 0) { var myThis = this; var lambda = function myTimeOutFunction(){myThis._doMove(false);} this.runId = window.setTimeout(lambda, this._scrollDelay - this._deltaStartStop); this._deltaStartStop = 0; } ]]> </body> </method> <method name="stop" exposeToUntrustedContent="true"> <body> <![CDATA[ if (this.runId != 0) { this._deltaStartStop = Date.now()- this._lastMoveDate; clearTimeout(this.runId); } this.runId = 0; ]]> </body> </method> <method name="_doMove"> <parameter name="aResetPosition"/> <body> <![CDATA[ this._lastMoveDate = Date.now(); //startNewDirection is true at first load and whenever the direction is changed if (this.startNewDirection) { this.startNewDirection = false; //we only want this to run once every scroll direction change var corrvalue = 0; switch (this._direction) { case "up": var height = document.defaultView.getComputedStyle(this, "").height; this.outerDiv.style.height = height; if (this.originalHeight > this.outerDiv.offsetHeight) { corrvalue = this.originalHeight - this.outerDiv.offsetHeight; } this.innerDiv.style.padding = height + " 0"; this.dirsign = 1; this.startAt = (this._behavior == 'alternate') ? (this.originalHeight - corrvalue) : 0; this.stopAt = (this._behavior == 'alternate' || this._behavior == 'slide') ? (parseInt(height) + corrvalue) : (this.originalHeight + parseInt(height)); break; case "down": var height = document.defaultView.getComputedStyle(this, "").height; this.outerDiv.style.height = height; if (this.originalHeight > this.outerDiv.offsetHeight) { corrvalue = this.originalHeight - this.outerDiv.offsetHeight; } this.innerDiv.style.padding = height + " 0"; this.dirsign = -1; this.startAt = (this._behavior == 'alternate') ? (parseInt(height) + corrvalue) : (this.originalHeight + parseInt(height)); this.stopAt = (this._behavior == 'alternate' || this._behavior == 'slide') ? (this.originalHeight - corrvalue) : 0; break; case "right": if (this.innerDiv.offsetWidth > this.outerDiv.offsetWidth) { corrvalue = this.innerDiv.offsetWidth - this.outerDiv.offsetWidth; } this.dirsign = -1; this.stopAt = (this._behavior == 'alternate' || this._behavior == 'slide') ? (this.innerDiv.offsetWidth - corrvalue) : 0; this.startAt = this.outerDiv.offsetWidth + ((this._behavior == 'alternate') ? corrvalue : (this.innerDiv.offsetWidth + this.stopAt)); break; case "left": default: if (this.innerDiv.offsetWidth > this.outerDiv.offsetWidth) { corrvalue = this.innerDiv.offsetWidth - this.outerDiv.offsetWidth; } this.dirsign = 1; this.startAt = (this._behavior == 'alternate') ? (this.innerDiv.offsetWidth - corrvalue) : 0; this.stopAt = this.outerDiv.offsetWidth + ((this._behavior == 'alternate' || this._behavior == 'slide') ? corrvalue : (this.innerDiv.offsetWidth + this.startAt)); } if (aResetPosition) { this.newPosition = this.startAt; this._fireEvent("start", false, false); } } //end if this.newPosition = this.newPosition + (this.dirsign * this._scrollAmount); if ((this.dirsign == 1 && this.newPosition > this.stopAt) || (this.dirsign == -1 && this.newPosition < this.stopAt)) { switch (this._behavior) { case 'alternate': // lets start afresh this.startNewDirection = true; // swap direction const swap = {left: "right", down: "up", up: "down", right: "left"}; this._direction = swap[this._direction]; this.newPosition = this.stopAt; if ((this._direction == "up") || (this._direction == "down")) { this.outerDiv.scrollTop = this.newPosition; } else { this.outerDiv.scrollLeft = this.newPosition; } if (this._loop != 1) { this._fireEvent("bounce", false, true); } break; case 'slide': if (this._loop > 1) { this.newPosition = this.startAt; } break; default: this.newPosition = this.startAt; if ((this._direction == "up") || (this._direction == "down")) { this.outerDiv.scrollTop = this.newPosition; } else { this.outerDiv.scrollLeft = this.newPosition; } //dispatch start event, even when this._loop == 1, comp. with IE6 this._fireEvent("start", false, false); } if (this._loop > 1) { this._loop--; } else if (this._loop == 1) { if ((this._direction == "up") || (this._direction == "down")) { this.outerDiv.scrollTop = this.stopAt; } else { this.outerDiv.scrollLeft = this.stopAt; } this.stop(); this._fireEvent("finish", false, true); return; } } else { if ((this._direction == "up") || (this._direction == "down")) { this.outerDiv.scrollTop = this.newPosition; } else { this.outerDiv.scrollLeft = this.newPosition; } } var myThis = this; var lambda = function myTimeOutFunction(){myThis._doMove(false);} this.runId = window.setTimeout(lambda, this._scrollDelay); ]]> </body> </method> <method name="init"> <body> <![CDATA[ this.stop(); if ((this._direction != "up") && (this._direction != "down")) { var width = window.getComputedStyle(this, "").width; this.innerDiv.parentNode.style.margin = '0 ' + width; //XXX Adding the margin sometimes causes the marquee to widen, // see testcase from bug bug 364434: // https://bugzilla.mozilla.org/attachment.cgi?id=249233 // Just add a fixed width with current marquee's width for now if (width != window.getComputedStyle(this, "").width) { var width = window.getComputedStyle(this, "").width; this.outerDiv.style.width = width; this.innerDiv.parentNode.style.margin = '0 ' + width; } } else { // store the original height before we add padding this.innerDiv.style.padding = 0; this.originalHeight = this.innerDiv.offsetHeight; } this._doMove(true); ]]> </body> </method> <method name="_mutationActor"> <parameter name="aMutations"/> <body> <![CDATA[ while (aMutations.length > 0) { var mutation = aMutations.shift(); var attrName = mutation.attributeName.toLowerCase(); var oldValue = mutation.oldValue; var target = mutation.target; var newValue = target.getAttribute(attrName); if (oldValue != newValue) { switch (attrName) { case "loop": target._set_loop(newValue); if (target.rundId == 0) { target.start(); } break; case "scrollamount": target._set_scrollAmount(newValue); break; case "scrolldelay": target._set_scrollDelay(newValue); target.stop(); target.start(); break; case "truespeed": //needed to update target._scrollDelay var myThis = target; var lambda = function() {myThis._set_scrollDelay(myThis.getAttribute('scrolldelay'));} window.setTimeout(lambda, 0); break; case "behavior": target._set_behavior(newValue); target.startNewDirection = true; if ((oldValue == "slide" && target.newPosition == target.stopAt) || newValue == "alternate" || newValue == "slide") { target.stop(); target._doMove(true); } break; case "direction": if (!newValue) { newValue = "left"; } target._set_direction(newValue); break; case "width": case "height": target.startNewDirection = true; break; case "onstart": target._setEventListener("start", newValue); break; case "onfinish": target._setEventListener("finish", newValue); break; case "onbounce": target._setEventListener("bounce", newValue); break; } } } ]]> </body> </method> <constructor> <![CDATA[ // Set up state. this._scrollAmount = 6; this._scrollDelay = 85; this._direction = "left"; this._behavior = "scroll"; this._loop = -1; this.dirsign = 1; this.startAt = 0; this.stopAt = 0; this.newPosition = 0; this.runId = 0; this.originalHeight = 0; this.startNewDirection = true; // hack needed to fix js error, see bug 386470 var myThis = this; var lambda = function myScopeFunction() { if (myThis.init) myThis.init(); } this._set_direction(this.getAttribute('direction')); this._set_behavior(this.getAttribute('behavior')); this._set_scrollDelay(this.getAttribute('scrolldelay')); this._set_scrollAmount(this.getAttribute('scrollamount')); this._set_loop(this.getAttribute('loop')); this._setEventListener("start", this.getAttribute("onstart")); this._setEventListener("finish", this.getAttribute("onfinish")); this._setEventListener("bounce", this.getAttribute("onbounce")); this.startNewDirection = true; this._mutationObserver = new MutationObserver(this._mutationActor); this._mutationObserver.observe(this, { attributes: true, attributeOldValue: true, attributeFilter: ['loop', 'scrollamount', 'scrolldelay', '', 'truespeed', 'behavior', 'direction', 'width', 'height', 'onstart', 'onfinish', 'onbounce'] }); // init needs to be run after the page has loaded in order to calculate // the correct height/width if (document.readyState == "complete") { lambda(); } else { window.addEventListener("load", lambda, false); } ]]> </constructor> </implementation> </binding> <binding id="marquee-horizontal" bindToUntrustedContent="true" extends="chrome://xbl-marquee/content/xbl-marquee.xml#marquee" inheritstyle="false"> <!-- White-space isn't allowed because a marquee could be inside 'white-space: pre' --> <content> <html:div style="display: -moz-box; overflow: hidden; width: -moz-available;" ><html:div style="display: -moz-box;" ><html:div class="innerDiv" style="display: table; border-spacing: 0;" ><html:div ><children /></html:div ></html:div ></html:div ></html:div> </content> </binding> <binding id="marquee-vertical" bindToUntrustedContent="true" extends="chrome://xbl-marquee/content/xbl-marquee.xml#marquee" inheritstyle="false"> <!-- White-space isn't allowed because a marquee could be inside 'white-space: pre' --> <content> <html:div style="overflow: hidden; width: -moz-available;" ><html:div class="innerDiv" ><children /></html:div ></html:div> </content> </binding> <binding id="marquee-horizontal-editable" bindToUntrustedContent="true" inheritstyle="false"> <!-- White-space isn't allowed because a marquee could be inside 'white-space: pre' --> <content> <html:div style="display: inline-block; overflow: auto; width: -moz-available;" ><children /></html:div> </content> </binding> <binding id="marquee-vertical-editable" bindToUntrustedContent="true" inheritstyle="false"> <!-- White-space isn't allowed because a marquee could be inside 'white-space: pre' --> <content> <html:div style="overflow: auto; height: inherit; width: -moz-available;" ><children/></html:div> </content> </binding> </bindings>