Bug 1495357 - convert wizard-buttons binding to a Custom Element, r=bgrins
authorAlexander Surkov <surkov.alexander@gmail.com>
Thu, 04 Apr 2019 14:14:52 +0000
changeset 468016 33510bccced0c4691cfbe8214a1ca404bb401271
parent 468015 ca7e5845a89512d93f2106b33d94ab9006230e76
child 468017 50d64901b71fe8e6ed7d9730467bf4b4d6060f01
push id35815
push userccoroiu@mozilla.com
push dateThu, 04 Apr 2019 21:55:21 +0000
treeherdermozilla-central@50d64901b71f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins
bugs1495357
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1495357 - convert wizard-buttons binding to a Custom Element, r=bgrins Differential Revision: https://phabricator.services.mozilla.com/D23392
.eslintignore
testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/wizard.py
toolkit/content/jar.mn
toolkit/content/widgets/wizard.js
toolkit/content/widgets/wizard.xml
toolkit/content/xul.css
--- a/.eslintignore
+++ b/.eslintignore
@@ -336,31 +336,31 @@ testing/xpcshell/dns-packet/**
 testing/xpcshell/node-ip/**
 
 
 # Third party services
 services/common/kinto-http-client.js
 services/common/kinto-offline-client.js
 
 # toolkit/ exclusions
+toolkit/content/widgets/wizard.xml
 
 # Intentionally invalid JS
 toolkit/components/workerloader/tests/moduleF-syntax-error.js
 
 # Tests old non-star function generators
 toolkit/modules/tests/xpcshell/test_task.js
 
 # External code:
 browser/components/payments/res/vendor/*
 toolkit/components/reader/Readability.js
 toolkit/components/reader/JSDOMParser.js
 
 # Uses preprocessing
 toolkit/components/reader/Readerable.jsm
-toolkit/content/widgets/wizard.xml
 toolkit/modules/AppConstants.jsm
 toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js
 tools/tryselect/selectors/chooser/templates/chooser.html
 
 # Third party
 toolkit/modules/third_party/**
 third_party/**
 
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/wizard.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/wizard.py
@@ -49,37 +49,37 @@ class Wizard(UIBaseLib):
     # Properties for visual buttons of the wizard #
 
     @property
     def _buttons(self):
         return self.element.find_element(By.ANON_ATTRIBUTE, {'anonid': 'Buttons'})
 
     @property
     def cancel_button(self):
-        return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'cancel'})
+        return self._buttons.find_element(By.CSS_SELECTOR, '[dlgtype="cancel"]')
 
     @property
     def extra1_button(self):
-        return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'extra1'})
+        return self._buttons.find_element(By.CSS_SELECTOR, '[dlgtype="extra1"]')
 
     @property
     def extra2_button(self):
-        return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'extra2'})
+        return self._buttons.find_element(By.CSS_SELECTOR, '[dlgtype="extra2"]')
 
     @property
     def previous_button(self):
-        return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'back'})
+        return self._buttons.find_element(By.CSS_SELECTOR, '[dlgtype="back"]')
 
     @property
     def finish_button(self):
-        return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'finish'})
+        return self._buttons.find_element(By.CSS_SELECTOR, '[dlgtype="finish"]')
 
     @property
     def next_button(self):
-        return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'next'})
+        return self._buttons.find_element(By.CSS_SELECTOR, '[dlgtype="next"]')
 
     # Properties for visual panels of the wizard #
 
     @property
     def checking(self):
         """The checking for updates panel.
 
         :returns: :class:`CheckingPanel` instance.
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -77,17 +77,17 @@ toolkit.jar:
    content/global/bindings/spinner.js          (widgets/spinner.js)
    content/global/bindings/tabbox.xml          (widgets/tabbox.xml)
    content/global/bindings/text.xml            (widgets/text.xml)
    content/global/elements/text.js             (widgets/text.js)
 *  content/global/bindings/textbox.xml         (widgets/textbox.xml)
    content/global/bindings/timekeeper.js       (widgets/timekeeper.js)
    content/global/bindings/timepicker.js       (widgets/timepicker.js)
    content/global/bindings/toolbarbutton.xml   (widgets/toolbarbutton.xml)
-*  content/global/bindings/wizard.xml          (widgets/wizard.xml)
+   content/global/bindings/wizard.xml          (widgets/wizard.xml)
    content/global/elements/autocomplete-popup.js              (widgets/autocomplete-popup.js)
    content/global/elements/autocomplete-richlistitem.js       (widgets/autocomplete-richlistitem.js)
    content/global/elements/browser-custom-element.js          (widgets/browser-custom-element.js)
    content/global/elements/checkbox.js         (widgets/checkbox.js)
    content/global/elements/datetimebox.js      (widgets/datetimebox.js)
    content/global/elements/findbar.js          (widgets/findbar.js)
    content/global/elements/editor.js           (widgets/editor.js)
    content/global/elements/general.js          (widgets/general.js)
--- a/toolkit/content/widgets/wizard.js
+++ b/toolkit/content/widgets/wizard.js
@@ -2,16 +2,20 @@
  * 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/. */
 
 "use strict";
 
 // This is loaded into chrome windows with the subscript loader. Wrap in
 // a block to prevent accidentally leaking globals onto `window`.
 {
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+const kDTDs = [ "chrome://global/locale/wizard.dtd" ];
+
 class MozWizardPage extends MozXULElement {
   constructor() {
     super();
     this.pageIndex = -1;
   }
   get pageid() {
     return this.getAttribute("pageid");
   }
@@ -24,9 +28,149 @@ class MozWizardPage extends MozXULElemen
   set next(val) {
     this.setAttribute("next", val);
     this.parentNode._accessMethod = "random";
     return val;
   }
 }
 
 customElements.define("wizardpage", MozWizardPage);
+
+class MozWizardButtons extends MozXULElement {
+  connectedCallback() {
+    this.textContent = "";
+    this.appendChild(MozXULElement.parseXULToFragment(this._markup, kDTDs));
+
+    this._wizardButtonDeck = this.querySelector(".wizard-next-deck");
+
+    this.initializeAttributeInheritance();
+
+    const listeners = [
+      ["back", () => document.documentElement.rewind()],
+      ["next", () => document.documentElement.advance()],
+      ["finish", () => document.documentElement.advance()],
+      ["cancel", () => document.documentElement.cancel()],
+      ["extra1", () => document.documentElement.extra1()],
+      ["extra2", () => document.documentElement.extra2()],
+    ];
+    for (let [name, listener] of listeners) {
+      let btn = this.getButton(name);
+      if (btn) {
+        btn.addEventListener("command", listener);
+      }
+    }
+  }
+
+  static get inheritedAttributes() {
+    return AppConstants.platform == "macosx" ? {
+      "[dlgtype='finish']": "hidefinishbutton",
+      "[dlgtype='next']": "lastpage",
+    } : null;
+  }
+
+  get _markup() {
+    if (AppConstants.platform == "macosx") {
+      return `
+        <vbox flex="1">
+          <hbox class="wizard-buttons-btm">
+            <button class="wizard-button" dlgtype="extra1" hidden="true"/>
+            <button class="wizard-button" dlgtype="extra2" hidden="true"/>
+            <button label="&button-cancel-mac.label;"
+                    class="wizard-button" dlgtype="cancel"/>
+            <spacer flex="1"/>
+            <button label="&button-back-mac.label;"
+                    accesskey="&button-back-mac.accesskey;"
+                    class="wizard-button wizard-nav-button" dlgtype="back"/>
+            <button label="&button-next-mac.label;"
+                    accesskey="&button-next-mac.accesskey;"
+                    class="wizard-button wizard-nav-button" dlgtype="next"
+                    default="true" />
+            <button label="&button-finish-mac.label;" class="wizard-button"
+                    dlgtype="finish" default="true" />
+          </hbox>
+        </vbox>`;
+    }
+
+    let buttons = AppConstants.platform == "linux" ? `
+      <button label="&button-cancel-unix.label;"
+              class="wizard-button"
+              dlgtype="cancel"/>
+      <spacer style="width: 24px;"/>
+      <button label="&button-back-unix.label;"
+              accesskey="&button-back-unix.accesskey;"
+              class="wizard-button" dlgtype="back"/>
+      <deck class="wizard-next-deck">
+        <hbox>
+          <button label="&button-finish-unix.label;"
+                  class="wizard-button"
+                  dlgtype="finish" default="true" flex="1"/>
+        </hbox>
+        <hbox>
+          <button label="&button-next-unix.label;"
+                  accesskey="&button-next-unix.accesskey;"
+                  class="wizard-button" dlgtype="next"
+                  default="true" flex="1"/>
+        </hbox>
+      </deck>` : `
+      <button label="&button-back-win.label;"
+              accesskey="&button-back-win.accesskey;"
+              class="wizard-button" dlgtype="back"/>
+      <deck class="wizard-next-deck">
+        <hbox>
+          <button label="&button-finish-win.label;"
+                  class="wizard-button"
+                  dlgtype="finish" default="true" flex="1"/>
+        </hbox>
+        <hbox>
+          <button label="&button-next-win.label;"
+                  accesskey="&button-next-win.accesskey;"
+                  class="wizard-button" dlgtype="next"
+                  default="true" flex="1"/>
+        </hbox>
+      </deck>
+      <button label="&button-cancel-win.label;"
+              class="wizard-button"
+              dlgtype="cancel"/>`;
+
+    return `
+      <vbox class="wizard-buttons-box-1" flex="1">
+        <separator class="wizard-buttons-separator groove"/>
+        <hbox class="wizard-buttons-box-2">
+          <button class="wizard-button" dlgtype="extra1" hidden="true"/>
+          <button class="wizard-button" dlgtype="extra2" hidden="true"/>
+          <spacer flex="1" anonid="spacer"/>
+          ${buttons}
+        </hbox>
+      </vbox>`;
+  }
+
+  onPageChange() {
+    if (AppConstants.platform == "macosx") {
+      this.setAttribute("hidefinishbutton",
+                        !(this.getAttribute("lastpage") == "true"));
+    } else if (this.getAttribute("lastpage") == "true") {
+      this._wizardButtonDeck.setAttribute("selectedIndex", 0);
+    } else {
+      this._wizardButtonDeck.setAttribute("selectedIndex", 1);
+    }
+  }
+
+  getButton(type) {
+    return this.querySelector(`[dlgtype="${type}"]`);
+  }
+
+  get defaultButton() {
+    const kXULNS =
+      "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+    let buttons = this._wizardButtonDeck.selectedPanel.
+      getElementsByTagNameNS(kXULNS, "button");
+    for (let i = 0; i < buttons.length; i++) {
+      if (buttons[i].getAttribute("default") == "true" &&
+          !buttons[i].hidden && !buttons[i].disabled) {
+        return buttons[i];
+      }
+    }
+    return null;
+  }
 }
+
+customElements.define("wizard-buttons", MozWizardButtons);
+}
--- a/toolkit/content/widgets/wizard.xml
+++ b/toolkit/content/widgets/wizard.xml
@@ -1,44 +1,39 @@
 <?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/. -->
 
 
