webtrack-extension / src / content / addon / TwitterTracker.js
TwitterTracker.js
Raw
import Tracker from '../Tracker';

export default class TwitterTracker extends Tracker{

  constructor(worker, privacy, extensionfilter=[]){
    super(worker, privacy);
    this.extensionfilter = extensionfilter;
    this.onStart = this.onStart.bind(this);
    this.is_allowed = null;
    
    this.allow = false;
    this.twitter_debug = false;
    this.twitter_debugEvents = false;
    this.selectors = {
      root: ['#stream-items-id'],
      allow: ['#stream-items-id'],
      articels: ['article'],//#['#stream-items-id li > .tweet'],
      likeButton: ['.ProfileTweet-actionList .js-actionFavorite'],
      commentDialog: {
        wrapper: ['#global-tweet-dialog'],
        id: ['#global-tweet-dialog-body .tweet'],
        content: ['#tweet-box-global'],
        submitButton: ['.tweet-button > .EdgeButton']
      },
      retweetDialog: {
        wrapper: ['#retweet-tweet-dialog'],
        id: ['#retweet-tweet-dialog-body .tweet'],
        content: ['#retweet-with-comment'],
        submitButton: ['.RetweetDialog-footer .tweet-button > .EdgeButton']
      },
      tweetstormDialog:{
        wrapper: ['#Tweetstorm-dialog'],
        id: ['#Tweetstorm-dialog #Tweetstorm-tweet-box-0 .tweet'],
        content: ['.TweetstormDialog-tweet-box.initialized .RichEditor-scrollContainer .tweet-box'],
        submitButton: ['.modal-footer .SendTweetsButton'],
        newTweetButton: ['.modal-footer .js-add-tweet']
      },
      permalinkOverlay:{
        wrapper: ['#permalink-overlay-dialog', '#permalink-overlay'],
        content: ['#permalink-overlay-dialog .permalink-tweet-container .tweet'],
        comment: {
          text: ['.inline-reply-tweetbox-container .RichEditor-scrollContainer .tweet-box'],
          submitButton: ['.tweet-button > .EdgeButton']
        }
      },
      svg_home_activated: 'nav a svg g path[d="M22.58 7.35L12.475 1.897c-.297-.16-.654-.16-.95 0L1.425 7.35c-.486.264-.667.87-.405 1.356.18.335.525.525.88.525.16 0 .324-.038.475-.12l.734-.396 1.59 11.25c.216 1.214 1.31 2.062 2.66 2.062h9.282c1.35 0 2.444-.848 2.662-2.088l1.588-11.225.737.398c.485.263 1.092.082 1.354-.404.263-.486.08-1.093-.404-1.355zM12 15.435c-1.795 0-3.25-1.455-3.25-3.25s1.455-3.25 3.25-3.25 3.25 1.455 3.25 3.25-1.455 3.25-3.25 3.25z"]',
      svg_home_deactivated: 'nav a svg g path[d="M22.46 7.57L12.357 2.115c-.223-.12-.49-.12-.713 0L1.543 7.57c-.364.197-.5.652-.303 1.017.135.25.394.393.66.393.12 0 .243-.03.356-.09l.815-.44L4.7 19.963c.214 1.215 1.308 2.062 2.658 2.062h9.282c1.352 0 2.445-.848 2.663-2.087l1.626-11.49.818.442c.364.193.82.06 1.017-.304.196-.363.06-.818-.304-1.016zm-4.638 12.133c-.107.606-.703.822-1.18.822H7.36c-.48 0-1.075-.216-1.178-.798L4.48 7.69 12 3.628l7.522 4.06-1.7 12.015z"]',
      
      //svg_maintweet_protected: ':not(article) svg g path[d="M19.75 7.31h-1.88c-.19-3.08-2.746-5.526-5.87-5.526S6.32 4.232 6.13 7.31H4.25C3.01 7.31 2 8.317 2 9.56v10.23c0 1.24 1.01 2.25 2.25 2.25h15.5c1.24 0 2.25-1.01 2.25-2.25V9.56c0-1.242-1.01-2.25-2.25-2.25zm-7 8.377v1.396c0 .414-.336.75-.75.75s-.75-.336-.75-.75v-1.396c-.764-.3-1.307-1.04-1.307-1.91 0-1.137.92-2.058 2.057-2.058 1.136 0 2.057.92 2.057 2.056 0 .87-.543 1.61-1.307 1.91zM7.648 7.31C7.838 5.06 9.705 3.284 12 3.284s4.163 1.777 4.352 4.023H7.648z"]',
      //svg_tweet_protected: 'article svg g path[d="M19.75 7.31h-1.88c-.19-3.08-2.746-5.526-5.87-5.526S6.32 4.232 6.13 7.31H4.25C3.01 7.31 2 8.317 2 9.56v10.23c0 1.24 1.01 2.25 2.25 2.25h15.5c1.24 0 2.25-1.01 2.25-2.25V9.56c0-1.242-1.01-2.25-2.25-2.25zm-7 8.377v1.396c0 .414-.336.75-.75.75s-.75-.336-.75-.75v-1.396c-.764-.3-1.307-1.04-1.307-1.91 0-1.137.92-2.058 2.057-2.058 1.136 0 2.057.92 2.057 2.056 0 .87-.543 1.61-1.307 1.91zM7.648 7.31C7.838 5.06 9.705 3.284 12 3.284s4.163 1.777 4.352 4.023H7.648z"]',

      svg_tweet_protected: 'article svg g path[d="M19.75 7.31h-1.88c-.19-3.08-2.746-5.526-5.87-5.526S6.32 4.232 6.13 7.31H4.25C3.01 7.31 2 8.317 2 9.56v10.23c0 1.24 1.01 2.25 2.25 2.25h15.5c1.24 0 2.25-1.01 2.25-2.25V9.56c0-1.242-1.01-2.25-2.25-2.25zm-7 8.377v1.396c0 .414-.336.75-.75.75s-.75-.336-.75-.75v-1.396c-.764-.3-1.307-1.04-1.307-1.91 0-1.137.92-2.058 2.057-2.058 1.136 0 2.057.92 2.057 2.056 0 .87-.543 1.61-1.307 1.91zM7.648 7.31C7.838 5.06 9.705 3.284 12 3.284s4.163 1.777 4.352 4.023H7.648z"]',
      svg_tweet_promoted: 'article svg g path[d="M20.75 2H3.25C2.007 2 1 3.007 1 4.25v15.5C1 20.993 2.007 22 3.25 22h17.5c1.243 0 2.25-1.007 2.25-2.25V4.25C23 3.007 21.993 2 20.75 2zM17.5 13.504c0 .483-.392.875-.875.875s-.875-.393-.875-.876V9.967l-7.547 7.546c-.17.17-.395.256-.62.256s-.447-.086-.618-.257c-.342-.342-.342-.896 0-1.237l7.547-7.547h-3.54c-.482 0-.874-.393-.874-.876s.392-.875.875-.875h5.65c.483 0 .875.39.875.874v5.65z"]',

      svg_account_protected: 'div span svg g path[d="M19.75 7.31h-1.88c-.19-3.08-2.746-5.526-5.87-5.526S6.32 4.232 6.13 7.31H4.25C3.01 7.31 2 8.317 2 9.56v10.23c0 1.24 1.01 2.25 2.25 2.25h15.5c1.24 0 2.25-1.01 2.25-2.25V9.56c0-1.242-1.01-2.25-2.25-2.25zm-7 8.377v1.396c0 .414-.336.75-.75.75s-.75-.336-.75-.75v-1.396c-.764-.3-1.307-1.04-1.307-1.91 0-1.137.92-2.058 2.057-2.058 1.136 0 2.057.92 2.057 2.056 0 .87-.543 1.61-1.307 1.91zM7.648 7.31C7.838 5.06 9.705 3.284 12 3.284s4.163 1.777 4.352 4.023H7.648z"]',

      // assumes that the target is the tweet/comment
      //svg_proeet_favorite: ':not(article) svg g path[d="M12 21.638h-.014C9.403 21.59 1.95 14.856 1.95 8.478c0-3.064 2.525-5.754 5.403-5.754 2.29 0 3.83 1.58 4.646 2.73.814-1.148 2.354-2.73 4.645-2.73 2.88 0 5.404 2.69 5.404 5.755 0 6.376-7.454 13.11-10.037 13.157H12zM7.354 4.225c-2.08 0-3.903 1.988-3.903 4.255 0 5.74 7.034 11.596 8.55 11.658 1.518-.062 8.55-5.917 8.55-11.658 0-2.267-1.823-4.255-3.903-4.255-2.528 0-3.94 2.936-3.952 2.965-.23.562-1.156.562-1.387 0-.014-.03-1.425-2.965-3.954-2.965z"]',
      //svg_favorite: 'article svg g path[d="M12 21.638h-.014C9.403 21.59 1.95 14.856 1.95 8.478c0-3.064 2.525-5.754 5.403-5.754 2.29 0 3.83 1.58 4.646 2.73.814-1.148 2.354-2.73 4.645-2.73 2.88 0 5.404 2.69 5.404 5.755 0 6.376-7.454 13.11-10.037 13.157H12zM7.354 4.225c-2.08 0-3.903 1.988-3.903 4.255 0 5.74 7.034 11.596 8.55 11.658 1.518-.062 8.55-5.917 8.55-11.658 0-2.267-1.823-4.255-3.903-4.255-2.528 0-3.94 2.936-3.952 2.965-.23.562-1.156.562-1.387 0-.014-.03-1.425-2.965-3.954-2.965z"]',
      svg_favorite: 'svg g path[d="M12 21.638h-.014C9.403 21.59 1.95 14.856 1.95 8.478c0-3.064 2.525-5.754 5.403-5.754 2.29 0 3.83 1.58 4.646 2.73.814-1.148 2.354-2.73 4.645-2.73 2.88 0 5.404 2.69 5.404 5.755 0 6.376-7.454 13.11-10.037 13.157H12zM7.354 4.225c-2.08 0-3.903 1.988-3.903 4.255 0 5.74 7.034 11.596 8.55 11.658 1.518-.062 8.55-5.917 8.55-11.658 0-2.267-1.823-4.255-3.903-4.255-2.528 0-3.94 2.936-3.952 2.965-.23.562-1.156.562-1.387 0-.014-.03-1.425-2.965-3.954-2.965z"]',
 
      /**This is not enough!!!, the icon appears twice!!!
      an idea is two check for the content:
      .parentNode.parentNode.parentNode.innerText != ''
      **/
      //svg_maintweet_retweet: ':not(article) svg g path[d="M23.77 15.67c-.292-.293-.767-.293-1.06 0l-2.22 2.22V7.65c0-2.068-1.683-3.75-3.75-3.75h-5.85c-.414 0-.75.336-.75.75s.336.75.75.75h5.85c1.24 0 2.25 1.01 2.25 2.25v10.24l-2.22-2.22c-.293-.293-.768-.293-1.06 0s-.294.768 0 1.06l3.5 3.5c.145.147.337.22.53.22s.383-.072.53-.22l3.5-3.5c.294-.292.294-.767 0-1.06zm-10.66 3.28H7.26c-1.24 0-2.25-1.01-2.25-2.25V6.46l2.22 2.22c.148.147.34.22.532.22s.384-.073.53-.22c.293-.293.293-.768 0-1.06l-3.5-3.5c-.293-.294-.768-.294-1.06 0l-3.5 3.5c-.294.292-.294.767 0 1.06s.767.293 1.06 0l2.22-2.22V16.7c0 2.068 1.683 3.75 3.75 3.75h5.85c.414 0 .75-.336.75-.75s-.337-.75-.75-.75z"]',
      //svg_retweet: 'article svg g path[d="M23.77 15.67c-.292-.293-.767-.293-1.06 0l-2.22 2.22V7.65c0-2.068-1.683-3.75-3.75-3.75h-5.85c-.414 0-.75.336-.75.75s.336.75.75.75h5.85c1.24 0 2.25 1.01 2.25 2.25v10.24l-2.22-2.22c-.293-.293-.768-.293-1.06 0s-.294.768 0 1.06l3.5 3.5c.145.147.337.22.53.22s.383-.072.53-.22l3.5-3.5c.294-.292.294-.767 0-1.06zm-10.66 3.28H7.26c-1.24 0-2.25-1.01-2.25-2.25V6.46l2.22 2.22c.148.147.34.22.532.22s.384-.073.53-.22c.293-.293.293-.768 0-1.06l-3.5-3.5c-.293-.294-.768-.294-1.06 0l-3.5 3.5c-.294.292-.294.767 0 1.06s.767.293 1.06 0l2.22-2.22V16.7c0 2.068 1.683 3.75 3.75 3.75h5.85c.414 0 .75-.336.75-.75s-.337-.75-.75-.75z"]'
      svg_retweet: 'svg g path[d="M23.77 15.67c-.292-.293-.767-.293-1.06 0l-2.22 2.22V7.65c0-2.068-1.683-3.75-3.75-3.75h-5.85c-.414 0-.75.336-.75.75s.336.75.75.75h5.85c1.24 0 2.25 1.01 2.25 2.25v10.24l-2.22-2.22c-.293-.293-.768-.293-1.06 0s-.294.768 0 1.06l3.5 3.5c.145.147.337.22.53.22s.383-.072.53-.22l3.5-3.5c.294-.292.294-.767 0-1.06zm-10.66 3.28H7.26c-1.24 0-2.25-1.01-2.25-2.25V6.46l2.22 2.22c.148.147.34.22.532.22s.384-.073.53-.22c.293-.293.293-.768 0-1.06l-3.5-3.5c-.293-.294-.768-.294-1.06 0l-3.5 3.5c-.294.292-.294.767 0 1.06s.767.293 1.06 0l2.22-2.22V16.7c0 2.068 1.683 3.75 3.75 3.75h5.85c.414 0 .75-.336.75-.75s-.337-.75-.75-.75z"]',
      /** there is also the retweet with comment**/

      // the username can be taken from here
      svg_lists_deactivated: 'nav svg g path[d="M19.75 22H4.25C3.01 22 2 20.99 2 19.75V4.25C2 3.01 3.01 2 4.25 2h15.5C20.99 2 22 3.01 22 4.25v15.5c0 1.24-1.01 2.25-2.25 2.25zM4.25 3.5c-.414 0-.75.337-.75.75v15.5c0 .413.336.75.75.75h15.5c.414 0 .75-.337.75-.75V4.25c0-.413-.336-.75-.75-.75H4.25z"]',
      svg_lists_activated: 'nav svg g path[d="M19.75 2H4.25C3.013 2 2 3.013 2 4.25v15.5C2 20.987 3.013 22 4.25 22h15.5c1.237 0 2.25-1.013 2.25-2.25V4.25C22 3.013 20.987 2 19.75 2zM11 16.75H7c-.414 0-.75-.336-.75-.75s.336-.75.75-.75h4c.414 0 .75.336.75.75s-.336.75-.75.75zm6-4H7c-.414 0-.75-.336-.75-.75s.336-.75.75-.75h10c.414 0 .75.336.75.75s-.336.75-.75.75zm0-4H7c-.414 0-.75-.336-.75-.75s.336-.75.75-.75h10c.414 0 .75.336.75.75s-.336.75-.75.75z"]',

      // the twitter id needs to be search in the .parentNode.parentNode.parentNode.getAttribute('href'), e.g. "/username/status/995297509001048064/analytics"
      svg_maintweet_tweetid: 'a svg g path[d="M12 22c-.414 0-.75-.336-.75-.75V2.75c0-.414.336-.75.75-.75s.75.336.75.75v18.5c0 .414-.336.75-.75.75zm5.14 0c-.415 0-.75-.336-.75-.75V7.89c0-.415.335-.75.75-.75s.75.335.75.75v13.36c0 .414-.337.75-.75.75zM6.86 22c-.413 0-.75-.336-.75-.75V10.973c0-.414.337-.75.75-.75s.75.336.75.75V21.25c0 .414-.335.75-.75.75z"]',
      svg_maintweet_tweetid_regex: /.*status\/(\d+).*/,
      // the twitter id needs to be search in the .parentNode.getAttribute('href'), e.g. "/username/status/1176767347950084096"
      time_tweetid: 'a time',
      time_tweetid_regex: /.*status\/(\d+).*/
    };

    this.lastUrlPath = '';

    this.values = [];
    this.elements = [];
    this.elementStrings = '';
    this.tweetId2Element = {};
    this.whoId2Element = {};
    this.trendId2Element = {};
    this.tweets_exist = false;

    this.header = null;
    this.sidebar_left = null;
    this.sidebar_right = null;

    this.startswith_denylist = ['/messages/', '/settings/', '/notifications/', '/login/', '/account/'];

    this.pos_2nd_denylist = ['bookmarks', 'signup', 'flow', 'notifications'];

    this.privacy_flags = {
      'user_id': null,
      'username': null,
      'guest_id': null,
      'email': null,
      'is_logged_in': null,
      'seen_protected_tweet': null,
      'seen_promoted_tweet': null
    }

    this.logged_username = null;
    this.credentials = null;


    this.tweet_header_ids_captured = new Set();
    this.tweet_header_ids_ignored = new Set();
    this.tweet_ids_captured = new Set();
    this.tweet_ids_ignored = new Set();
    this.promoted_tweets_captured = new Set();
    this.tweet_wo_id_seen = 0;


  }


