Skip to content

Instantly share code, notes, and snippets.

@brianloveswords
Last active July 16, 2018 00:38
Show Gist options
  • Select an option

  • Save brianloveswords/d7c5d390c6a807c36691e16e9adbc724 to your computer and use it in GitHub Desktop.

Select an option

Save brianloveswords/d7c5d390c6a807c36691e16e9adbc724 to your computer and use it in GitHub Desktop.

Revisions

  1. brianloveswords revised this gist Jul 16, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion !noncrono.org
    Original 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, 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.
    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.

  2. brianloveswords revised this gist Jul 16, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion !noncrono.org
    Original 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. The observer callback gets a Once we get a mutation event, we can run ~processTweets~ on the list of added nodes.
    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 = () => {
  3. brianloveswords revised this gist Jul 16, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion !noncrono.org
    Original 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.
    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 => {
  4. brianloveswords revised this gist Jul 16, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion !noncrono.org
    Original 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 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.
    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 => {
  5. brianloveswords revised this gist Jul 16, 2018. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions !noncrono.org
    Original 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
  6. brianloveswords revised this gist Jul 16, 2018. 2 changed files with 0 additions and 0 deletions.
    File renamed without changes.
    File renamed without changes.
  7. brianloveswords revised this gist Jul 16, 2018. 2 changed files with 5 additions and 5 deletions.
    4 changes: 4 additions & 0 deletions 1-nonchrono.org
    Original 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)
    6 changes: 1 addition & 5 deletions 2-nonchrono.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,3 @@
    { // create new lexical scope

    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);

    }
    attachDocObserver(tweetObserver);
  8. brianloveswords revised this gist Jul 15, 2018. 2 changed files with 4 additions and 0 deletions.
    File renamed without changes.
    4 changes: 4 additions & 0 deletions nonchrono.js → 2-nonchrono.js
    Original 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);

    }
  9. brianloveswords revised this gist Jul 15, 2018. 1 changed file with 37 additions and 0 deletions.
    37 changes: 37 additions & 0 deletions nonchrono.js
    Original 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);
  10. brianloveswords revised this gist Jul 15, 2018. 1 changed file with 50 additions and 0 deletions.
    50 changes: 50 additions & 0 deletions nonchrono.org
    Original 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
  11. brianloveswords revised this gist Jul 15, 2018. 1 changed file with 3 additions and 51 deletions.
    54 changes: 3 additions & 51 deletions nonchrono.org
    Original file line number Diff line number Diff line change
    @@ -1,23 +1,4 @@
    * 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}`)) {
    @@ -26,26 +7,10 @@ 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.

    #+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 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)
    }
    #+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 => {
    @@ -73,13 +31,7 @@ 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
  12. brianloveswords revised this gist Jul 15, 2018. 1 changed file with 55 additions and 11 deletions.
    66 changes: 55 additions & 11 deletions nonchrono.org
    Original file line number Diff line number Diff line change
    @@ -1,20 +1,26 @@
    * Identifying Non-Chronological Tweets In Timeline
    :PROPERTIES:
    :header-args: :tangle yes
    :header-args: :tangle yes :padline no
    :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.
    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
    let getAllTweets = () => Array.from(document.querySelectorAll('.js-stream-item'))
    const getInitialTweets = () => [...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.
    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
    let nonchrono = e => {
    const nonchrono = e => {
    for (typ of ['follower', 'promoted', 'heartBadge']) {
    if (e.querySelector(`.tweet-context .Icon--${typ}`)) {
    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
    let decorate = e => e.style.border = "10px solid red";
    const 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.
    Putting it together, ~processTweets~ takes a set of tweets, filters down to just the
    non-chronological tweets, then decorates them.

    #+BEGIN_SRC js
    let process = () => getAllTweets().filter(nonchrono).forEach(decorate);
    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

    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.
    With all the functions we need created, we can kick everything off:

    #+BEGIN_SRC js
    let timer = window.setInterval(process, 1000);
    const tweetObserver = createTweetObserver();
    processTweets(getInitialTweets());
    attachTweetObserver(tweetObserver);
    attachDocObserver(tweetObserver);
    #+END_SRC
  13. brianloveswords revised this gist Jul 15, 2018. 1 changed file with 12 additions and 9 deletions.
    21 changes: 12 additions & 9 deletions nonchrono.org
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,17 @@
    #+TITLE: Finding Non-timeline Tweet In Timeline
    * 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 :exports code :tangle yes
    #+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 :exports code :tangle yes
    #+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 :exports code :tangle yes
    let decorate = e => e.style.border = "10px solid red"
    #+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 :exports code :tangle yes
    let process => () => getAllTweets().filter(nonchrono).forEach(decorate)
    #+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 :exports code :tangle yes
    let timer = window.setInterval(process, 1000)
    #+BEGIN_SRC js
    let timer = window.setInterval(process, 1000);
    #+END_SRC
  14. brianloveswords created this gist Jul 15, 2018.
    38 changes: 38 additions & 0 deletions nonchrono.org
    Original 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