-<!DOCTYPE bindings [
-  <!ENTITY % wizardDTD SYSTEM "chrome://global/locale/wizard.dtd">
-  %wizardDTD;
-]>
-
 <bindings id="wizardBindings"
    xmlns="http://www.mozilla.org/xbl"
    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="wizard">
     <content>
       <xul:hbox class="wizard-header" anonid="Header"/>
 
       <xul:deck class="wizard-page-box" flex="1" anonid="Deck">
         <children includes="wizardpage"/>
       </xul:deck>
       <children/>
 
-      <xul:hbox class="wizard-buttons" anonid="Buttons" xbl:inherits="pagestep,firstpage,lastpage"/>
+      <xul:wizard-buttons class="wizard-buttons" anonid="Buttons" xbl:inherits="pagestep,firstpage,lastpage"/>
     </content>
 
     <implementation>
       <property name="title" onget="return document.title;"
                              onset="return document.title = val;"/>
 
       <property name="canAdvance" onget="return this._canAdvance;"
-                                  onset="this._nextButton.disabled = !val; return this._canAdvance = val;"/>
+                                  onset="this.getButton('next').disabled = !val; return this._canAdvance = val;"/>
       <property name="canRewind" onget="return this._canRewind;"
-                                 onset="this._backButton.disabled = !val; return this._canRewind = val;"/>
+                                 onset="this.getButton('back').disabled = !val; return this._canRewind = val;"/>
 
       <property name="pageStep" readonly="true" onget="return this._pageStack.length"/>
 
       <field name="pageCount">0</field>
 
       <field name="_accessMethod">null</field>
       <field name="_pageStack">null</field>
       <field name="_currentPage">null</field>
