toolkit/content/widgets/scrollbox.xml

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 <?xml version="1.0"?>
     2 <!-- This Source Code Form is subject to the terms of the Mozilla Public
     3    - License, v. 2.0. If a copy of the MPL was not distributed with this
     4    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
     7 <bindings id="arrowscrollboxBindings"
     8    xmlns="http://www.mozilla.org/xbl"
     9    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    10    xmlns:xbl="http://www.mozilla.org/xbl">
    12   <binding id="scrollbox-base" extends="chrome://global/content/bindings/general.xml#basecontrol">
    13     <resources>
    14       <stylesheet src="chrome://global/skin/scrollbox.css"/>
    15     </resources>
    16   </binding>
    18   <binding id="scrollbox" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
    19     <content>
    20       <xul:box class="box-inherit scrollbox-innerbox" xbl:inherits="orient,align,pack,dir" flex="1">
    21         <children/>
    22       </xul:box>
    23     </content>
    24   </binding>
    26   <binding id="arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
    27     <content>
    28       <xul:autorepeatbutton class="autorepeatbutton-up"
    29                             anonid="scrollbutton-up"
    30                             collapsed="true"
    31                             xbl:inherits="orient"
    32                             oncommand="_autorepeatbuttonScroll(event);"/>
    33       <xul:scrollbox class="arrowscrollbox-scrollbox"
    34                      anonid="scrollbox"
    35                      flex="1"
    36                      xbl:inherits="orient,align,pack,dir">
    37         <children/>
    38       </xul:scrollbox>
    39       <xul:autorepeatbutton class="autorepeatbutton-down"
    40                             anonid="scrollbutton-down"
    41                             collapsed="true"
    42                             xbl:inherits="orient"
    43                             oncommand="_autorepeatbuttonScroll(event);"/>
    44     </content>
    46     <implementation>
    47       <destructor><![CDATA[
    48         this._stopSmoothScroll();
    49       ]]></destructor>
    51       <field name="_scrollbox">
    52         document.getAnonymousElementByAttribute(this, "anonid", "scrollbox");
    53       </field>
    54       <field name="_scrollButtonUp">
    55         document.getAnonymousElementByAttribute(this, "anonid", "scrollbutton-up");
    56       </field>
    57       <field name="_scrollButtonDown">
    58         document.getAnonymousElementByAttribute(this, "anonid", "scrollbutton-down");
    59       </field>
    61       <field name="__prefBranch">null</field>
    62       <property name="_prefBranch" readonly="true">
    63         <getter><![CDATA[
    64           if (this.__prefBranch === null) {
    65             this.__prefBranch = Components.classes['@mozilla.org/preferences-service;1']
    66                                           .getService(Components.interfaces.nsIPrefBranch);
    67           }
    68           return this.__prefBranch;
    69         ]]></getter>
    70       </property>
    72       <field name="_scrollIncrement">null</field>
    73       <property name="scrollIncrement" readonly="true">
    74         <getter><![CDATA[
    75           if (this._scrollIncrement === null) {
    76             try {
    77               this._scrollIncrement = this._prefBranch
    78                                           .getIntPref("toolkit.scrollbox.scrollIncrement");
    79             }
    80             catch (ex) {
    81               this._scrollIncrement = 20;
    82             }
    83           }
    84           return this._scrollIncrement;
    85         ]]></getter>
    86       </property>
    88       <field name="_smoothScroll">null</field>
    89       <property name="smoothScroll">
    90         <getter><![CDATA[
    91           if (this._smoothScroll === null) {
    92             if (this.hasAttribute("smoothscroll")) {
    93               this._smoothScroll = (this.getAttribute("smoothscroll") == "true");
    94             } else {
    95               try {
    96                 this._smoothScroll = this._prefBranch
    97                                          .getBoolPref("toolkit.scrollbox.smoothScroll");
    98               }
    99               catch (ex) {
   100                 this._smoothScroll = true;
   101               }
   102             }
   103           }
   104           return this._smoothScroll;
   105         ]]></getter>
   106         <setter><![CDATA[
   107           this._smoothScroll = val;
   108           return val;
   109         ]]></setter>
   110       </property>
   112       <field name="_scrollBoxObject">null</field>
   113       <property name="scrollBoxObject" readonly="true">
   114         <getter><![CDATA[
   115           if (!this._scrollBoxObject) {
   116             this._scrollBoxObject =
   117               this._scrollbox.boxObject
   118                              .QueryInterface(Components.interfaces.nsIScrollBoxObject);
   119           }
   120           return this._scrollBoxObject;
   121         ]]></getter>
   122       </property>
   124       <property name="scrollClientRect" readonly="true">
   125         <getter><![CDATA[
   126           return this._scrollbox.getBoundingClientRect();
   127         ]]></getter>
   128       </property>
   130       <property name="scrollClientSize" readonly="true">
   131         <getter><![CDATA[
   132           return this.orient == "vertical" ?
   133                  this._scrollbox.clientHeight :
   134                  this._scrollbox.clientWidth;
   135         ]]></getter>
   136       </property>
   138       <property name="scrollSize" readonly="true">
   139         <getter><![CDATA[
   140           return this.orient == "vertical" ?
   141                  this._scrollbox.scrollHeight :
   142                  this._scrollbox.scrollWidth;
   143         ]]></getter>
   144       </property>
   145       <property name="scrollPaddingRect" readonly="true">
   146         <getter><![CDATA[
   147           // This assumes that this._scrollbox doesn't have any border.
   148           var outerRect = this.scrollClientRect;
   149           var innerRect = {};
   150           innerRect.left = outerRect.left - this._scrollbox.scrollLeft;
   151           innerRect.top = outerRect.top - this._scrollbox.scrollTop;
   152           innerRect.right = innerRect.left + this._scrollbox.scrollWidth;
   153           innerRect.bottom = innerRect.top + this._scrollbox.scrollHeight;
   154           return innerRect;
   155         ]]></getter>
   156       </property>
   157       <property name="scrollboxPaddingStart" readonly="true">
   158         <getter><![CDATA[
   159           var ltr = (window.getComputedStyle(this, null).direction == "ltr");
   160           var paddingStartName = ltr ? "padding-left" : "padding-right";
   161           var scrollboxStyle = window.getComputedStyle(this._scrollbox, null);
   162           return parseFloat(scrollboxStyle.getPropertyValue(paddingStartName));
   163         ]]></getter>
   164       </property>
   165       <property name="scrollPosition">
   166         <getter><![CDATA[
   167           return this.orient == "vertical" ?
   168                  this._scrollbox.scrollTop :
   169                  this._scrollbox.scrollLeft;
   170         ]]></getter>
   171         <setter><![CDATA[
   172           if (this.orient == "vertical")
   173             this._scrollbox.scrollTop = val;
   174           else
   175             this._scrollbox.scrollLeft = val;
   176           return val;
   177         ]]></setter>
   178       </property>
   180       <property name="_startEndProps" readonly="true">
   181         <getter><![CDATA[
   182           return this.orient == "vertical" ?
   183                  ["top", "bottom"] : ["left", "right"];
   184         ]]></getter>
   185       </property>
   187       <field name="_isRTLScrollbox"><![CDATA[
   188         this.orient != "vertical" &&
   189         document.defaultView.getComputedStyle(this._scrollbox, "").direction == "rtl";
   190       ]]></field>
   192       <field name="_scrollTarget">null</field>
   194       <method name="_canScrollToElement">
   195         <parameter name="element"/>
   196         <body><![CDATA[
   197           return window.getComputedStyle(element).display != "none";
   198         ]]></body>
   199       </method>
   201       <method name="ensureElementIsVisible">
   202         <parameter name="element"/>
   203         <parameter name="aSmoothScroll"/>
   204         <body><![CDATA[
   205           if (!this._canScrollToElement(element))
   206             return;
   208           var vertical = this.orient == "vertical";
   209           var rect = this.scrollClientRect;
   210           var containerStart = vertical ? rect.top : rect.left;
   211           var containerEnd = vertical ? rect.bottom : rect.right;
   212           rect = element.getBoundingClientRect();
   213           var elementStart = vertical ? rect.top : rect.left;
   214           var elementEnd = vertical ? rect.bottom : rect.right;
   216           var scrollPaddingRect = this.scrollPaddingRect;
   217           let style = window.getComputedStyle(this._scrollbox, null);
   218           var scrollContentRect = {
   219             left: scrollPaddingRect.left + parseFloat(style.paddingLeft),
   220             top: scrollPaddingRect.top + parseFloat(style.paddingTop),
   221             right: scrollPaddingRect.right - parseFloat(style.paddingRight),
   222             bottom: scrollPaddingRect.bottom - parseFloat(style.paddingBottom)
   223           };
   225           // Provide an entry point for derived bindings to adjust these values.
   226           if (this._adjustElementStartAndEnd) {
   227             [elementStart, elementEnd] =
   228               this._adjustElementStartAndEnd(element, elementStart, elementEnd);
   229           }
   231           if (elementStart <= (vertical ? scrollContentRect.top : scrollContentRect.left)) {
   232             elementStart = vertical ? scrollPaddingRect.top : scrollPaddingRect.left;
   233           }
   234           if (elementEnd >= (vertical ? scrollContentRect.bottom : scrollContentRect.right)) {
   235             elementEnd = vertical ? scrollPaddingRect.bottom : scrollPaddingRect.right;
   236           }
   238           var amountToScroll;
   240           if (elementStart < containerStart) {
   241             amountToScroll = elementStart - containerStart;
   242           } else if (containerEnd < elementEnd) {
   243             amountToScroll = elementEnd - containerEnd;
   244           } else if (this._isScrolling) {
   245             // decelerate if a currently-visible element is selected during the scroll
   246             const STOP_DISTANCE = 15;
   247             if (this._isScrolling == -1 && elementStart - STOP_DISTANCE < containerStart)
   248               amountToScroll = elementStart - containerStart;
   249             else if (this._isScrolling == 1 && containerEnd - STOP_DISTANCE < elementEnd)
   250               amountToScroll = elementEnd - containerEnd;
   251             else
   252               amountToScroll = this._isScrolling * STOP_DISTANCE;
   253           } else {
   254             return;
   255           }
   257           this._stopSmoothScroll();
   259           if (aSmoothScroll != false && this.smoothScroll) {
   260             this._smoothScrollByPixels(amountToScroll, element);
   261           } else {
   262             this.scrollByPixels(amountToScroll);
   263           }
   264         ]]></body>
   265       </method>
   267       <method name="_smoothScrollByPixels">
   268         <parameter name="amountToScroll"/>
   269         <parameter name="element"/><!-- optional -->
   270         <body><![CDATA[
   271           this._stopSmoothScroll();
   272           if (amountToScroll == 0)
   273             return;
   275           this._scrollTarget = element;
   276           // Positive amountToScroll makes us scroll right (elements fly left), negative scrolls left.
   277           this._isScrolling = amountToScroll < 0 ? -1 : 1;
   279           this._scrollAnim.start(amountToScroll);
   280         ]]></body>
   281       </method>
   283       <field name="_scrollAnim"><![CDATA[({
   284         scrollbox: this,
   285         requestHandle: 0, /* 0 indicates there is no pending request */
   286         start: function scrollAnim_start(distance) {
   287           this.distance = distance;
   288           this.startPos = this.scrollbox.scrollPosition;
   289           this.duration = Math.min(1000, Math.round(50 * Math.sqrt(Math.abs(distance))));
   290           this.startTime = window.mozAnimationStartTime;
   292           if (!this.requestHandle)
   293             this.requestHandle = window.mozRequestAnimationFrame(this);
   294         },
   295         stop: function scrollAnim_stop() {
   296           window.mozCancelAnimationFrame(this.requestHandle);
   297           this.requestHandle = 0;
   298         },
   299         sample: function scrollAnim_handleEvent(timeStamp) {
   300           const timePassed = timeStamp - this.startTime;
   301           const pos = timePassed >= this.duration ? 1 :
   302                       1 - Math.pow(1 - timePassed / this.duration, 4);
   304           this.scrollbox.scrollPosition = this.startPos + (this.distance * pos);
   306           if (pos == 1)
   307             this.scrollbox._stopSmoothScroll();
   308           else
   309             this.requestHandle = window.mozRequestAnimationFrame(this);
   310         }
   311       })]]></field>
   313       <method name="scrollByIndex">
   314         <parameter name="index"/>
   315         <parameter name="aSmoothScroll"/>
   316         <body><![CDATA[
   317           if (index == 0)
   318             return;
   320           // Each scrollByIndex call is expected to scroll the given number of
   321           // items. If a previous call is still in progress because of smooth
   322           // scrolling, we need to complete it before starting a new one.
   323           if (this._scrollTarget) {
   324             let elements = this._getScrollableElements();
   325             if (this._scrollTarget != elements[0] &&
   326                 this._scrollTarget != elements[elements.length - 1])
   327               this.ensureElementIsVisible(this._scrollTarget, false);
   328           }
   330           var rect = this.scrollClientRect;
   331           var [start, end] = this._startEndProps;
   332           var x = index > 0 ? rect[end] + 1 : rect[start] - 1;
   333           var nextElement = this._elementFromPoint(x, index);
   334           if (!nextElement)
   335             return;
   337           var targetElement;
   338           if (this._isRTLScrollbox)
   339             index *= -1;
   340           while (index < 0 && nextElement) {
   341             if (this._canScrollToElement(nextElement))
   342               targetElement = nextElement;
   343             nextElement = nextElement.previousSibling;
   344             index++;
   345           }
   346           while (index > 0 && nextElement) {
   347             if (this._canScrollToElement(nextElement))
   348               targetElement = nextElement;
   349             nextElement = nextElement.nextSibling;
   350             index--;
   351           }
   352           if (!targetElement)
   353             return;
   355           this.ensureElementIsVisible(targetElement, aSmoothScroll);
   356         ]]></body>
   357       </method>
   359       <method name="_getScrollableElements">
   360         <body><![CDATA[
   361           var nodes = this.childNodes;
   362           if (nodes.length == 1 &&
   363               nodes[0].localName == "children" &&
   364               nodes[0].namespaceURI == "http://www.mozilla.org/xbl") {
   365             nodes = document.getBindingParent(this).childNodes;
   366           }
   368           return Array.filter(nodes, this._canScrollToElement, this);
   369         ]]></body>
   370       </method>
   372       <method name="_elementFromPoint">
   373         <parameter name="aX"/>
   374         <parameter name="aPhysicalScrollDir"/>
   375         <body><![CDATA[
   376           var elements = this._getScrollableElements();
   377           if (!elements.length)
   378             return null;
   380           if (this._isRTLScrollbox)
   381             elements.reverse();
   383           var [start, end] = this._startEndProps;
   384           var low = 0;
   385           var high = elements.length - 1;
   387           if (aX < elements[low].getBoundingClientRect()[start] ||
   388               aX > elements[high].getBoundingClientRect()[end])
   389             return null;
   391           var mid, rect;
   392           while (low <= high) {
   393             mid = Math.floor((low + high) / 2);
   394             rect = elements[mid].getBoundingClientRect();
   395             if (rect[start] > aX)
   396               high = mid - 1; 
   397             else if (rect[end] < aX)
   398               low = mid + 1;
   399             else
   400               return elements[mid];
   401           }
   403           // There's no element at the requested coordinate, but the algorithm
   404           // from above yields an element next to it, in a random direction.
   405           // The desired scrolling direction leads to the correct element.
   407           if (!aPhysicalScrollDir)
   408             return null;
   410           if (aPhysicalScrollDir < 0 && rect[start] > aX)
   411             mid = Math.max(mid - 1, 0);
   412           else if (aPhysicalScrollDir > 0 && rect[end] < aX)
   413             mid = Math.min(mid + 1, elements.length - 1);
   415           return elements[mid];
   416         ]]></body>
   417       </method>
   419       <method name="_autorepeatbuttonScroll">
   420         <parameter name="event"/>
   421         <body><![CDATA[
   422           var dir = event.originalTarget == this._scrollButtonUp ? -1 : 1;
   423           if (this._isRTLScrollbox)
   424             dir *= -1;
   426           this.scrollByPixels(this.scrollIncrement * dir);
   428           event.stopPropagation();
   429         ]]></body>
   430       </method>
   432       <method name="scrollByPixels">
   433         <parameter name="px"/>
   434         <body><![CDATA[
   435           this.scrollPosition += px;
   436         ]]></body>
   437       </method>
   439       <!-- 0: idle
   440            1: scrolling right
   441           -1: scrolling left -->
   442       <field name="_isScrolling">0</field>
   443       <field name="_prevMouseScrolls">[null, null]</field>
   445       <method name="_stopSmoothScroll">
   446         <body><![CDATA[
   447           if (this._isScrolling) {
   448             this._scrollAnim.stop();
   449             this._isScrolling = 0;
   450             this._scrollTarget = null;
   451           }
   452         ]]></body>
   453       </method>
   455       <method name="_updateScrollButtonsDisabledState">
   456         <body><![CDATA[
   457           var disableUpButton = false;
   458           var disableDownButton = false;
   460           if (this.scrollPosition == 0) {
   461             // In the RTL case, this means the _last_ element in the
   462             // scrollbox is visible
   463             if (this._isRTLScrollbox) 
   464               disableDownButton = true;
   465             else
   466               disableUpButton = true;
   467           }
   468           else if (this.scrollClientSize + this.scrollPosition == this.scrollSize) {
   469             // In the RTL case, this means the _first_ element in the
   470             // scrollbox is visible
   471             if (this._isRTLScrollbox)
   472               disableUpButton = true;
   473             else
   474               disableDownButton = true;
   475           }
   477           this._scrollButtonUp.disabled = disableUpButton;
   478           this._scrollButtonDown.disabled = disableDownButton;
   479         ]]></body>
   480       </method>
   481     </implementation>
   483     <handlers>
   484       <handler event="DOMMouseScroll"><![CDATA[
   485         if (this.orient == "vertical") {
   486           // prevent horizontal scrolling from scrolling a vertical scrollbox
   487           if (event.axis == event.HORIZONTAL_AXIS)
   488             return;
   489           this.scrollByIndex(event.detail);
   490         }
   491         // We allow vertical scrolling to scroll a horizontal scrollbox
   492         // because many users have a vertical scroll wheel but no
   493         // horizontal support.
   494         // Because of this, we need to avoid scrolling chaos on trackpads
   495         // and mouse wheels that support simultaneous scrolling in both axes.
   496         // We do this by scrolling only when the last two scroll events were
   497         // on the same axis as the current scroll event.
   498         else {
   499           let isVertical = event.axis == event.VERTICAL_AXIS;
   501           if (this._prevMouseScrolls.every(function(prev) prev == isVertical))
   502             this.scrollByIndex(isVertical && this._isRTLScrollbox ? -event.detail :
   503                                                                     event.detail);
   505           if (this._prevMouseScrolls.length > 1)
   506             this._prevMouseScrolls.shift();
   507           this._prevMouseScrolls.push(isVertical);
   508         }
   510         event.stopPropagation();
   511         event.preventDefault();
   512       ]]></handler>
   514       <handler event="MozMousePixelScroll"><![CDATA[
   515         event.stopPropagation();
   516         event.preventDefault();
   517       ]]></handler>
   519       <handler event="underflow" phase="capturing"><![CDATA[
   520         // filter underflow events which were dispatched on nested scrollboxes
   521         if (event.target != this)
   522           return;
   524         // Ignore events that doesn't match our orientation.
   525         // Scrollport event orientation:
   526         //   0: vertical
   527         //   1: horizontal
   528         //   2: both
   529         if (this.orient == "vertical") {
   530           if (event.detail == 1)
   531             return;
   532         }
   533         else {    // horizontal scrollbox
   534           if (event.detail == 0)
   535             return;
   536         }
   538         this._scrollButtonUp.collapsed = true;
   539         this._scrollButtonDown.collapsed = true;
   540         try {
   541           // See bug 341047 and comments in overflow handler as to why 
   542           // try..catch is needed here
   543           let childNodes = this._getScrollableElements();
   544           if (childNodes && childNodes.length)
   545             this.ensureElementIsVisible(childNodes[0], false);
   546         }
   547         catch(e) {
   548           this._scrollButtonUp.collapsed = false;
   549           this._scrollButtonDown.collapsed = false;
   550         }
   551       ]]></handler>
   553       <handler event="overflow" phase="capturing"><![CDATA[
   554         // filter underflow events which were dispatched on nested scrollboxes
   555         if (event.target != this)
   556           return;
   558         // Ignore events that doesn't match our orientation.
   559         // Scrollport event orientation:
   560         //   0: vertical
   561         //   1: horizontal
   562         //   2: both
   563         if (this.orient == "vertical") {
   564           if (event.detail == 1)
   565             return;
   566         }
   567         else {    // horizontal scrollbox
   568           if (event.detail == 0)
   569             return;
   570         }
   572         this._scrollButtonUp.collapsed = false;
   573         this._scrollButtonDown.collapsed = false;
   574         try {
   575           // See bug 341047, the overflow event is dispatched when the 
   576           // scrollbox already is mostly destroyed. This causes some code in
   577           // _updateScrollButtonsDisabledState() to throw an error. It also
   578           // means that the scrollbarbuttons were uncollapsed when that should
   579           // not be happening, because the whole overflow event should not be
   580           // happening in that case.
   581           this._updateScrollButtonsDisabledState();
   582         } 
   583         catch(e) {
   584           this._scrollButtonUp.collapsed = true;
   585           this._scrollButtonDown.collapsed = true;
   586         }
   587       ]]></handler>
   589       <handler event="scroll" action="this._updateScrollButtonsDisabledState()"/>
   590     </handlers>
   591   </binding>
   593   <binding id="autorepeatbutton" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
   594     <content repeat="hover">
   595       <xul:image class="autorepeatbutton-icon"/>
   596     </content>
   597   </binding>
   599   <binding id="arrowscrollbox-clicktoscroll" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox">
   600     <content>
   601       <xul:toolbarbutton class="scrollbutton-up" collapsed="true"
   602                          xbl:inherits="orient"
   603                          anonid="scrollbutton-up"
   604                          onclick="_distanceScroll(event);"
   605                          onmousedown="if (event.button == 0) _startScroll(-1);"
   606                          onmouseup="if (event.button == 0) _stopScroll();"
   607                          onmouseover="_continueScroll(-1);"
   608                          onmouseout="_pauseScroll();"/>
   609       <xul:scrollbox class="arrowscrollbox-scrollbox"
   610                      anonid="scrollbox"
   611                      flex="1"
   612                      xbl:inherits="orient,align,pack,dir">
   613         <children/>
   614       </xul:scrollbox>
   615       <xul:toolbarbutton class="scrollbutton-down" collapsed="true"
   616                          xbl:inherits="orient"
   617                          anonid="scrollbutton-down"
   618                          onclick="_distanceScroll(event);"
   619                          onmousedown="if (event.button == 0) _startScroll(1);"
   620                          onmouseup="if (event.button == 0) _stopScroll();"
   621                          onmouseover="_continueScroll(1);"
   622                          onmouseout="_pauseScroll();"/>
   623     </content>
   624     <implementation implements="nsITimerCallback, nsIDOMEventListener">
   625       <constructor><![CDATA[
   626         try {
   627           this._scrollDelay = this._prefBranch
   628                                   .getIntPref("toolkit.scrollbox.clickToScroll.scrollDelay");
   629         }
   630         catch (ex) {
   631         }
   632       ]]></constructor>
   634       <destructor><![CDATA[
   635         // Release timer to avoid reference cycles.
   636         if (this._scrollTimer) {
   637           this._scrollTimer.cancel();
   638           this._scrollTimer = null;
   639         }
   640       ]]></destructor>
   642       <field name="_scrollIndex">0</field>
   643       <field name="_scrollDelay">150</field>
   645       <method name="notify">
   646         <parameter name="aTimer"/>
   647         <body>
   648         <![CDATA[
   649           if (!document)
   650             aTimer.cancel();
   652           this.scrollByIndex(this._scrollIndex);
   653         ]]>
   654         </body>
   655       </method>
   657       <field name="_arrowScrollAnim"><![CDATA[({
   658         scrollbox: this,
   659         requestHandle: 0, /* 0 indicates there is no pending request */
   660         start: function arrowSmoothScroll_start() {
   661           this.lastFrameTime = window.mozAnimationStartTime;
   662           if (!this.requestHandle)
   663             this.requestHandle = window.mozRequestAnimationFrame(this);
   664         },
   665         stop: function arrowSmoothScroll_stop() {
   666           window.mozCancelAnimationFrame(this.requestHandle);
   667           this.requestHandle = 0;
   668         },
   669         sample: function arrowSmoothScroll_handleEvent(timeStamp) {
   670           const scrollIndex = this.scrollbox._scrollIndex;
   671           const timePassed = timeStamp - this.lastFrameTime;
   672           this.lastFrameTime = timeStamp;
   674           const scrollDelta = 0.5 * timePassed * scrollIndex;
   675           this.scrollbox.scrollPosition += scrollDelta;
   677           this.requestHandle = window.mozRequestAnimationFrame(this);
   678         }
   679       })]]></field>
   681       <method name="_startScroll">
   682         <parameter name="index"/>
   683         <body><![CDATA[
   684           if (this._isRTLScrollbox)
   685             index *= -1;
   686           this._scrollIndex = index;
   687           this._mousedown = true;
   688           if (this.smoothScroll) {
   689             this._arrowScrollAnim.start();
   690             return;
   691           }
   693           if (!this._scrollTimer)
   694             this._scrollTimer =
   695               Components.classes["@mozilla.org/timer;1"]
   696                         .createInstance(Components.interfaces.nsITimer);
   697           else
   698             this._scrollTimer.cancel();
   700           this._scrollTimer.initWithCallback(this, this._scrollDelay,
   701                                              this._scrollTimer.TYPE_REPEATING_SLACK);
   702           this.notify(this._scrollTimer);
   703         ]]>
   704         </body>
   705       </method>
   707       <method name="_stopScroll">
   708         <body><![CDATA[
   709           if (this._scrollTimer)
   710             this._scrollTimer.cancel();
   711           this._mousedown = false;
   712           if (!this._scrollIndex || !this.smoothScroll)
   713             return;
   715           this.scrollByIndex(this._scrollIndex);
   716           this._scrollIndex = 0;
   717           this._arrowScrollAnim.stop();
   718         ]]></body>
   719       </method>
   721       <method name="_pauseScroll">
   722         <body><![CDATA[
   723           if (this._mousedown) {
   724             this._stopScroll();
   725             this._mousedown = true;
   726             document.addEventListener("mouseup", this, false);
   727             document.addEventListener("blur", this, true);
   728           }
   729         ]]></body>
   730       </method>
   732       <method name="_continueScroll">
   733         <parameter name="index"/>
   734         <body><![CDATA[
   735           if (this._mousedown)
   736             this._startScroll(index);
   737         ]]></body>
   738       </method>
   740       <method name="handleEvent">
   741         <parameter name="aEvent"/>
   742         <body><![CDATA[
   743           if (aEvent.type == "mouseup" ||
   744               aEvent.type == "blur" && aEvent.target == document) {
   745             this._mousedown = false;
   746             document.removeEventListener("mouseup", this, false);
   747             document.removeEventListener("blur", this, true);
   748           }
   749         ]]></body>
   750       </method>
   752       <method name="_distanceScroll">
   753         <parameter name="aEvent"/>
   754         <body><![CDATA[
   755           if (aEvent.detail < 2 || aEvent.detail > 3)
   756             return;
   758           var scrollBack = (aEvent.originalTarget == this._scrollButtonUp);
   759           var scrollLeftOrUp = this._isRTLScrollbox ? !scrollBack : scrollBack;
   760           var targetElement;
   762           if (aEvent.detail == 2) {
   763             // scroll by the size of the scrollbox
   764             let [start, end] = this._startEndProps;
   765             let x;
   766             if (scrollLeftOrUp)
   767               x = this.scrollClientRect[start] - this.scrollClientSize;
   768             else
   769               x = this.scrollClientRect[end] + this.scrollClientSize;
   770             targetElement = this._elementFromPoint(x, scrollLeftOrUp ? -1 : 1);
   772             // the next partly-hidden element will become fully visible,
   773             // so don't scroll too far
   774             if (targetElement)
   775               targetElement = scrollBack ?
   776                               targetElement.nextSibling :
   777                               targetElement.previousSibling;
   778           }
   780           if (!targetElement) {
   781             // scroll to the first resp. last element
   782             let elements = this._getScrollableElements();
   783             targetElement = scrollBack ?
   784                             elements[0] :
   785                             elements[elements.length - 1];
   786           }
   788           this.ensureElementIsVisible(targetElement);
   789         ]]></body>
   790       </method>
   792     </implementation>
   793   </binding>
   794 </bindings>

mercurial