From: Kohei Yoshino Date: Fri, 19 Jul 2019 19:49:27 +0000 (-0400) Subject: Bug 1563360 - End key does not scroll all the way to the bottom of BMO page X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e711a893f47b824343a3c6fc1c4b815390041d8a;p=thirdparty%2Fbugzilla.git Bug 1563360 - End key does not scroll all the way to the bottom of BMO page --- diff --git a/extensions/BugModal/web/bug_modal.css b/extensions/BugModal/web/bug_modal.css index f4a9710bc..2c8532cf4 100644 --- a/extensions/BugModal/web/bug_modal.css +++ b/extensions/BugModal/web/bug_modal.css @@ -839,8 +839,13 @@ h3.change-name a { text-decoration: none; } +.change-set .attachment .outer:empty { + width: 426px; + height: 240px; +} + .change-set .attachment button.outer { - padding: 0; + padding: 0 !important; box-shadow: none; font-weight: normal; transition: none; diff --git a/extensions/BugModal/web/comments.js b/extensions/BugModal/web/comments.js index b39f22efe..35f35e6e4 100644 --- a/extensions/BugModal/web/comments.js +++ b/extensions/BugModal/web/comments.js @@ -473,7 +473,7 @@ $(function() { * Reference or define the Bugzilla app namespace. * @namespace */ -var Bugzilla = Bugzilla || {}; +var Bugzilla = Bugzilla || {}; // eslint-disable-line no-var /** * Reference or define the Review namespace. @@ -493,11 +493,7 @@ Bugzilla.BugModal.Comments = class Comments { } /** - * Prepare to show image, media and text attachments inline if possible. For a better performance, this functionality - * uses the Intersection Observer API to show attachments when the associated comment goes into the viewport, when the - * page is scrolled down or the collapsed comment is expanded. This also utilizes the Network Information API to save - * @see https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API - * @see https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API + * Prepare to show an attachment inline if possible. */ prepare_inline_attachments() { // Check the connectivity, API support, user setting, bug security and sensitive keywords @@ -508,15 +504,6 @@ Bugzilla.BugModal.Comments = class Comments { return; } - const observer = new IntersectionObserver(entries => entries.forEach(entry => { - const $att = entry.target; - - if (entry.intersectionRatio > 0) { - observer.unobserve($att); - this.show_attachment($att); - } - }), { root: document.querySelector('#bugzilla-body') }); - document.querySelectorAll('.change-set').forEach($set => { // Skip if the comment has the `hide-attachment` tag const $comment = $set.querySelector('.comment:not([data-tags~="hide-attachment"])'); @@ -524,80 +511,162 @@ Bugzilla.BugModal.Comments = class Comments { const $attachment = $set.querySelector('.attachment:not(.obsolete):not(.deleted)'); if ($comment && $attachment) { - observer.observe($attachment); + this.attachment = new Bugzilla.InlineAttachment($attachment); } }); } +}; +/** + * Implement the inline attachment renderer that will be used for bug comments. For a better performance, this + * functionality uses the Intersection Observer API to show an attachment when the comment goes into the viewport. + */ +Bugzilla.InlineAttachment = class InlineAttachment { /** - * Load and show an image, audio, video or text attachment. - * @param {HTMLElement} $att An attachment wrapper element. + * Initiate a new InlineAttachment instance. + * @param {HTMLElement} $attachment Attachment container on each comment. */ - async show_attachment($att) { - const id = Number($att.dataset.id); - const link = $att.querySelector('.link').href; - const name = $att.querySelector('[itemprop="name"]').content; - const type = $att.querySelector('[itemprop="encodingFormat"]').content; - const size = Number($att.querySelector('[itemprop="contentSize"]').content); - - // Skip if the attachment is marked as binary - if (type.match(/^application\/(?:octet-stream|binary)$/)) { - return; - } + constructor($attachment) { + this.$attachment = $attachment; + this.id = Number(this.$attachment.dataset.id); + this.link = this.$attachment.querySelector('.link').href; + this.name = this.$attachment.querySelector('[itemprop="name"]').content; + this.size = Number(this.$attachment.querySelector('[itemprop="contentSize"]').content); + this.type = this.$attachment.querySelector('[itemprop="encodingFormat"]').content; + this.media = this.type.split('/').shift(); // Show image smaller than 2 MB, excluding SVG and non-standard formats - if (type.match(/^image\/(?!vnd|svg).+$/) && size < 2000000) { - $att.insertAdjacentHTML('beforeend', ` - ${name.htmlEncode()}`); + if (this.type.match(/^image\/(?!vnd|svg).+$/) && this.size < 2000000) { + this.show_image(); + } + + // Show audio and video + if (this.type.match(/^(?:audio|video)\/(?!vnd).+$/) && document.createElement(this.media).canPlayType(this.type)) { + this.show_media(); + } + + // Detect text (code from attachment.js) + this.is_patch = this.$attachment.matches('.patch'); + this.is_markdown = !!this.name.match(/\.(?:md|mkdn?|mdown|markdown)$/); + this.is_source = !!this.name.match(/\.(?:cpp|es|h|js|json|rs|rst|sh|toml|ts|tsx|xml|yaml|yml)$/); + this.is_text = this.type.match(/^text\/(?!x-).+$/) || this.is_patch || this.is_markdown || this.is_source; + + // Show text smaller than 50 KB + if (this.is_text && this.size < 50000) { + this.show_text(); + } + } + + /** + * Show an image attachment. + */ + async show_image() { + // Insert a placeholder first + this.$attachment.insertAdjacentHTML('beforeend', ``); + + // Wait until the container goes into the viewport + await this.watch_visibility(); + + const $image = new Image(); + + try { + await new Promise((resolve, reject) => { + $image.addEventListener('load', () => resolve(), { once: true }); + $image.addEventListener('error', () => reject(), { once: true }); + $image.src = this.link; + }); + + $image.setAttribute('itemprop', 'image'); + $image.alt = this.name; + this.$outer.appendChild($image); // Add lightbox support - $att.querySelector('.outer.lightbox').addEventListener('click', event => { - if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) { - return; + this.$outer.addEventListener('click', event => { + if (!event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey) { + event.preventDefault(); + lb_show(event.target); } + }); + } catch (ex) { + this.$outer.remove(); + } + } - event.preventDefault(); - lb_show(event.target); + /** + * Show an audio or video attachment. + */ + async show_media() { + // Insert a placeholder first + this.$attachment.insertAdjacentHTML('beforeend', ''); + + // Wait until the container goes into the viewport + await this.watch_visibility(); + + const $media = document.createElement(this.media); + + try { + await new Promise((resolve, reject) => { + $media.addEventListener('loadedmetadata', () => resolve(), { once: true }); + $media.addEventListener('error', () => reject(), { once: true }); + $media.src = this.link; }); + + $media.setAttribute('itemprop', this.media); + $media.controls = true; + this.$outer.appendChild($media); + } catch (ex) { + this.$outer.remove(); } + } - // Show audio and video - if (type.match(/^(?:audio|video)\/(?!vnd).+$/)) { - const media = type.split('/')[0]; + /** + * Show a text attachment. Fetch the raw text via the API. + */ + async show_text() { + // Insert a placeholder first + this.$attachment.insertAdjacentHTML('beforeend', + ``); + + // Wait until the container goes into the viewport + await this.watch_visibility(); - if (document.createElement(media).canPlayType(type)) { - $att.insertAdjacentHTML('beforeend', ` - <${media} src="${link}" controls itemprop="${media}">`); + try { + const { attachments } = await Bugzilla.API.get(`bug/attachment/${this.id}`, { include_fields: 'data' }); + const text = decodeURIComponent(escape(atob(attachments[this.id].data))); + const lang = this.is_patch ? 'diff' : this.type.match(/\w+$/)[0]; + + this.$outer.innerHTML = ``; + + // Make the button work as a link. It cannot be `` because Prism Autolinker plugin may add links to `
`
+      this.$attachment.querySelector('[role="link"]').addEventListener('click', () => location.href = this.link);
+
+      if (Prism) {
+        Prism.highlightElement(this.$attachment.querySelector('pre'));
+        this.$attachment.querySelectorAll('pre a').forEach($a => $a.tabIndex = -1);
       }
+    } catch (ex) {
+      this.$outer.remove();
     }
+  }
 
-    // Detect text (code from attachment.js)
-    const is_patch = $att.matches('.patch');
-    const is_markdown = !!name.match(/\.(?:md|mkdn?|mdown|markdown)$/);
-    const is_source = !!name.match(/\.(?:cpp|es|h|js|json|rs|rst|sh|toml|ts|tsx|xml|yaml|yml)$/);
-    const is_text = type.match(/^text\/(?!x-).+$/) || is_patch || is_markdown || is_source;
-
-    // Show text smaller than 50 KB
-    if (is_text && size < 50000) {
-      // Load text body
-      try {
-        const { attachments } = await Bugzilla.API.get(`bug/attachment/${id}`, { include_fields: 'data' });
-        const text = decodeURIComponent(escape(atob(attachments[id].data)));
-        const lang = is_patch ? 'diff' : type.match(/\w+$/)[0];
-
-        $att.insertAdjacentHTML('beforeend', `
-          `);
-
-        // Make the button work as a link. It cannot be `` because Prism Autolinker plugin may add links to `
`
-        $att.querySelector('[role="link"]').addEventListener('click', () => location.href = link);
-
-        if (Prism) {
-          Prism.highlightElement($att.querySelector('pre'));
-          $att.querySelectorAll('pre a').forEach($a => $a.tabIndex = -1);
+  /**
+   * Use the Intersection Observer API to watch the visibility of the attachment container.
+   * @returns {Promise} Resolved once the container goes into the viewport.
+   * @see https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
+   */
+  async watch_visibility() {
+    this.$outer = this.$attachment.querySelector('.outer');
+
+    return new Promise(resolve => {
+      const observer = new IntersectionObserver(entries => entries.forEach(entry => {
+        if (entry.intersectionRatio > 0) {
+          observer.disconnect();
+          resolve();
         }
-      } catch (ex) {}
-    }
+      }), { root: document.querySelector('#bugzilla-body') });
+
+      observer.observe(this.$attachment);
+    });
   }
 };
 
diff --git a/skins/standard/global.css b/skins/standard/global.css
index d909dd9c1..600b25c93 100644
--- a/skins/standard/global.css
+++ b/skins/standard/global.css
@@ -991,7 +991,6 @@ input[type="radio"]:checked {
     overflow-x: auto;
     overflow-y: scroll;
     -webkit-overflow-scrolling: touch; /* Enable momentum scrolling on iOS */
-    scroll-behavior: smooth;
     will-change: transform; /* Enable smooth scrolling (Safari) */
   }
 }