@@ -62,23 +57,23 @@
 
           // Setting this attribute allows wizard's clients to dynamically
           // change the styles of each page based on purpose of the page.
           this.setAttribute("currentpageid", val.pageid);
           if (this.onFirstPage) {
             this.canRewind = false;
             this.setAttribute("firstpage", "true");
             if (/Linux/.test(navigator.platform)) {
-              this._backButton.setAttribute('hidden', 'true');
+              this.getButton("back").setAttribute("hidden", "true");
             }
           } else {
             this.canRewind = true;
             this.setAttribute("firstpage", "false");
             if (/Linux/.test(navigator.platform)) {
-              this._backButton.setAttribute('hidden', 'false');
+              this.getButton("back").setAttribute("hidden", "false");
             }
           }
 
           if (this.onLastPage) {
             this.canAdvance = true;
             this.setAttribute("lastpage", "true");
           } else {
             this.setAttribute("lastpage", "false");
@@ -123,43 +118,26 @@
                        (this._accessMethod == "random" && cp.next == ""));
          ]]></getter>
       </property>
 
       <method name="getButton">
         <parameter name="aDlgType"/>
         <body>
         <![CDATA[
-          var btns = this.getElementsByAttribute("dlgtype", aDlgType);
-          return btns.item(0) ? btns[0] : document.getAnonymousElementByAttribute(this._wizardButtons, "dlgtype", aDlgType);
+          return this._wizardButtons.getButton(aDlgType);
         ]]>
         </body>
       </method>
 
       <field name="_canAdvance"/>
       <field name="_canRewind"/>
       <field name="_wizardHeader"/>
       <field name="_wizardButtons"/>
       <field name="_deck"/>