  /**
   * [_getValues return values of articel]
   * @param  {Object} target
   * @return {Array}
   */
  _getValues(target){
    let search = [
      {
        name: 'articel-time',
        query: ['.tweet-timestamp'],
        default: undefined,
        filter: e => e.getAttribute('title')
      },
      {
        name: 'articel-link',
        query: ['.tweet-timestamp'],
        default: undefined,
        filter: e => location.href.substring(0, location.href.length-1)+e.getAttribute('href')
      },
      {
        name: 'articel-headertext',
        query: ['.tweet-text'],
        default: undefined,
        filter: e => e.textContent
      },
      {
        name: 'articel-description',
        query: ['.TweetTextSize'],
        default: undefined,
        filter: e => e.textContent
      },
      {
        name: 'articel-publisher-name',
        query: ['.FullNameGroup .fullname'],
        default: undefined,
        filter: e => e.textContent.match(/\b(\w*[A-Za-z0-9 äÄöÖüÜß]\w*)\b/g).join('')
      },
      {
        name: 'articel-count-likes',
        query: ['.ProfileTweet-actionButton.js-actionFavorite .ProfileTweet-actionCountForPresentation'],
        default: undefined,
        filter: e => parseInt(e.textContent, 10)
      },
      {
        name: 'articel-count-comments',
        query: ['.ProfileTweet-actionCountList .ProfileTweet-actionCount'],
        default: undefined,
        filter: e => parseInt(e.getAttribute('data-tweet-stat-count'), 10)
      }
    ],
    values = [];
    for (let s of search) {
      try {
        let value = s.default;
        for (let query of s.query) {
          var r = target.querySelectorAll(query);
          if(r.length>0){
            r = r[0];
            let data = s.filter(r);
            if(data!=null) value = data;
          }
        }//for
        values.push({name: s.name, value: value})
      } catch (err) {
        console.log(err);
        console.log(target, err.toString());
      }
    }//for
    return values;
  }


