From bbb455c0c596b5cb4d77929267f7680952667e11 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Thu, 14 Feb 2013 12:13:24 +0100 Subject: [PATCH] Automatically reposition a quote preview or image hover once the content is loaded. Close #237. --- 4chan_x.user.js | 66 ++++++++++++++++++++++++++++----------------- lib/ui.coffee | 20 +++++++++++--- src/features.coffee | 34 ++++++++++++----------- 3 files changed, 77 insertions(+), 43 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index c50062b08..5c4dd7b51 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -521,19 +521,33 @@ } return localStorage.setItem("" + g.NAMESPACE + this.id + ".position", this.style.cssText); }; - hoverstart = function(root, el, events, cb) { - var event, o, _i, _len, _ref; + hoverstart = function(_arg) { + var asap, asapTest, cb, el, event, events, initialEvent, o, root, _i, _len, _ref; + root = _arg.root, el = _arg.el, initialEvent = _arg.initialEvent, events = _arg.events, asapTest = _arg.asapTest, cb = _arg.cb; o = { root: root, el: el, style: el.style, cb: cb, events: events.split(' '), + mousemove: function(e) { + return initialEvent = e; + }, clientHeight: doc.clientHeight, clientWidth: doc.clientWidth }; o.hover = hover.bind(o); o.hoverend = hoverend.bind(o); + root.addEventListener('mousemove', o.mousemove, false); + asap = function() { + if (asapTest()) { + root.removeEventListener('mousemove', o.mousemove, false); + return o.hover(initialEvent); + } else { + return o.timeout = setTimeout(asap, 25); + } + }; + asap(); _ref = o.events; for (_i = 0, _len = _ref.length; _i < _len; _i++) { event = _ref[_i]; @@ -568,6 +582,8 @@ this.root.removeEventListener(event, this.hoverend, false); } this.root.removeEventListener('mousemove', this.hover, false); + this.root.removeEventListener('mousemove', this.mousemove, false); + clearTimeout(this.timeout); if (this.cb) { return this.cb.call(this); } @@ -2923,7 +2939,16 @@ }); $.add(d.body, qp); Get.postClone(board, threadID, postID, qp, Get.contextFromLink(this)); - UI.hover(this, qp, 'mouseout click', QuotePreview.mouseout); + UI.hover({ + root: this, + el: qp, + initialEvent: e, + events: 'mouseout click', + cb: QuotePreview.mouseout, + asapTest: function() { + return qp.firstElementChild; + } + }); if (!(origin = g.posts["" + board + "." + postID])) { return; } @@ -3725,35 +3750,28 @@ } return $.on(this.file.thumb, 'mouseover', ImageHover.mouseover); }, - mouseover: function() { - var el, - _this = this; + mouseover: function(e) { + var el; el = $.el('img', { id: 'ihover', src: this.parentNode.href }); $.add(d.body, el); - $.on(el, 'load', function() { - return ImageHover.load(_this, el); + UI.hover({ + root: this, + el: el, + initialEvent: e, + events: 'mouseout', + asapTest: function() { + return el.naturalHeight; + } }); - $.on(el, 'error', ImageHover.error); - return UI.hover(this, el, 'mouseout'); - }, - load: function(root, el) { - var e, style; - if (!el.parentNode) { - return; - } - style = el.style; - e = new Event('mousemove'); - e.clientX = -45 + parseInt(style.left); - e.clientY = 120 + parseInt(style.top); - return root.dispatchEvent(e); + return $.on(el, 'error', ImageHover.error); }, error: function() { var URL, src, timeoutID, _this = this; - if (!this.parentNode) { + if (!doc.contains(this)) { return; } src = this.src.split('/'); @@ -3830,7 +3848,7 @@ input = _ref1[_i]; if (input.type === 'checkbox') { $.on(input, 'click', this.cb.checkbox.bind(this)); - input.dispatchEvent(new Event('click')); + $.event('click', null, input); } switch (input.name) { case 'Scroll BG': @@ -3842,7 +3860,7 @@ break; case 'Interval': $.on(input, 'change', this.cb.interval.bind(this)); - input.dispatchEvent(new Event('change')); + $.event('change', null, input); break; case 'Update Now': $.on(input, 'click', this.update.bind(this)); diff --git a/lib/ui.coffee b/lib/ui.coffee index c05f2b149..493d400eb 100644 --- a/lib/ui.coffee +++ b/lib/ui.coffee @@ -286,18 +286,30 @@ UI = (-> d.removeEventListener 'mouseup', @up, false localStorage.setItem "#{g.NAMESPACE}#{@id}.position", @style.cssText - hoverstart = (root, el, events, cb) -> + hoverstart = ({root, el, initialEvent, events, asapTest, cb}) -> o = { root: root el: el style: el.style cb: cb events: events.split ' ' + mousemove: (e) -> initialEvent = e clientHeight: doc.clientHeight clientWidth: doc.clientWidth } o.hover = hover.bind o o.hoverend = hoverend.bind o + + # Set position once content is loaded. + root.addEventListener 'mousemove', o.mousemove, false + asap = -> + if asapTest() + root.removeEventListener 'mousemove', o.mousemove, false + o.hover initialEvent + else + o.timeout = setTimeout asap, 25 + asap() + for event in o.events root.addEventListener event, o.hoverend, false root.addEventListener 'mousemove', o.hover, false @@ -328,8 +340,10 @@ UI = (-> hoverend = -> @el.parentNode.removeChild @el for event in @events - @root.removeEventListener event, @hoverend, false - @root.removeEventListener 'mousemove', @hover, false + @root.removeEventListener event, @hoverend, false + @root.removeEventListener 'mousemove', @hover, false + @root.removeEventListener 'mousemove', @mousemove, false + clearTimeout @timeout @cb.call @ if @cb diff --git a/src/features.coffee b/src/features.coffee index 5dd36a7cc..1afe0e5d5 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1765,7 +1765,13 @@ QuotePreview = $.add d.body, qp Get.postClone board, threadID, postID, qp, Get.contextFromLink @ - UI.hover @, qp, 'mouseout click', QuotePreview.mouseout + UI.hover + root: @ + el: qp + initialEvent: e + events: 'mouseout click' + cb: QuotePreview.mouseout + asapTest: -> qp.firstElementChild return unless origin = g.posts["#{board}.#{postID}"] @@ -2172,7 +2178,7 @@ ImageExpand = thumb.hidden = true $.addClass post.nodes.root, 'expanded-image' if img = $ '.full-image', thumb.parentNode - # Expand already loaded picture + # Expand already loaded picture. img.hidden = false return img = $.el 'img', @@ -2299,24 +2305,20 @@ ImageHover = node: -> return unless @file?.isImage $.on @file.thumb, 'mouseover', ImageHover.mouseover - mouseover: -> + mouseover: (e) -> el = $.el 'img' id: 'ihover' src: @parentNode.href $.add d.body, el - $.on el, 'load', => ImageHover.load @, el + UI.hover + root: @ + el: el + initialEvent: e + events: 'mouseout' + asapTest: -> el.naturalHeight $.on el, 'error', ImageHover.error - UI.hover @, el, 'mouseout' - load: (root, el) -> - return unless el.parentNode - # 'Fake' mousemove event by giving required values. - {style} = el - e = new Event 'mousemove' - e.clientX = - 45 + parseInt style.left - e.clientY = 120 + parseInt style.top - root.dispatchEvent e error: -> - return unless @parentNode + return unless doc.contains @ src = @src.split '/' unless src[2] is 'images.4chan.org' and URL = Redirect.image src[3], src[5] return if g.DEAD @@ -2373,7 +2375,7 @@ ThreadUpdater = for input in $$ 'input', dialog if input.type is 'checkbox' $.on input, 'click', @cb.checkbox.bind @ - input.dispatchEvent new Event 'click' + $.event 'click', null, input switch input.name when 'Scroll BG' $.on input, 'click', @cb.scrollBG.bind @ @@ -2382,7 +2384,7 @@ ThreadUpdater = $.on input, 'click', @cb.autoUpdate.bind @ when 'Interval' $.on input, 'change', @cb.interval.bind @ - input.dispatchEvent new Event 'change' + $.event 'change', null, input when 'Update Now' $.on input, 'click', @update.bind @