diff options
Diffstat (limited to 'layout/style/xbl-marquee')
-rw-r--r-- | layout/style/xbl-marquee/jar.mn | 8 | ||||
-rw-r--r-- | layout/style/xbl-marquee/moz.build | 7 | ||||
-rw-r--r-- | layout/style/xbl-marquee/xbl-marquee.css | 12 | ||||
-rw-r--r-- | layout/style/xbl-marquee/xbl-marquee.xml | 733 |
4 files changed, 760 insertions, 0 deletions
diff --git a/layout/style/xbl-marquee/jar.mn b/layout/style/xbl-marquee/jar.mn new file mode 100644 index 000000000..9247cb4a1 --- /dev/null +++ b/layout/style/xbl-marquee/jar.mn @@ -0,0 +1,8 @@ +# 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/. + +toolkit.jar: +% content xbl-marquee %content/xbl-marquee/ contentaccessible=yes + content/xbl-marquee/xbl-marquee.xml + content/xbl-marquee/xbl-marquee.css diff --git a/layout/style/xbl-marquee/moz.build b/layout/style/xbl-marquee/moz.build new file mode 100644 index 000000000..eb4454d28 --- /dev/null +++ b/layout/style/xbl-marquee/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file diff --git a/layout/style/xbl-marquee/xbl-marquee.css b/layout/style/xbl-marquee/xbl-marquee.css new file mode 100644 index 000000000..e6d3ee94b --- /dev/null +++ b/layout/style/xbl-marquee/xbl-marquee.css @@ -0,0 +1,12 @@ +/* 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/. */ + +/* PRINT ONLY rules */ +@media print { + + marquee > * > * { + margin: 0 !important; + padding: 0 !important; + } /* This hack is needed until bug 119078 gets fixed */ +} diff --git a/layout/style/xbl-marquee/xbl-marquee.xml b/layout/style/xbl-marquee/xbl-marquee.xml new file mode 100644 index 000000000..bb837624d --- /dev/null +++ b/layout/style/xbl-marquee/xbl-marquee.xml @@ -0,0 +1,733 @@ +<?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> |