  /**
   * [_setAllow check if url changed and search in dom if find some elements they not allowed and set this.allow]
   */
  _setAllow(){
    return new Promise((resolve, reject) => {
      console.log(this.lastURL!==location.pathname, this.lastURL, location.pathname);
      if(this.lastUrlPath!==location.pathname){
        setTimeout(() => {
          this.lastUrlPath = location.pathname
          console.log('?????????????????');
          for (let query of this.selectors.allow) {
            let found = document.querySelectorAll(query+':not(.tracked)');
            console.log('found', found);
            this.allow = !found.length>0
          }
          console.log('ALLOW?', this.allow);
          resolve(this.allow);
        }, 2000)
      }else{
        resolve(this.allow);
      }
    });
  }


  get_is_content_allowed() {
    if (document.querySelector(this.selectors.svg_account_protected)){
      return false;
    }
    return true;
  }

  /**
   * [_setEventLikeButton set like-events for un-/like buttons from articel]
   * @param {number} id
   */
  _setEventLikeButton(id){
    let likeButton = this._getElements(this.selectors.likeButton, this.tweetId2Element[id]);
    for (var i = 0; i < likeButton.length; i++) {
      likeButton[i].addEventListener('click', e => {
        let value = 'like';
        if(e.srcElement.parentNode.parentNode.classList.contains('u-linkClean')) value = 'unlike';
        this.eventFn.onEvent({
          event: 'like',
          type: 'articel',
          values: this._getValues(this.tweetId2Element[id]).concat([
             {name: 'like', value: value}
          ])
        })
      })//mouseover
    }
  }

