Last active
July 16, 2018 00:38
-
-
Save brianloveswords/d7c5d390c6a807c36691e16e9adbc724 to your computer and use it in GitHub Desktop.
Revisions
-
brianloveswords revised this gist
Jul 16, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -60,7 +60,7 @@ const attachTweetObserver = observer => { } #+END_SRC We need one more observer: since twitter dot com is a single page app when the user navigates to their notifications it will unload their timeline, and the node the observer is observing will be destroyed (so we might as well disconnect the observer). Once the timeline is reloaded, we want to re-process the initially loaded tweets and reconnect the observer to the new timeline node. By observing for attribute changes on the ~#doc~ element we can watch for when the class contains "route-home", which is an indicator that the main timeline view is active. -
brianloveswords revised this gist
Jul 16, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -47,7 +47,7 @@ const processTweets = tweets => tweets.filter(nonchrono).forEach(handler); New tweets are loaded once the user scrolls near the bottom of the page, so we need to do something to make sure those new tweets get processed. This is a great opportunity to use [[https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver][MutationObservers]]. We set up the ~MutationObserver~ to watch the stream container, ~.js-navigable-stream~, and we configure it to only care about the list of children. Once we get a mutation event, we can run ~processTweets~ on the list of added nodes. #+BEGIN_SRC js const createTweetObserver = () => { -
brianloveswords revised this gist
Jul 16, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -17,7 +17,7 @@ const getInitialTweets = () => [...document.querySelectorAll('.js-stream-item')] We want a filter that keeps only the tweets that don't belong in the timeline so we can do something with those. One way to identify these tweets is by the icons twitter includes in non-chronological tweets to give the user context of why those tweets are showing up in the timeline. The ~nonchrono~ function looks at the children of the tweet element and if any of the children are the contextual icon for one of the non-chronological tweets we're looking for – promoted, other user likes, or "your friends follow this person" – we return ~true~ to keep that tweet. #+BEGIN_SRC js const nonchrono = e => { -
brianloveswords revised this gist
Jul 16, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -17,7 +17,7 @@ const getInitialTweets = () => [...document.querySelectorAll('.js-stream-item')] We want a filter that keeps only the tweets that don't belong in the timeline so we can do something with those. One way to identify these tweets is by the icons twitter includes in non-chronological tweets to give the user context of why those tweets are showing up in the timeline. The ~nonchrono~ function looks at the children of the tweet element and if any of the children is the contextual icon for one of the non-chronological tweets we're looking for – promoted, other user likes, or "your friends follow this person" – we return ~true~ to keep that tweet. #+BEGIN_SRC js const nonchrono = e => { -
brianloveswords revised this gist
Jul 16, 2018 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,6 +3,8 @@ :header-args: :tangle yes :padline no :END: [[[https://gist.github.com/brianloveswords/d7c5d390c6a807c36691e16e9adbc724#file-nonchrono-js][jump to complete source]]] There are two stages to this: 1. identify initially loaded non-chrono tweets 2. identify newly loaded non-chrono tweets as user scrolls -
brianloveswords revised this gist
Jul 16, 2018 . 2 changed files with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes.File renamed without changes. -
brianloveswords revised this gist
Jul 16, 2018 . 2 changed files with 5 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -85,3 +85,7 @@ processTweets(getInitialTweets()); attachTweetObserver(tweetObserver); attachDocObserver(tweetObserver); #+END_SRC ** Totally Unrelated Links - [[https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/][Greasemonkey]] (Firefox) - [[https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl%3Den][Tampermonkey]] (Chrome) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,3 @@ const getInitialTweets = () => [...document.querySelectorAll('.js-stream-item')] const nonchrono = e => { for (typ of ['follower', 'promoted', 'heartBadge']) { @@ -36,6 +34,4 @@ const attachDocObserver = (tweetObserver) => { const tweetObserver = createTweetObserver(); processTweets(getInitialTweets()); attachTweetObserver(tweetObserver); attachDocObserver(tweetObserver); -
brianloveswords revised this gist
Jul 15, 2018 . 2 changed files with 4 additions and 0 deletions.There are no files selected for viewing
File renamed without changes.This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,5 @@ { // create new lexical scope const getInitialTweets = () => [...document.querySelectorAll('.js-stream-item')] const nonchrono = e => { for (typ of ['follower', 'promoted', 'heartBadge']) { @@ -35,3 +37,5 @@ const tweetObserver = createTweetObserver(); processTweets(getInitialTweets()); attachTweetObserver(tweetObserver); attachDocObserver(tweetObserver); } -
brianloveswords revised this gist
Jul 15, 2018 . 1 changed file with 37 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,37 @@ const getInitialTweets = () => [...document.querySelectorAll('.js-stream-item')] const nonchrono = e => { for (typ of ['follower', 'promoted', 'heartBadge']) { if (e.querySelector(`.context .Icon--${typ}`)) { return true; } } return false; } const decorate = e => e.style.border = "10px solid red"; const remove = e => e.remove(); const handler = decorate; const processTweets = tweets => tweets.filter(nonchrono).forEach(handler); const createTweetObserver = () => { return new MutationObserver(mut => processTweets([...mut[0].addedNodes])) } const attachTweetObserver = observer => { const config = { attributes: false, childList: true, subtree: false }; const target = document.querySelector('ol.js-navigable-stream') observer.observe(target, config) } const attachDocObserver = (tweetObserver) => { const config = { attributes: true, childList: false, subtree: false }; const handler = mut => { tweetObserver.disconnect(); if (mut[0].target.classList.contains('route-home')) { processTweets(getInitialTweets()); attachTweetObserver(tweetObserver); } } const observer = new MutationObserver(handler); observer.observe(document.getElementById('doc'), config); } const tweetObserver = createTweetObserver(); processTweets(getInitialTweets()); attachTweetObserver(tweetObserver); attachDocObserver(tweetObserver); -
brianloveswords revised this gist
Jul 15, 2018 . 1 changed file with 50 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,23 @@ * Identifying Non-Chronological Tweets In Timeline :PROPERTIES: :header-args: :tangle yes :padline no :END: There are two stages to this: 1. identify initially loaded non-chrono tweets 2. identify newly loaded non-chrono tweets as user scrolls We start by getting an array of all tweets. ~querySelectorAll~ returns a ~NodeList~, which unfortunately doesn't have a ~filter~ method, so we use ~[...<iterable>]~ to collect the results into an array. #+BEGIN_SRC js const getInitialTweets = () => [...document.querySelectorAll('.js-stream-item')] #+END_SRC We want a filter that keeps only the tweets that don't belong in the timeline so we can do something with those. One way to identify these tweets is by the icons twitter includes in non-chronological tweets to give the user context of why those tweets are showing up in the timeline. The ~nonchrono~ function looks at the children of the tweet element and if any of the children matches the contextual icon for one of the non-chronological tweets we're looking for – promoted, other user likes, or "your friends follow this person" – we return ~true~ to keep that tweet. #+BEGIN_SRC js const nonchrono = e => { for (typ of ['follower', 'promoted', 'heartBadge']) { if (e.querySelector(`.context .Icon--${typ}`)) { @@ -7,10 +26,28 @@ const nonchrono = e => { } return false; } #+END_SRC We also want a function that will do something with the non-chronological tweets we've found. In our case we just want to highlight them with a red border. If we wanted to remove those nodes instead, we could use [[https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove][ChildNode.remove]]. #+BEGIN_SRC js const decorate = e => e.style.border = "10px solid red"; const remove = e => e.remove(); const handler = decorate; #+END_SRC Putting it together, ~processTweets~ takes a set of tweets, filters down to just the non-chronological tweets, then decorates them. #+BEGIN_SRC js const processTweets = tweets => tweets.filter(nonchrono).forEach(handler); #+END_SRC New tweets are loaded once the user scrolls near the bottom of the page, so we need to do something to make sure those new tweets get processed. This is a great opportunity to use [[https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver][MutationObservers]]. We set up the ~MutationObserver~ to watch the stream container, ~.js-navigable-stream~, and we configure it to only care about the list of children. The observer callback gets a Once we get a mutation event, we can run ~processTweets~ on the list of added nodes. #+BEGIN_SRC js const createTweetObserver = () => { return new MutationObserver(mut => processTweets([...mut[0].addedNodes])) } @@ -19,6 +56,13 @@ const attachTweetObserver = observer => { const target = document.querySelector('ol.js-navigable-stream') observer.observe(target, config) } #+END_SRC We need one more observer: since twitter dot com is a single page app, if the user navigates to their notifications, for example, it will unload their timeline and the node the observer is observing will be destroyed, so we might as well disconnect the observer. Once the timeline is reloaded, we want to re-process the initially loaded tweets and re-attach the observer to the new timeline node. By observing for attribute changes on the ~#doc~ element we can watch for when the class contains "route-home", which is an indicator that the main timeline view is active. #+BEGIN_SRC js const attachDocObserver = (tweetObserver) => { const config = { attributes: true, childList: false, subtree: false }; const handler = mut => { @@ -31,7 +75,13 @@ const attachDocObserver = (tweetObserver) => { const observer = new MutationObserver(handler); observer.observe(document.getElementById('doc'), config); } #+END_SRC With all the functions we need created, we can kick everything off: #+BEGIN_SRC js const tweetObserver = createTweetObserver(); processTweets(getInitialTweets()); attachTweetObserver(tweetObserver); attachDocObserver(tweetObserver); #+END_SRC -
brianloveswords revised this gist
Jul 15, 2018 . 1 changed file with 3 additions and 51 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,23 +1,4 @@ const getInitialTweets = () => [...document.querySelectorAll('.js-stream-item')] const nonchrono = e => { for (typ of ['follower', 'promoted', 'heartBadge']) { if (e.querySelector(`.context .Icon--${typ}`)) { @@ -26,26 +7,10 @@ const nonchrono = e => { } return false; } const decorate = e => e.style.border = "10px solid red"; const remove = e => e.remove(); const handler = decorate; const processTweets = tweets => tweets.filter(nonchrono).forEach(handler); const createTweetObserver = () => { return new MutationObserver(mut => processTweets([...mut[0].addedNodes])) } @@ -54,13 +19,6 @@ const attachTweetObserver = observer => { const target = document.querySelector('ol.js-navigable-stream') observer.observe(target, config) } const attachDocObserver = (tweetObserver) => { const config = { attributes: true, childList: false, subtree: false }; const handler = mut => { @@ -73,13 +31,7 @@ const attachDocObserver = (tweetObserver) => { const observer = new MutationObserver(handler); observer.observe(document.getElementById('doc'), config); } const tweetObserver = createTweetObserver(); processTweets(getInitialTweets()); attachTweetObserver(tweetObserver); attachDocObserver(tweetObserver); -
brianloveswords revised this gist
Jul 15, 2018 . 1 changed file with 55 additions and 11 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,20 +1,26 @@ * Identifying Non-Chronological Tweets In Timeline :PROPERTIES: :header-args: :tangle yes :padline no :END: There are two stages to this: 1. identify initially loaded non-chrono tweets 2. identify newly loaded non-chrono tweets as user scrolls We start by getting an array of all tweets. ~querySelectorAll~ returns a ~NodeList~, which unfortunately doesn't have a ~filter~ method, so we use ~[...<iterable>]~ to collect the results into an array. #+BEGIN_SRC js const getInitialTweets = () => [...document.querySelectorAll('.js-stream-item')] #+END_SRC We want a filter that keeps only the tweets that don't belong in the timeline so we can do something with those. One way to identify these tweets is by the icons twitter includes in non-chronological tweets to give you the user context of why those tweets are showing up in the timeline. The ~nonchrono~ function looks at the children of the tweet element and if any of the children matches the contextual icon for one of the non-chronological tweets we're looking for – promoted, other user likes, or "your friends follow this person" – we return ~true~ to keep that tweet. #+BEGIN_SRC js const nonchrono = e => { for (typ of ['follower', 'promoted', 'heartBadge']) { if (e.querySelector(`.context .Icon--${typ}`)) { return true; } } @@ -25,17 +31,55 @@ let nonchrono = e => { We also want a function that will do something with the non-chronological tweets we've found. In our case we just want to highlight them with a red border. #+BEGIN_SRC js const decorate = e => e.style.border = "10px solid red"; #+END_SRC Putting it together, ~processTweets~ takes a set of tweets, filters down to just the non-chronological tweets, then decorates them. #+BEGIN_SRC js const processTweets = tweets => tweets.filter(nonchrono).forEach(decorate); #+END_SRC New tweets are loaded once the user scrolls near the bottom of the page, so we need to do something to make sure those new tweets get processed. This is a great opportunity to use [[https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver][MutationObservers]]. We set up the ~MutationObserver~ to watch the stream container, ~.js-navigable-stream~, and we configure it to only care about the list of children. The observer callback gets a Once we get a mutation event, we can run ~processTweets~ on the list of added nodes. #+BEGIN_SRC js const createTweetObserver = () => { return new MutationObserver(mut => processTweets([...mut[0].addedNodes])) } const attachTweetObserver = observer => { const config = { attributes: false, childList: true, subtree: false }; const target = document.querySelector('ol.js-navigable-stream') observer.observe(target, config) } #+END_SRC We need one more observer: since twitter dot com is a single page app, if the user navigates to their notifications, for example, it will unload their timeline and the node the observer is observing will be destroyed, so we might as well disconnect the observer. Once the timeline is reloaded, we want to re-process the initially loaded tweets and re-attach the observer to the new timeline node. By observing for attribute changes on the ~#doc~ element we can watch for when the class contains "route-home", which is an indicator that the main timeline view is active. #+BEGIN_SRC js const attachDocObserver = (tweetObserver) => { const config = { attributes: true, childList: false, subtree: false }; const handler = mut => { tweetObserver.disconnect(); if (mut[0].target.classList.contains('route-home')) { processTweets(getInitialTweets()); attachTweetObserver(tweetObserver); } } const observer = new MutationObserver(handler); observer.observe(document.getElementById('doc'), config); } #+END_SRC With all the functions we need created, we can kick everything off: #+BEGIN_SRC js const tweetObserver = createTweetObserver(); processTweets(getInitialTweets()); attachTweetObserver(tweetObserver); attachDocObserver(tweetObserver); #+END_SRC -
brianloveswords revised this gist
Jul 15, 2018 . 1 changed file with 12 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,14 +1,17 @@ * Identifying Non-Chronological Tweets In Timeline :PROPERTIES: :header-args: :tangle yes :END: We want all the top-level element representing each tweet. Fortunately Twitter provides a convenient class we can select against, ~js-stream-item~. We want to be able to filter that array using ~Array#filter~, but ~document.querySelectorAll~ returns a ~NodeList~, so we convert to an array first. #+BEGIN_SRC js let getAllTweets = () => Array.from(document.querySelectorAll('.js-stream-item')) #+END_SRC Given all tweets, we want a filter that keeps just the tweets that don't belong in the timeline so we can do something with those. One way to identify these tweets is by the icons twitter includes in non-chronological tweets to give you the user context of why those tweets are showing up in the timeline. The ~nonchrono~ function looks at the children of the tweet element and if any of the children matches the contextual icon for one of the non-chronological tweets we're looking for – promoted, other user likes, or "your friends follow this person" – we return ~true~ to keep that tweet. #+BEGIN_SRC js let nonchrono = e => { for (typ of ['follower', 'promoted', 'heartBadge']) { if (e.querySelector(`.tweet-context .Icon--${typ}`)) { @@ -21,18 +24,18 @@ let nonchrono = e => { We also want a function that will do something with the non-chronological tweets we've found. In our case we just want to highlight them with a red border. #+BEGIN_SRC js let decorate = e => e.style.border = "10px solid red"; #+END_SRC Putting it together, we get all the tweets, filter down to just the non-chronological tweets, then decorate them. #+BEGIN_SRC js let process = () => getAllTweets().filter(nonchrono).forEach(decorate); #+END_SRC New tweets are loaded once the user scrolls near the bottom of the page, so we need to do something to make sure those new tweets get processed. We could probably get sophisticated about this, but for this quick hack we'll continually run ~process~ on a 1000ms timer. #+BEGIN_SRC js let timer = window.setInterval(process, 1000); #+END_SRC -
brianloveswords created this gist
Jul 15, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,38 @@ #+TITLE: Finding Non-timeline Tweet In Timeline We want all the top-level element representing each tweet. Fortunately Twitter provides a convenient class we can select against, ~js-stream-item~. We want to be able to filter that array using ~Array#filter~, but ~document.querySelectorAll~ returns a ~NodeList~, so we convert to an array first. #+BEGIN_SRC js :exports code :tangle yes let getAllTweets = () => Array.from(document.querySelectorAll('.js-stream-item')) #+END_SRC Given all tweets, we want a filter that keeps just the tweets that don't belong in the timeline so we can do something with those. One way to identify these tweets is by the icons twitter includes in non-chronological tweets to give you the user context of why those tweets are showing up in the timeline. The ~nonchrono~ function looks at the children of the tweet element and if any of the children matches the contextual icon for one of the non-chronological tweets we're looking for – promoted, other user likes, or "your friends follow this person" – we return ~true~ to keep that tweet. #+BEGIN_SRC js :exports code :tangle yes let nonchrono = e => { for (typ of ['follower', 'promoted', 'heartBadge']) { if (e.querySelector(`.tweet-context .Icon--${typ}`)) { return true; } } return false; } #+END_SRC We also want a function that will do something with the non-chronological tweets we've found. In our case we just want to highlight them with a red border. #+BEGIN_SRC js :exports code :tangle yes let decorate = e => e.style.border = "10px solid red" #+END_SRC Putting it together, we get all the tweets, filter down to just the non-chronological tweets, then decorate them. #+BEGIN_SRC js :exports code :tangle yes let process => () => getAllTweets().filter(nonchrono).forEach(decorate) #+END_SRC New tweets are loaded once the user scrolls near the bottom of the page, so we need to do something to make sure those new tweets get processed. We could probably get sophisticated about this, but for this quick hack we'll continually run ~process~ on a 1000ms timer. #+BEGIN_SRC js :exports code :tangle yes let timer = window.setInterval(process, 1000) #+END_SRC