-      <field name="_backButton"/>
-      <field name="_nextButton"/>
-      <field name="_cancelButton"/>
-
-      <!-- functions to be added as oncommand listeners to the wizard buttons -->
-      <field name="_backFunc">(function() { document.documentElement.rewind(); })</field>
-      <field name="_nextFunc">(function() { document.documentElement.advance(); })</field>
-      <field name="_finishFunc">(function() { document.documentElement.advance(); })</field>
-      <field name="_cancelFunc">(function() { document.documentElement.cancel(); })</field>
-      <field name="_extra1Func">(function() { document.documentElement.extra1(); })</field>
-      <field name="_extra2Func">(function() { document.documentElement.extra2(); })</field>
-
-      <field name="_closeHandler">(function(event) {
-        if (document.documentElement.cancel())
-          event.preventDefault();
-      })</field>
 
       <constructor><![CDATA[
         this._canAdvance = true;
         this._canRewind = false;
         this._hasLoaded = false;
 
         this._pageStack = [];
 
@@ -196,28 +174,27 @@
                 <label class="wizard-header-description"/>
               </vbox>
               <image class="wizard-header-icon"/>
             </hbox>`
           )
         );
 
         this._wizardButtons = document.getAnonymousElementByAttribute(this, "anonid", "Buttons");
-        this._deck = document.getAnonymousElementByAttribute(this, "anonid", "Deck");
+        customElements.upgrade(this._wizardButtons);
 
-        this._initWizardButton("back");
-        this._initWizardButton("next");
-        this._initWizardButton("finish");
-        this._initWizardButton("cancel");
-        this._initWizardButton("extra1");
-        this._initWizardButton("extra2");
+        this._deck = document.getAnonymousElementByAttribute(this, "anonid", "Deck");
 
         this._initPages();
 
-        window.addEventListener("close", this._closeHandler);
+        window.addEventListener("close", (event) => {
+          if (document.documentElement.cancel()) {
+            event.preventDefault();
+          }
+        });
 
         // start off on the first page
         this.pageCount = this.wizardPages.length;
         this.advance();
 
         // give focus to the first focusable element in the dialog
         window.addEventListener("load", this._setInitialFocus);
       ]]></constructor>
@@ -381,28 +358,16 @@
             page.pageIndex = i;
             if (page.next != "")
               meth = "random";
           }
           this._accessMethod = meth;
         ]]></body>
       </method>
 
-      <method name="_initWizardButton">
-        <parameter name="aName"/>
-        <body><![CDATA[
-         var btn = document.getAnonymousElementByAttribute(this._wizardButtons, "dlgtype", aName);
-         if (btn) {
-           btn.addEventListener("command", this["_"+aName+"Func"]);
-           this["_"+aName+"Button"] = btn;
-         }
-         return btn;
-        ]]></body>
-      </method>
-
       <method name="_adjustWizardHeader">
         <body><![CDATA[
           var label = this.currentPage.getAttribute("label");
           if (!label && this.onFirstPage && this._bundle) {
             if (/Mac/.test(navigator.platform)) {
               label = this._bundle.GetStringFromName("default-first-title-mac");
             } else {
               label = this._bundle.formatStringFromName("default-first-title", [this.title], 1);
@@ -455,121 +420,9 @@
       <handler event="keypress" keycode="VK_RETURN"
                group="system" action="this._hitEnter(event)"/>
       <handler event="keypress" keycode="VK_ESCAPE" group="system">
         if (!event.defaultPrevented)
           this.cancel();
       </handler>
     </handlers>
   </binding>
-
-#ifdef XP_MACOSX
-  <binding id="wizard-buttons">
-    <content>
-      <xul:vbox flex="1">
-        <xul:hbox class="wizard-buttons-btm">
-          <xul:button class="wizard-button" dlgtype="extra1" hidden="true"/>
-          <xul:button class="wizard-button" dlgtype="extra2" hidden="true"/>
-          <xul:button label="&button-cancel-mac.label;" class="wizard-button" dlgtype="cancel"/>
-          <xul:spacer flex="1"/>
-          <xul:button label="&button-back-mac.label;" accesskey="&button-back-mac.accesskey;"
-                      class="wizard-button wizard-nav-button" dlgtype="back"/>
-          <xul:button label="&button-next-mac.label;" accesskey="&button-next-mac.accesskey;"
-                      class="wizard-button wizard-nav-button" dlgtype="next"
-                      default="true" xbl:inherits="hidden=lastpage" />
-          <xul:button label="&button-finish-mac.label;" class="wizard-button"
-                      dlgtype="finish" default="true" xbl:inherits="hidden=hidefinishbutton" />
-        </xul:hbox>
-      </xul:vbox>
-    </content>
-
-    <implementation>
-      <method name="onPageChange">
-        <body><![CDATA[
-          this.setAttribute("hidefinishbutton", !(this.getAttribute("lastpage") == "true"));
-        ]]></body>
-      </method>
-    </implementation>
-
-  </binding>
-
-#else
-
-  <binding id="wizard-buttons">
-    <content>
-      <xul:vbox class="wizard-buttons-box-1" flex="1">
-        <xul:separator class="wizard-buttons-separator groove"/>
-        <xul:hbox class="wizard-buttons-box-2">
-          <xul:button class="wizard-button" dlgtype="extra1" hidden="true"/>
-          <xul:button class="wizard-button" dlgtype="extra2" hidden="true"/>
-          <xul:spacer flex="1" anonid="spacer"/>
-#ifdef XP_UNIX
-          <xul:button label="&button-cancel-unix.label;" class="wizard-button"
-                      dlgtype="cancel"/>
-          <xul:spacer style="width: 24px"/>
-          <xul:button label="&button-back-unix.label;" accesskey="&button-back-unix.accesskey;"
-                      class="wizard-button" dlgtype="back"/>
-          <xul:deck class="wizard-next-deck" anonid="WizardButtonDeck">
-            <xul:hbox>
-              <xul:button label="&button-finish-unix.label;" class="wizard-button"
-                          dlgtype="finish" default="true" flex="1"/>
-            </xul:hbox>
-            <xul:hbox>
-              <xul:button label="&button-next-unix.label;" accesskey="&button-next-unix.accesskey;"
-                          class="wizard-button" dlgtype="next"
-                          default="true" flex="1"/>
-            </xul:hbox>
-          </xul:deck>
-#else
-          <xul:button label="&button-back-win.label;" accesskey="&button-back-win.accesskey;"
-                      class="wizard-button" dlgtype="back"/>
-          <xul:deck class="wizard-next-deck" anonid="WizardButtonDeck">
-            <xul:hbox>
-              <xul:button label="&button-finish-win.label;" class="wizard-button"
-                          dlgtype="finish" default="true" flex="1"/>
-            </xul:hbox>
-            <xul:hbox>
-              <xul:button label="&button-next-win.label;" accesskey="&button-next-win.accesskey;"
-                          class="wizard-button" dlgtype="next"
-                          default="true" flex="1"/>
-            </xul:hbox>
-          </xul:deck>
-          <xul:button label="&button-cancel-win.label;" class="wizard-button"
-                      dlgtype="cancel"/>
-#endif
-        </xul:hbox>
-      </xul:vbox>
-    </content>
-
-    <implementation>
-      <field name="_wizardButtonDeck" readonly="true">
-        document.getAnonymousElementByAttribute(this, "anonid", "WizardButtonDeck");
-      </field>
-
-      <method name="onPageChange">
-        <body><![CDATA[
-          if (this.getAttribute("lastpage") == "true") {
-            this._wizardButtonDeck.setAttribute("selectedIndex", 0);
-          } else {
-            this._wizardButtonDeck.setAttribute("selectedIndex", 1);
-          }
-        ]]></body>
-      </method>
-
-      <property name="defaultButton" readonly="true">
-        <getter><![CDATA[
-          const kXULNS =
-            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-          var buttons = this._wizardButtonDeck.selectedPanel
-                            .getElementsByTagNameNS(kXULNS, "button");
-          for (var i = 0; i < buttons.length; i++) {
-            if (buttons[i].getAttribute("default") == "true" &&
-                !buttons[i].hidden && !buttons[i].disabled)
-              return buttons[i];
-          }
-          return null;
-        ]]></getter>
-      </property>
-    </implementation>
-  </binding>
-#endif
-
 </bindings>
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -667,20 +667,16 @@ wizard:root /* override :root from above
   height: 30em;
 }
 
 wizardpage {
   -moz-box-orient: vertical;
   overflow: auto;
 }
 
-.wizard-buttons {
-  -moz-binding: url("chrome://global/content/bindings/wizard.xml#wizard-buttons");
-}
-
 /********** Rich Listbox ********/
 
 richlistbox {
   -moz-user-focus: normal;
   -moz-box-orient: vertical;
 }
 
 richlistitem {