  /**
   * [_eventListenComment listen the comment dialog and fire comment event if the user write some comment]
   */
  _eventListenComment(){
    let tweet = this._getElements(this.selectors.commentDialog.id, document);
    if(tweet.length>0){
      let id = tweet[0].getAttribute("data-tweet-id");
      if(Object.keys(this.tweetId2Element).includes(id)){
        let wrapper = this._getElements(this.selectors.commentDialog.wrapper, document, {setBorder: false})[0];
        let content = this._getElements(this.selectors.commentDialog.content, wrapper)[0];
        let submitButton = this._getElements(this.selectors.commentDialog.submitButton, wrapper)[0];
        submitButton.addEventListener('click', e => {
          this.eventFn.onEvent({
            event: 'comment',
            type: 'articel',
            values: this._getValues(this.tweetId2Element[id]).concat([
               {name: 'comment', value: content.textContent}
            ])
          })
        })
      }//if
    }//if
  }

  /**
   * [_eventListenComment listen the retweet dialog and fire retweet-event if the user write some new retweet]
   */
  _eventListenRetweet(){
    let tweet = this._getElements(this.selectors.retweetDialog.id, document);
    if(tweet.length>0){
      let id = tweet[0].getAttribute("data-tweet-id");
      if(Object.keys(this.tweetId2Element).includes(id)){
        let wrapper = this._getElements(this.selectors.retweetDialog.wrapper, document, {setBorder: false})[0];
        let content = this._getElements(this.selectors.retweetDialog.content, wrapper)[0];
        let submitButtons = this._getElements(this.selectors.retweetDialog.submitButton, wrapper);
        for (var i = 0; i < submitButtons.length; i++) {
          submitButtons[i].addEventListener('click', e => {
            this.eventFn.onEvent({
              event: 'retweet',
              type: 'articel',
              values: this._getValues(this.tweetId2Element[id]).concat([
                 {name: 'comment', value: content.textContent}
              ])
            })
          })
        }//for
      }//if
    }//if
  }

