From f012a6fe327252aa08d414aeccb8f81650cc2abe Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 29 Jul 2016 17:24:45 +0100 Subject: [PATCH 1/5] Scroll patcher for Safari. --- .../coffee/ide/SafariScrollPatcher.coffee | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 services/web/public/coffee/ide/SafariScrollPatcher.coffee diff --git a/services/web/public/coffee/ide/SafariScrollPatcher.coffee b/services/web/public/coffee/ide/SafariScrollPatcher.coffee new file mode 100644 index 0000000000..4c77802926 --- /dev/null +++ b/services/web/public/coffee/ide/SafariScrollPatcher.coffee @@ -0,0 +1,59 @@ +define [ +], () -> + class SafariScrollPatcher + constructor: (ide, $scope) -> + @isBootstrapped = false + @isOverAce = false + @pdfDiv = null + @aceDiv = null + + $scope.$on "loaded", () => + console.log this + if !@isBootstrapped + console.log "bootstrapping" + + @isBootstrapped = true; + @pdfDiv = document.querySelector ".pdfjs-viewer" # Grab the PDF div. + @aceDiv = document.querySelector ".ace_content" # Also the editor. + @isOverAce = false # Flag to control if the pointer is over Ace. + + # Start listening to PDF wheel events when the pointer leaves the PDF region. + # P.S. This is the problem in a nutshell: although the pointer is elsewhere, + # wheel events keep being dispatched to the PDF. + @pdfDiv.addEventListener "mouseleave", () => + @pdfDiv.addEventListener "wheel", dispatchToAce + + # Stop listening to wheel events when the pointer enters the PDF region. If + # the pointer is over the PDF, native behaviour is adequate. + @pdfDiv.addEventListener "mouseenter", () => + @pdfDiv.removeEventListener "wheel", dispatchToAce + + # Set the "pointer over Ace" flag as false, when the mouse leaves its area. + @aceDiv.addEventListener "mouseleave", () => + @isOverAce = false + + # Set the "pointer over Ace" flag as true, when the mouse enters its area. + @aceDiv.addEventListener "mouseenter", () => + @isOverAce = true + + # Handler for wheel events on the PDF. + # If the pointer is over Ace, grab the event, prevent default behaviour + # and dispatch it to Ace. + dispatchToAce = (e) => + if @isOverAce + # If this is logged, the problem just happened: the event arrived + # here (the PDF wheel handler), but it should've gone to Ace. + console.log "Event was bound to the PDF, dispatching to Ace" + + # Small timeout - if we dispatch immediately, an exception is thrown. + window.setTimeout(() => + # Dispatch the exact same event to Ace (this will keep values + # values e.g. `wheelDelta` consistent with user interaction). + @aceDiv.dispatchEvent e + , 5) + + # Avoid scrolling the PDF, as we assume this was intended to the + # editor. + e.preventDefault() + + From 9b3a28048e62e8c560d6843950254ecebfef5785 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 29 Jul 2016 17:24:55 +0100 Subject: [PATCH 2/5] Integrate Safari scroll patcher. --- services/web/public/coffee/ide.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index 4c75ab5d77..d22ec949fb 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -9,6 +9,7 @@ define [ "ide/pdf/PdfManager" "ide/binary-files/BinaryFilesManager" "ide/references/ReferencesManager" + "ide/SafariScrollPatcher" "ide/settings/index" "ide/share/index" "ide/chat/index" @@ -40,6 +41,7 @@ define [ PdfManager BinaryFilesManager ReferencesManager + SafariScrollPatcher ) -> App.controller "IdeController", ($scope, $timeout, ide, localStorage, event_tracking) -> @@ -135,6 +137,9 @@ define [ catch err console.error err + if ide.browserIsSafari + ide.safariScrollPatcher = new SafariScrollPatcher(ide, $scope) + # User can append ?ft=somefeature to url to activate a feature toggle ide.featureToggle = location?.search?.match(/^\?ft=(\w+)$/)?[1] From 16e8cd7820108369d7544ea0bcf98792ce403fb6 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 29 Jul 2016 17:45:50 +0100 Subject: [PATCH 3/5] Refactor Safari scroll patcher; ensure it works after PDF is reloaded. --- .../coffee/ide/SafariScrollPatcher.coffee | 119 ++++++++++-------- 1 file changed, 70 insertions(+), 49 deletions(-) diff --git a/services/web/public/coffee/ide/SafariScrollPatcher.coffee b/services/web/public/coffee/ide/SafariScrollPatcher.coffee index 4c77802926..c1535e3d27 100644 --- a/services/web/public/coffee/ide/SafariScrollPatcher.coffee +++ b/services/web/public/coffee/ide/SafariScrollPatcher.coffee @@ -2,58 +2,79 @@ define [ ], () -> class SafariScrollPatcher constructor: (ide, $scope) -> - @isBootstrapped = false - @isOverAce = false + @isOverAce = false # Flag to control if the pointer is over Ace. @pdfDiv = null @aceDiv = null + # Start listening to PDF wheel events when the pointer leaves the PDF region. + # P.S. This is the problem in a nutshell: although the pointer is elsewhere, + # wheel events keep being dispatched to the PDF. + @handlePdfDivMouseLeave = () => + console.log "dispatching to ace enabled" + @pdfDiv.addEventListener "wheel", @dispatchToAce + + # Stop listening to wheel events when the pointer enters the PDF region. If + # the pointer is over the PDF, native behaviour is adequate. + @handlePdfDivMouseEnter = () => + console.log "dispatching to ace disabled" + @pdfDiv.removeEventListener "wheel", @dispatchToAce + + # Set the "pointer over Ace" flag as false, when the mouse leaves its area. + @handleAceDivMouseLeave = () => + @isOverAce = false + console.log 'is over ace = ' + @isOverAce + + # Set the "pointer over Ace" flag as true, when the mouse enters its area. + @handleAceDivMouseEnter = () => + @isOverAce = true + console.log 'is over ace = ' + @isOverAce + + # Grab the elements (pdfDiv, aceDiv) and set the "hover" event listeners. + # If elements are already defined, clear existing event listeners and do + # the process again (grab elements, set listeners). + @setListeners = () => + @isOverAce = false + + # If elements aren't null, remove existing listeners. + if @pdfDiv? + @pdfDiv.removeEventListener @handlePdfDivMouseLeave + @pdfDiv.removeEventListener @handlePdfDivMouseEnter + + if @aceDiv? + @aceDiv.removeEventListener @handleAceDivMouseLeave + @aceDiv.removeEventListener @handleAceDivMouseEnter + + # Grab elements. + @pdfDiv = document.querySelector ".pdfjs-viewer" # Grab the PDF div. + @aceDiv = document.querySelector ".ace_content" # Also the editor. + + # Set hover-related listeners. + @pdfDiv.addEventListener "mouseleave", @handlePdfDivMouseLeave + @pdfDiv.addEventListener "mouseenter", @handlePdfDivMouseEnter + @aceDiv.addEventListener "mouseleave", @handleAceDivMouseLeave + @aceDiv.addEventListener "mouseenter", @handleAceDivMouseEnter + + # Handler for wheel events on the PDF. + # If the pointer is over Ace, grab the event, prevent default behaviour + # and dispatch it to Ace. + @dispatchToAce = (e) => + if @isOverAce + # If this is logged, the problem just happened: the event arrived + # here (the PDF wheel handler), but it should've gone to Ace. + console.log "Event was bound to the PDF, dispatching to Ace" + + # Small timeout - if we dispatch immediately, an exception is thrown. + window.setTimeout(() => + # Dispatch the exact same event to Ace (this will keep values + # values e.g. `wheelDelta` consistent with user interaction). + @aceDiv.dispatchEvent e + , 5) + + # Avoid scrolling the PDF, as we assume this was intended to the + # editor. + e.preventDefault() + $scope.$on "loaded", () => - console.log this - if !@isBootstrapped - console.log "bootstrapping" - - @isBootstrapped = true; - @pdfDiv = document.querySelector ".pdfjs-viewer" # Grab the PDF div. - @aceDiv = document.querySelector ".ace_content" # Also the editor. - @isOverAce = false # Flag to control if the pointer is over Ace. - - # Start listening to PDF wheel events when the pointer leaves the PDF region. - # P.S. This is the problem in a nutshell: although the pointer is elsewhere, - # wheel events keep being dispatched to the PDF. - @pdfDiv.addEventListener "mouseleave", () => - @pdfDiv.addEventListener "wheel", dispatchToAce - - # Stop listening to wheel events when the pointer enters the PDF region. If - # the pointer is over the PDF, native behaviour is adequate. - @pdfDiv.addEventListener "mouseenter", () => - @pdfDiv.removeEventListener "wheel", dispatchToAce - - # Set the "pointer over Ace" flag as false, when the mouse leaves its area. - @aceDiv.addEventListener "mouseleave", () => - @isOverAce = false - - # Set the "pointer over Ace" flag as true, when the mouse enters its area. - @aceDiv.addEventListener "mouseenter", () => - @isOverAce = true - - # Handler for wheel events on the PDF. - # If the pointer is over Ace, grab the event, prevent default behaviour - # and dispatch it to Ace. - dispatchToAce = (e) => - if @isOverAce - # If this is logged, the problem just happened: the event arrived - # here (the PDF wheel handler), but it should've gone to Ace. - console.log "Event was bound to the PDF, dispatching to Ace" - - # Small timeout - if we dispatch immediately, an exception is thrown. - window.setTimeout(() => - # Dispatch the exact same event to Ace (this will keep values - # values e.g. `wheelDelta` consistent with user interaction). - @aceDiv.dispatchEvent e - , 5) - - # Avoid scrolling the PDF, as we assume this was intended to the - # editor. - e.preventDefault() + @setListeners() From 0d4e1e5495113a6eaf3c7f27459533ee17f10f22 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 1 Aug 2016 11:12:50 +0100 Subject: [PATCH 4/5] Try a smaller timeout + minor fixes. --- services/web/public/coffee/ide.coffee | 2 +- .../web/public/coffee/ide/SafariScrollPatcher.coffee | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index d22ec949fb..e62227c9be 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -138,7 +138,7 @@ define [ console.error err if ide.browserIsSafari - ide.safariScrollPatcher = new SafariScrollPatcher(ide, $scope) + ide.safariScrollPatcher = new SafariScrollPatcher($scope) # User can append ?ft=somefeature to url to activate a feature toggle ide.featureToggle = location?.search?.match(/^\?ft=(\w+)$/)?[1] diff --git a/services/web/public/coffee/ide/SafariScrollPatcher.coffee b/services/web/public/coffee/ide/SafariScrollPatcher.coffee index c1535e3d27..1859511c99 100644 --- a/services/web/public/coffee/ide/SafariScrollPatcher.coffee +++ b/services/web/public/coffee/ide/SafariScrollPatcher.coffee @@ -1,7 +1,7 @@ define [ ], () -> class SafariScrollPatcher - constructor: (ide, $scope) -> + constructor: ($scope) -> @isOverAce = false # Flag to control if the pointer is over Ace. @pdfDiv = null @aceDiv = null @@ -10,24 +10,20 @@ define [ # P.S. This is the problem in a nutshell: although the pointer is elsewhere, # wheel events keep being dispatched to the PDF. @handlePdfDivMouseLeave = () => - console.log "dispatching to ace enabled" @pdfDiv.addEventListener "wheel", @dispatchToAce # Stop listening to wheel events when the pointer enters the PDF region. If # the pointer is over the PDF, native behaviour is adequate. @handlePdfDivMouseEnter = () => - console.log "dispatching to ace disabled" @pdfDiv.removeEventListener "wheel", @dispatchToAce # Set the "pointer over Ace" flag as false, when the mouse leaves its area. @handleAceDivMouseLeave = () => @isOverAce = false - console.log 'is over ace = ' + @isOverAce # Set the "pointer over Ace" flag as true, when the mouse enters its area. @handleAceDivMouseEnter = () => @isOverAce = true - console.log 'is over ace = ' + @isOverAce # Grab the elements (pdfDiv, aceDiv) and set the "hover" event listeners. # If elements are already defined, clear existing event listeners and do @@ -46,7 +42,7 @@ define [ # Grab elements. @pdfDiv = document.querySelector ".pdfjs-viewer" # Grab the PDF div. - @aceDiv = document.querySelector ".ace_content" # Also the editor. + @aceDiv = document.querySelector ".ace_content" # Also the editor. # Set hover-related listeners. @pdfDiv.addEventListener "mouseleave", @handlePdfDivMouseLeave @@ -61,14 +57,13 @@ define [ if @isOverAce # If this is logged, the problem just happened: the event arrived # here (the PDF wheel handler), but it should've gone to Ace. - console.log "Event was bound to the PDF, dispatching to Ace" # Small timeout - if we dispatch immediately, an exception is thrown. window.setTimeout(() => # Dispatch the exact same event to Ace (this will keep values # values e.g. `wheelDelta` consistent with user interaction). @aceDiv.dispatchEvent e - , 5) + , 1) # Avoid scrolling the PDF, as we assume this was intended to the # editor. From 4b8ab2dbba64f950e28cbf4834189b20def391ac Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 1 Aug 2016 13:35:49 +0100 Subject: [PATCH 5/5] More comments. --- services/web/public/coffee/ide/SafariScrollPatcher.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/web/public/coffee/ide/SafariScrollPatcher.coffee b/services/web/public/coffee/ide/SafariScrollPatcher.coffee index 1859511c99..07ad2e7565 100644 --- a/services/web/public/coffee/ide/SafariScrollPatcher.coffee +++ b/services/web/public/coffee/ide/SafariScrollPatcher.coffee @@ -69,6 +69,9 @@ define [ # editor. e.preventDefault() + # "loaded" event is emitted from the pdfViewer controller $scope. This means + # that the previous PDF DOM element was destroyed and a new one is available, + # so we need to grab the elements and set the listeners again. $scope.$on "loaded", () => @setListeners()