From 80f3a4a067bb17144cb882e05a1c62859003c027 Mon Sep 17 00:00:00 2001 From: Hunter Stern Date: Thu, 24 Jul 2014 16:44:05 -0700 Subject: [PATCH 1/7] Enhancement to allow embedded soundcloud audio files to be detected --- umbra/behaviors.d/default.js | 99 +++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/umbra/behaviors.d/default.js b/umbra/behaviors.d/default.js index dc66819..6b432f0 100644 --- a/umbra/behaviors.d/default.js +++ b/umbra/behaviors.d/default.js @@ -5,18 +5,105 @@ // Scrolls to the bottom of the page. That's it at the moment. // +var umbraAboveBelowOrOnScreen = function(e) { + var eTop = e.getBoundingClientRect().top; + if (eTop < window.scrollY) { + return -1; // above + } else if (eTop > window.scrollY + window.innerHeight) { + return 1; // below + } else { + return 0; // on screen + } +} + var umbraState = {'idleSince':null}; +var umbraAlreadyClicked = {}; +var UMBRA_IFRAME_SOUNDCLOUD_EMBEDDED_SELECTOR = "iframe[src^='https://w.soundcloud.com/player']"; +var UMBRA_THINGS_TO_CLICK_SOUNDCLOUD_EMBEDDED_SELECTOR = "button.playButton"; var umbraFinished = false; var umbraIntervalFunc = function() { - var needToScroll = (window.scrollY + window.innerHeight < document.documentElement.scrollHeight); - // console.log('intervalFunc umbraState.idleSince=' + umbraState.idleSince + ' needToScroll=' + needToScroll + ' window.scrollY=' + window.scrollY + ' window.innerHeight=' + window.innerHeight + ' document.documentElement.scrollHeight=' + document.documentElement.scrollHeight); - if (needToScroll) { - window.scrollBy(0, 200); - umbraState.idleSince = null; - } else if (umbraState.idleSince == null) { + var umbraSoundCloudEmbeddedElements = getUmbraSoundCloudEmbeddedElements(); + + var clickedSomething = false; + var somethingLeftBelow = false; + var somethingLeftAbove = false; + var missedAbove = 0; + + for (var i = 0; i < umbraSoundCloudEmbeddedElements.length; i++) { + + var targetId = umbraSoundCloudEmbeddedElements[i].id; + var target = umbraSoundCloudEmbeddedElements[i].target; + + if (!(targetId in umbraAlreadyClicked)) { + var where = umbraAboveBelowOrOnScreen(target); + + if (where == 0) { // on screen + // var pos = target.getBoundingClientRect().top; + // window.scrollTo(0, target.getBoundingClientRect().top - 100); + console.log("clicking at " + target.getBoundingClientRect().top + " on " + target.outerHTML); + if (target.click != undefined) { + target.click(); + } + umbraAlreadyClicked[targetId] = true; + clickedSomething = true; + umbraState.idleSince = null; + break; + } else if (where > 0) { + somethingLeftBelow = true; + } else if (where < 0) { + somethingLeftAbove = true; + } + } + } + + if (!clickedSomething) { + if (somethingLeftAbove) { + console.log("scrolling UP because everything on this screen has been clicked but we missed something above"); + window.scrollBy(0, -500); + umbraState.idleSince = null; + } else if (somethingLeftBelow) { + console.log("scrolling because everything on this screen has been clicked but there's more below document.body.clientHeight=" + document.body.clientHeight); + window.scrollBy(0, 200); + umbraState.idleSince = null; + } else if (window.scrollY + window.innerHeight < document.documentElement.scrollHeight) { + console.log("scrolling because we're not to the bottom yet document.body.clientHeight=" + document.body.clientHeight); + window.scrollBy(0, 200); + umbraState.idleSince = null; + } else if (umbraState.idleSince == null) { umbraState.idleSince = Date.now(); } + } + + if (umbraState.idleSince == null) { + umbraState.idleSince = Date.now(); + } +} + +//try to detect sound cloud "Play" buttons and return them as targets for clicking +var getUmbraSoundCloudEmbeddedElements = function() { + + var soundCloudEmbeddedElements = []; + + var id = 0; + + [].forEach.call(document.querySelectorAll(UMBRA_IFRAME_SOUNDCLOUD_EMBEDDED_SELECTOR), + function fn(elem){ + if (elem.src.indexOf("auto_play=false") != -1) { + var button = elem.contentWindow.document.body.querySelectorAll(UMBRA_THINGS_TO_CLICK_SOUNDCLOUD_EMBEDDED_SELECTOR); + + //use the iframe's src attribute as the key to the sound cloud player button. assumption is that each iframe created by the sound cloud widget + //contains only a single unique audio file on a given page + if (button && button.length > 0) { + //get the Element from the NodeList + soundCloudEmbeddedElements.push({"id" : elem.src, "target" : button.item(0)}); + id++; + } + } + } + ); + + return soundCloudEmbeddedElements; } // If we haven't had anything to do (scrolled, clicked, etc) in this amount of From 6a5d1e2266db148adb360aabad02a77f5d14a83f Mon Sep 17 00:00:00 2001 From: Hunter Stern Date: Thu, 24 Jul 2014 16:46:06 -0700 Subject: [PATCH 2/7] Disable web security in chromium so iframes on different domains can be accessed by behavior javascript. --- umbra/browser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/umbra/browser.py b/umbra/browser.py index dd042d2..84122ca 100644 --- a/umbra/browser.py +++ b/umbra/browser.py @@ -241,6 +241,7 @@ class Chrome: "--window-size=1100,900", "--no-default-browser-check", "--disable-first-run-ui", "--no-first-run", "--homepage=about:blank", "--disable-direct-npapi-requests", + "--disable-web-security", "about:blank"] self.logger.info("running {}".format(chrome_args)) self.chrome_process = subprocess.Popen(chrome_args, env=new_env, start_new_session=True) From e320654d1e7f09a90f065bb34165ffc78e32d3d8 Mon Sep 17 00:00:00 2001 From: Hunter Stern Date: Fri, 12 Sep 2014 09:56:41 -0700 Subject: [PATCH 3/7] Allow selector to detect https and http soundcloud widget. --- umbra/behaviors.d/default.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/umbra/behaviors.d/default.js b/umbra/behaviors.d/default.js index 6b432f0..615827e 100644 --- a/umbra/behaviors.d/default.js +++ b/umbra/behaviors.d/default.js @@ -18,7 +18,7 @@ var umbraAboveBelowOrOnScreen = function(e) { var umbraState = {'idleSince':null}; var umbraAlreadyClicked = {}; -var UMBRA_IFRAME_SOUNDCLOUD_EMBEDDED_SELECTOR = "iframe[src^='https://w.soundcloud.com/player']"; +var UMBRA_IFRAME_SOUNDCLOUD_EMBEDDED_SELECTOR = "iframe[src^='http://w.soundcloud.com/player'], iframe[src^='https://w.soundcloud.com/player']"; var UMBRA_THINGS_TO_CLICK_SOUNDCLOUD_EMBEDDED_SELECTOR = "button.playButton"; var umbraFinished = false; var umbraIntervalFunc = function() { @@ -89,18 +89,16 @@ var getUmbraSoundCloudEmbeddedElements = function() { [].forEach.call(document.querySelectorAll(UMBRA_IFRAME_SOUNDCLOUD_EMBEDDED_SELECTOR), function fn(elem){ - if (elem.src.indexOf("auto_play=false") != -1) { - var button = elem.contentWindow.document.body.querySelectorAll(UMBRA_THINGS_TO_CLICK_SOUNDCLOUD_EMBEDDED_SELECTOR); - - //use the iframe's src attribute as the key to the sound cloud player button. assumption is that each iframe created by the sound cloud widget - //contains only a single unique audio file on a given page - if (button && button.length > 0) { - //get the Element from the NodeList - soundCloudEmbeddedElements.push({"id" : elem.src, "target" : button.item(0)}); - id++; - } - } + var button = elem.contentWindow.document.body.querySelectorAll(UMBRA_THINGS_TO_CLICK_SOUNDCLOUD_EMBEDDED_SELECTOR); + + //use the iframe's src attribute as the key to the sound cloud player button. assumption is that each iframe created by the sound cloud widget + //contains only a single unique audio file on a given page + if (button && button.length > 0) { + //get the Element from the NodeList + soundCloudEmbeddedElements.push({"id" : elem.src, "target" : button.item(0)}); + id++; } + } ); return soundCloudEmbeddedElements; From a2ea2501db8e914e5dd0c29451775705219cbe50 Mon Sep 17 00:00:00 2001 From: Hunter Stern Date: Fri, 12 Sep 2014 16:07:32 -0700 Subject: [PATCH 4/7] More soundcloud changes. --- umbra/behaviors.d/default.js | 77 ++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/umbra/behaviors.d/default.js b/umbra/behaviors.d/default.js index 615827e..b3d95c9 100644 --- a/umbra/behaviors.d/default.js +++ b/umbra/behaviors.d/default.js @@ -4,7 +4,6 @@ // // Scrolls to the bottom of the page. That's it at the moment. // - var umbraAboveBelowOrOnScreen = function(e) { var eTop = e.getBoundingClientRect().top; if (eTop < window.scrollY) { @@ -16,14 +15,17 @@ var umbraAboveBelowOrOnScreen = function(e) { } } +var UMBRA_IFRAME_SOUNDCLOUD_EMBEDDED_SELECTOR = "iframe"; +var UMBRA_THINGS_TO_CLICK_SOUNDCLOUD_EMBEDDED_SELECTOR = "button.sc-button-play, button.playButton"; +var MAX_IFRAME_RECURSE_DEPTH = 1; //0-based var umbraState = {'idleSince':null}; var umbraAlreadyClicked = {}; -var UMBRA_IFRAME_SOUNDCLOUD_EMBEDDED_SELECTOR = "iframe[src^='http://w.soundcloud.com/player'], iframe[src^='https://w.soundcloud.com/player']"; -var UMBRA_THINGS_TO_CLICK_SOUNDCLOUD_EMBEDDED_SELECTOR = "button.playButton"; var umbraFinished = false; var umbraIntervalFunc = function() { - var umbraSoundCloudEmbeddedElements = getUmbraSoundCloudEmbeddedElements(); + var umbraSoundCloudEmbeddedElements = []; + + getUmbraSoundCloudEmbeddedElements(umbraSoundCloudEmbeddedElements); var clickedSomething = false; var somethingLeftBelow = false; @@ -36,6 +38,7 @@ var umbraIntervalFunc = function() { var target = umbraSoundCloudEmbeddedElements[i].target; if (!(targetId in umbraAlreadyClicked)) { + var where = umbraAboveBelowOrOnScreen(target); if (where == 0) { // on screen @@ -81,27 +84,33 @@ var umbraIntervalFunc = function() { } //try to detect sound cloud "Play" buttons and return them as targets for clicking -var getUmbraSoundCloudEmbeddedElements = function() { +var getUmbraSoundCloudEmbeddedElements = function(soundCloudEmbeddedElements, currentIframeDepth, currentDocument) { - var soundCloudEmbeddedElements = []; + //set default values for parameters + currentIframeDepth = currentIframeDepth || 0; + currentDocument = currentDocument || document; - var id = 0; + if (currentIframeDepth > MAX_IFRAME_RECURSE_DEPTH) { + return; + } - [].forEach.call(document.querySelectorAll(UMBRA_IFRAME_SOUNDCLOUD_EMBEDDED_SELECTOR), - function fn(elem){ - var button = elem.contentWindow.document.body.querySelectorAll(UMBRA_THINGS_TO_CLICK_SOUNDCLOUD_EMBEDDED_SELECTOR); - - //use the iframe's src attribute as the key to the sound cloud player button. assumption is that each iframe created by the sound cloud widget - //contains only a single unique audio file on a given page - if (button && button.length > 0) { - //get the Element from the NodeList - soundCloudEmbeddedElements.push({"id" : elem.src, "target" : button.item(0)}); - id++; - } - } - ); + //collect all buttons on current document first + var button = []; - return soundCloudEmbeddedElements; + button = currentDocument.querySelectorAll(UMBRA_THINGS_TO_CLICK_SOUNDCLOUD_EMBEDDED_SELECTOR); + + for (var i = 0; i < button.length; i++) { + soundCloudEmbeddedElements.push({"id" : getElementCssPath(button.item(i)), "target" : button.item(i)}); + } + + //now get all buttons in embedded iframes + var iframe = []; + + iframe = currentDocument.querySelectorAll(UMBRA_IFRAME_SOUNDCLOUD_EMBEDDED_SELECTOR); + + for (var i = 0; i < iframe.length; i++) { + getUmbraSoundCloudEmbeddedElements(soundCloudEmbeddedElements, currentIframeDepth + 1, iframe[i].contentWindow.document.body); + } } // If we haven't had anything to do (scrolled, clicked, etc) in this amount of @@ -119,4 +128,30 @@ var umbraBehaviorFinished = function() { return false; } +//copied from http://stackoverflow.com/questions/4588119/get-elements-css-selector-without-element-id +var getElementCssPath = function(element) { + + var names = []; + + while (element.parentNode){ + if (element.id){ + names.unshift('#' + element.id); + break; + } else { + if (element == element.ownerDocument.documentElement) { + names.unshift(element.tagName); + } + else { + for (var c = 1, e = element; e.previousElementSibling; e = e.previousElementSibling, c++); + + names.unshift(element.tagName + ":nth-child(" + c + ")"); + } + + element = element.parentNode; + } + } + + return names.join(" > "); +} + var umbraIntervalId = setInterval(umbraIntervalFunc, 100); From 6af3455dbf34a4e12b6a5543c57cc35e76a16b9e Mon Sep 17 00:00:00 2001 From: Hunter Stern Date: Mon, 22 Sep 2014 14:21:00 -0700 Subject: [PATCH 5/7] Improve formatting. --- umbra/behaviors.d/default.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/umbra/behaviors.d/default.js b/umbra/behaviors.d/default.js index b3d95c9..a658cbf 100644 --- a/umbra/behaviors.d/default.js +++ b/umbra/behaviors.d/default.js @@ -23,9 +23,9 @@ var umbraAlreadyClicked = {}; var umbraFinished = false; var umbraIntervalFunc = function() { - var umbraSoundCloudEmbeddedElements = []; + var umbraSoundCloudEmbeddedElements = []; - getUmbraSoundCloudEmbeddedElements(umbraSoundCloudEmbeddedElements); + getUmbraSoundCloudEmbeddedElements(umbraSoundCloudEmbeddedElements); var clickedSomething = false; var somethingLeftBelow = false; From 1ee45053c5d64c1dd7f75c3c351726b50095bbf4 Mon Sep 17 00:00:00 2001 From: Hunter Stern Date: Mon, 22 Sep 2014 14:22:52 -0700 Subject: [PATCH 6/7] Even more formatting changes. --- umbra/behaviors.d/default.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/umbra/behaviors.d/default.js b/umbra/behaviors.d/default.js index a658cbf..5dd8423 100644 --- a/umbra/behaviors.d/default.js +++ b/umbra/behaviors.d/default.js @@ -34,7 +34,7 @@ var umbraIntervalFunc = function() { for (var i = 0; i < umbraSoundCloudEmbeddedElements.length; i++) { - var targetId = umbraSoundCloudEmbeddedElements[i].id; + var targetId = umbraSoundCloudEmbeddedElements[i].id; var target = umbraSoundCloudEmbeddedElements[i].target; if (!(targetId in umbraAlreadyClicked)) { From 91f9788eb2274fb82cbbee098e0dd0a4aa072aa9 Mon Sep 17 00:00:00 2001 From: Hunter Stern Date: Wed, 21 Jan 2015 16:28:29 -0800 Subject: [PATCH 7/7] Add iframe css path to target id for soundcloud buttons. --- umbra/behaviors.d/default.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/umbra/behaviors.d/default.js b/umbra/behaviors.d/default.js index 5dd8423..21019cc 100644 --- a/umbra/behaviors.d/default.js +++ b/umbra/behaviors.d/default.js @@ -84,7 +84,8 @@ var umbraIntervalFunc = function() { } //try to detect sound cloud "Play" buttons and return them as targets for clicking -var getUmbraSoundCloudEmbeddedElements = function(soundCloudEmbeddedElements, currentIframeDepth, currentDocument) { +var getUmbraSoundCloudEmbeddedElements = function(soundCloudEmbeddedElements, currentIframeDepth, currentDocument, + iframeElement) { //set default values for parameters currentIframeDepth = currentIframeDepth || 0; @@ -99,8 +100,10 @@ var getUmbraSoundCloudEmbeddedElements = function(soundCloudEmbeddedElements, cu button = currentDocument.querySelectorAll(UMBRA_THINGS_TO_CLICK_SOUNDCLOUD_EMBEDDED_SELECTOR); + var cssPathIframe = iframeElement ? getElementCssPath(iframeElement) : ""; + for (var i = 0; i < button.length; i++) { - soundCloudEmbeddedElements.push({"id" : getElementCssPath(button.item(i)), "target" : button.item(i)}); + soundCloudEmbeddedElements.push({"id" : cssPathIframe + getElementCssPath(button.item(i)), "target" : button.item(i)}); } //now get all buttons in embedded iframes @@ -109,7 +112,7 @@ var getUmbraSoundCloudEmbeddedElements = function(soundCloudEmbeddedElements, cu iframe = currentDocument.querySelectorAll(UMBRA_IFRAME_SOUNDCLOUD_EMBEDDED_SELECTOR); for (var i = 0; i < iframe.length; i++) { - getUmbraSoundCloudEmbeddedElements(soundCloudEmbeddedElements, currentIframeDepth + 1, iframe[i].contentWindow.document.body); + getUmbraSoundCloudEmbeddedElements(soundCloudEmbeddedElements, currentIframeDepth + 1, iframe[i].contentWindow.document.body, iframe[i]); } }