  /**
   * [_eventListenTweetstorm listen the tweetstorm-dialog and fire event if the user write new threds]
   */
  _eventListenTweetstorm(){
    let tweet = this._getElements(this.selectors.tweetstormDialog.id, document);
    if(tweet.length>0){
      let id = tweet[0].getAttribute("data-tweet-id");
      if(Object.keys(this.tweetId2Element).includes(id)){

        let wrapper = this._getElements(this.selectors.tweetstormDialog.wrapper, document, {setBorder: false})[0];
        let content = this._getElements(this.selectors.tweetstormDialog.content, wrapper);
        let submitButton = this._getElements(this.selectors.tweetstormDialog.submitButton, wrapper)[0];
        let newTweetButton = this._getElements(this.selectors.tweetstormDialog.newTweetButton, wrapper)[0];

        newTweetButton.addEventListener('click', () => setTimeout(()=>{
          let newContent = this._getElements(this.selectors.tweetstormDialog.content, wrapper);
          content = content.concat(newContent);
        }, 500))

        submitButton.addEventListener('click', e => {
          let values = this._getValues(this.tweetId2Element[id]),
          comments = content.map(e => e.textContent).filter(e => e.length>0);

          for (var i = 0; i < comments.length; i++)
            values.push({name: 'comment-'+(i+1), value: comments[i]})

          this.eventFn.onEvent({
            event: 'retweet',
            type: 'articel',
            values: values
          })
        })
      }//if
    }//if
  }

  /**
   * [_eventListenPermalinkOverlay description]
   */
  _eventListenPermalinkOverlay(){
    let articels = this._getElements(this.selectors.permalinkOverlay.content, document);
    if(articels.length==1 && this._isPublic(articels[0])){
      let id = articels[0].getAttribute("data-tweet-id");
      this.tweetId2Element[id] = articels[0];
      let wrapper = this._getElements(this.selectors.permalinkOverlay.wrapper, document)[0];
      let content = this._getElements(this.selectors.permalinkOverlay.comment.text, wrapper)[0];
      let submitButton = this._getElements(this.selectors.permalinkOverlay.comment.submitButton, wrapper)[0];
      submitButton.addEventListener('click', e => {
        this.eventFn.onEvent({
          event: 'comment',
          type: 'articel',
          values: this._getValues(this.tweetId2Element[id]).concat([
             {name: 'comment', value: content.textContent}
          ])
        })
      })
    }//if
  }


  /**
   * Setup the credentials for the logged user (if any)
   */
  reset_credentials(){

    this.is_logged_in = this._isLoggedTwitter();

    if (this.is_logged_in){
      this.credentials = this.get_credentials();

      if (this.credentials != null) {
        try {
          this.logged_user_id = this.credentials.session.user_id;
          this.entity = this.credentials.entities.users.entities[this.logged_user_id];
          this.logged_username = this.entity.screen_name;
          this.logged_fullname = this.entity.name;
          this.logged_guest_id = this.credentials.session.guestId;

        } catch( error) {
          this.logged_username = this.get_username();
        }
        
      } else {
        this.logged_username = this.get_username();
      }
      

      this.is_content_allowed = this.get_is_content_allowed();


    }else{
      this.is_content_allowed = true;
    }


    // is social media path allowed
    this.is_sm_path_allowed = this.get_is_sm_path_allowed(location.pathname);
    console.log('IS ALLOWED', location.pathname, this.is_sm_path_allowed);


    if(this.twitter_debug) {
      let style = '';
      if (this.is_logged_in) {
        style += "border-top:7px solid green !important;";
      } else {
        style += "border-top:7px solid red !important;";
      }

      if (this.logged_username) {
        style += "border-right:7px solid green !important;";
      } else {
        style += "border-right:7px solid red !important;";
      }

      if (this.logged_fullname) {
        style += "border-bottom:7px solid green !important;";
      } else {
        style += "border-bottom:7px solid red !important;";
      }

      if (this.logged_guest_id) {
        style += "border-left:7px solid green !important;";
      } else {
        style += "border-left:7px solid red !important;";
      }

      if (this.is_sm_path_allowed) {
        style += "outline:7px solid blue !important;";
      } else {
        style += "outline:7px solid red !important;";
      }
      document.querySelector('a[aria-label]').setAttribute("style", style);

    }

  }



  /**
   * get the metadata from the file
   * @return {object} the metadata of the html
   */
  getMetadata(){
    let metadata = super.getMetadata();
    let anonym = {};

    if (this.logged_user_id) {
      anonym['user_id'] = this.logged_user_id;
      this.privacy_flags['user_id'] = true;
    }

    if (this.logged_username) {
      anonym['username'] = this.logged_username;
      this.privacy_flags['username'] = true;
    }

    if (this.logged_guest_id) {
      anonym['guest_id'] = this.logged_guest_id;
      this.privacy_flags['guest_id'] = true;
    }

    if (this.logged_fullname) {
      anonym['fullname'] = this.logged_fullname;
      this.privacy_flags['fullname'] = true;
    }

    metadata['anonym'] = anonym;
    metadata['privacy_flags'] = this.privacy_flags;

    return metadata;

  }

  /**
  Load the credentials from the script in twitter
  returns a dictionary with the credentials
  **/  
  get_credentials() {
    let scripts = document.querySelectorAll('body script[nonce]');
    for (var i = 0; i < scripts.length; i++) {
      let sc = scripts[i].textContent.trim();
      if (sc.startsWith('window.__INITIAL_STATE__')) {
        try {
          //window.__INITIAL_STATE__
          let mid_index = sc.indexOf(';');
          let initial_state = sc.substring(sc.indexOf('{'), mid_index);
          return JSON.parse(initial_state);

          // // extractin important parts from the json
          // let user_id = initial_state['session']['user_id'];
          // let guestId = initial_state['session']['guestId'];
          // let user_json = initial_state['entities']['users']['entities'][user_id];
          // user_json['user_id'] = user_id;

          // //window.__META_DATA__
          // let meta = sc.substring(mid_index).substring(
          //   sc.substring(sc.indexOf('{'), 
          //   sc.indexOf(';')));
          // meta = JSON.parse(meta);
          // user_json['is_logged_in'] = meta["isLoggedIn"];

          // return user_json;
        } catch (error){
          console.log(error);
        }

        
      }
    }
    return null;
  }


  /**
   * return true if user is logged in twitter
   * @return {boolean} true if user is logged
   */
  get_username(){
    //document.documentElement.querySelectorAll('script,link,svg,style');
    //var navs = document.documentElement.getElementsByTagName('nav');
    //var navs = document.documentElement.querySelectorAll('nav a[aria-label="Profile"]');
    //document.querySelector('head link[hreflang]') 


    var svg = document.documentElement.querySelector(
      this.selectors.svg_lists_deactivated);
    if (svg){
      return svg.parentElement.parentElement.parentElement.parentElement.parentElement.pathname.split('/')[1];
    } else {
      var svg = document.documentElement.querySelector(this.selectors.svg_lists_activated);
      if (svg){
        return svg.parentElement.parentElement.parentElement.parentElement.parentElement.pathname.split('/')[1];
      } 
    }
    return null;
  }

  /**
   * return true if user is logged in twitter
   * @return {boolean} true if user is logged
   */
  _isLoggedTwitter(){
    //document.documentElement.querySelectorAll('script,link,svg,style');
    //var navs = document.documentElement.getElementsByTagName('nav');
    //var navs = document.documentElement.querySelectorAll('nav a[aria-label="Profile"]');

    var svgs = document.documentElement.querySelectorAll(
      this.selectors.svg_home_deactivated);
    if (svgs && (svgs.length > 0)){
      this.privacy_flags['is_logged_in'] = true;
      return true;
    } else {
      var svgs = document.documentElement.querySelectorAll(
        this.selectors.svg_home_activated);
      if (svgs && (svgs.length > 0)){
        this.privacy_flags['is_logged_in'] = true;
        return true;
      } 
    }
    return false;
  }


  /**
   * [_isPublic checks if element is for the public oder private]
   * @param  {Object}  target [DomElement]
   * @return {Boolean}
   */
  _isPublic(target){

    // if the user is not logged in, the content is public for sure
    if (!this.is_logged_in){
      return true;
    }

    return !this._isProtected(target);
  }


  /**
   * [_isPublic checks if element is for the public oder private]
   * @param  {Object}  target [DomElement]
   * @return {Boolean}
   */
  _isProtected(target){

   // if the protected svg appear in the tweet, the content is private
    if (target.querySelector(this.selectors.svg_tweet_protected)) {
      this.privacy_flags['seen_protected_tweet'] = true;
      return true;
    } else {
      return false;
    }
  }

  /**
   * [_isPublic checks if element is for the public oder private]
   * @param  {Object}  target [DomElement]
   * @return {Boolean}
   */
  _isPromoted(target){

    // if the protected svg appear in the tweet, the content is private
    if (target.querySelector(this.selectors.svg_tweet_promoted)) {
      this.privacy_flags['seen_promoted_tweet'] = true;
      return true;
    } else {
      return false;
    }
  }


  /**
   * [_isHeaderTweet checks if element is the one open (header Tweet) ]
   * @param  {Object}  target [DomElement]
   * @return {Boolean}
   */
  _isHeaderTweet(target){
    if (target.hasChildNodes()){
      // Hackish way to detect the header
      if(target.children[0].childElementCount > 2) {
        return true;
      }
    }
    return false; 
  }

    /**
   * [_getHeaderId get the tweet header id from the location bar]
   * @param  {Object}  target [DomElement]
   * @return {int}
   */
  _getHeaderId(article){
    
    var match = location.pathname.match(this.selectors.time_tweetid_regex);
    if (match != null){
      return match[1];
    }
    return null;

  }


    /**
   * [_getPromotedId get the tweet header id from the location bar]
   * @param  {Object}  target [DomElement]
   * @return {int}
   */
  _getPromotedId(article){
    
    var _anchor = article.querySelector('a[role=link]');
    if (_anchor != null){
      return _anchor.getAttribute('href');
    }
    return 'default_promoted_id';

  }


  /**
   * [_getId looks for an href that contains the id of the element]
   * @param  {Object}  target [DomElement]
   * @return {int}
   */
  _getId(article){
    try {

      var times = article.querySelectorAll(this.selectors.time_tweetid);
      if (times.length > 0){
        return times[0].parentNode.getAttribute('href').match(this.selectors.time_tweetid_regex)[1];
      } else {
        var svgs = article.querySelectorAll(this.selectors.svg_maintweet_tweetid);
        if (svgs.length > 0){
          return svgs[0].parentNode.parentNode.parentNode.getAttribute('href').match(this.selectors.svg_maintweet_tweetid_regex)[1];
        }
      }
    }
    catch(error) {
      console.log(error);
      console.log('Unexpected error getting Twitter ID in TwitterTracker');
    }
    
    return null;
  }


  /**
   * [_getPublicArticels start tracking an article]
   */
  trackArticle(id, article, category){
    let cloned = article.cloneNode(true);
    cloned.classList.add(category);
    this.tweetId2Element[id] = cloned;
  }

  /**
   * [_getPublicArticels return elements of public articels]
   * @return {Array} true if at least one article was found
   */
  addPublicArticles(){
    let articles = document.querySelectorAll('article');

    for (var i = 0; i < articles.length; i++) {
      //let id = articles[i].getAttribute('data-tweet-id');
      let id = this._getId(articles[i]);
      let header_id = this._getHeaderId();

      if ((i == 0) && (header_id != null) && !this._isProtected(articles[i])) {
        this.tweet_header_ids_captured.add(header_id);
        this.trackArticle(header_id, articles[i], 'header-not-protected');
        if(this.twitter_debug) articles[i].setAttribute("style", "border:3px solid cyan !important;");
      
      } else if ((i == 0) && (header_id != null) && this._isProtected(articles[i])) {
        this.tweet_header_ids_ignored.add(header_id);
        if(this.twitter_debug) articles[i].setAttribute("style", "border:3px solid yellow !important;");
      
      } else if (this._isPromoted(articles[i])) {
        this.promoted_tweets_captured.add(promoted_id);
        let promoted_id = this._getPromotedId(articles[i]);
        this.trackArticle(promoted_id, articles[i], 'promoted');
        if(this.twitter_debug) articles[i].setAttribute("style", "border:3px solid blue !important;");
      
      } else if(id && !this._isProtected(articles[i])) {
        this.tweet_ids_captured.add(id);
        this.trackArticle(id, articles[i], 'not-protected');
        if(this.twitter_debug) articles[i].setAttribute("style", "border:3px solid green !important;");
      
      } else if(id) {
        this.tweet_ids_ignored.add(id);
        // This does not seem to be a public tweet
        if(this.twitter_debug) articles[i].setAttribute("style", "border:3px solid red !important;");
      
      } else {
        this.tweet_wo_id_seen += 1;
        // This does not seem to be a tweet at all (no id)
        if(this.twitter_debug) articles[i].setAttribute("style", "border:6px solid magent !important;");
      }
    }

    if (this.twitter_debug) console.log('Articles Found: ' + articles.length);

    // return True if at lest one article was found (regardless it being public/private)
    return articles.length > 0;
  }

  /**
   * [_getPublicArticels return elements of public articels]
   * @return {Array} true if at least one article was found
   */
  addPublicArticlesIfLoggedOut(){
    let articles = document.querySelectorAll('#stream-items-id li > .tweet');
    let counter = 0;

    for (var i = 0; i < articles.length; i++) {
      let id = articles[i].getAttribute('data-tweet-id');
      if (id != null) {
        this.trackArticle(id, articles[i], 'logged-out');
        counter += 1;
      }
    }

    return articles.length > 0;
  }


  /**
   * [_getId looks for a user id to follow]
   * @param  {Object}  target [DomElement]
   * @return {int}
   */
  _getWhoId(article){
    try {
      var _clone = article.cloneNode(true);
      var _anchor = article.querySelector('a[href]');
      if (_anchor){
        return _anchor.getAttribute('href');
      }
    }
    catch(error) {
      console.log(error);
      console.log('Unexpected error getting Who ID in TwitterTracker');
    }
    
    return null;
  }

  /**
   * [_getPublicArticels return elements of public articels]
   * @return {Array} true if at least one article was found
   */
  addWhoToFollow(){
    let who = document.querySelectorAll('div[data-testid="primaryColumn"] div[data-testid="UserCell"]')

    let counter = 0;
    for (var i = 0; i < who.length; i++) {
      //let id = who[i].getAttribute('data-tweet-id');
      let id = this._getWhoId(who[i]);
      if (id != null) {
        //if (this.twitter_debug) console.log('ID detected: ' + id);
        this.whoId2Element[id] = who[i].cloneNode(true);
        counter += 1;
      }
    }

    if (this.twitter_debug) console.log('WhoToFollow: ' + who.length);
    if (this.twitter_debug) console.log('WhoToFollow correct: ' + counter);

    // return True if at lest one article was found (regardless it being public/private)
    return who.length > 0;
  }




  /**
   * [_getId looks for a user id to follow]
   * @param  {DomElement}  target [DomElement]
   * @return {string} the element
   */
  _getTrendId(target){
    try {
      var _clone = target.cloneNode(true);
      var hashtag = target.querySelector('div[dir=ltr]');
      if (hashtag){
        return hashtag.innerText;
      }
    }
    catch(error) {
      console.log(error);
      console.log('Unexpected error getting Trend ID in TwitterTracker');
    }
    
    return null;
  }


  /**
   * add trends
   * @return number of trends
   */
  addTrends(){
    let trend = document.querySelectorAll('div[data-testid="primaryColumn"] div[data-testid="trend"]');

    let counter = 0;
    for (var i = 0; i < trend.length; i++) {
      //let id = trend[i].getAttribute('data-tweet-id');
      let id = this._getTrendId(trend[i]);
      if (id != null) {
        //if (this.twitter_debug) console.log('ID detected: ' + id);
        this.trendId2Element[id] = trend[i].cloneNode(true);
        counter += 1;
      }
    }

    if (this.twitter_debug) console.log('Trend: ' + trend.length);
    if (this.twitter_debug) console.log('Trend correct: ' + counter);

    // return True if at lest one article was found (regardless it being public/private)
    return trend.length > 0;
  }


  /**
   * [assembleDom with the existent html]
   * @return {String}
   */
  assembleDom(){
    var tweet_strings = '';
    var counter = 0;
    for (var key in this.tweetId2Element) {
      if (this.tweetId2Element.hasOwnProperty(key)){
        this.tweetId2Element[key].setAttribute('webtrack-tweet-id', key);
        tweet_strings += this.tweetId2Element[key].outerHTML;
        counter += 1;
      }
    }
    if(counter == 0){
      if (this.twitter_debug) console.log('No public tweets/replies found');
    }


    if (this.twitter_debug) console.log('Sending ' + counter + ' tweets');
    return '<html tweet_header_ids_captured="' + this.tweet_header_ids_captured.size +
                '" tweet_header_ids_ignored="' + this.tweet_header_ids_ignored.size +
                '" tweet_ids_captured="' + this.tweet_ids_captured.size +
                '" tweet_ids_ignored="' + this.tweet_ids_ignored.size +
                '" promoted_tweets_captured="' + this.promoted_tweets_captured.size +
                '" tweet_wo_id_seen="' + this.tweet_wo_id_seen +
            '" ><head></head><body><h1>Tweets</h1><div class="tweets">' + tweet_strings + '</div></body></html>';
  }


  /**
   * [return element without embedd js, css, etc]
   * @return {Promise}
   */
  _clean_embedded_scripts(target, selectors='script:not([src]),svg,style'){
    return super._clean_embedded_scripts(target, selectors + ',noscript');
  }

  /**
   * [return dom as string]
   * @return {Promise}
   */
  getDom(){
    //if (this._isNotLoggedTwitter()){
    //    return super.getDom();
    //} else {
      return new Promise((resolve, reject) => {
        let found = this.addPublicArticles();
        this.tweets_exist = found || this.tweets_exist;

        if (this.tweets_exist){
          if (this.twitter_debug) console.log('assembling dom');
          resolve(this.assembleDom());
        } else if (this.tweets_exist == false) {
          if (this.twitter_debug) console.log('No tweets were found');
          // just send the entire html
          resolve("<html><head></head><body><h1>No Tweets Found</h1></body></html>");
        }
      });
    //}
  }

  /**
   * [onStart on start event]
   * @param  {Function} fn
   */
  onStart(fn){
    setTimeout(() => {
      if (this.twitter_debug) console.log('START!!!!');
      fn(1000);
    }, 500);
  }

}//class