<commit_before>const __bind = function(fn, me) {
  return function() {
    return fn.apply(me, arguments);
  };
};

let somaPlayerBG;

const SomaPlayerBackground = (function() {
  function SomaPlayerBackground() {
    this.onTrack = __bind(this.onTrack, this);
    console.debug('initializing SomaPlayer background script');
    this.lastfm = SomaPlayerUtil.getLastfmConnection();
    this.audio = document.querySelector('audio');
    if (!this.audio) {
      console.debug('adding new audio tag');
      this.audio = document.createElement('audio');
      this.audio.setAttribute('autoplay', 'true');
      this.audio.setAttribute('data-station', '');
      document.body.appendChild(this.audio);
    }
    this.titleEl = document.getElementById('title');
    if (!this.titleEl) {
      this.titleEl = document.createElement('div');
      this.titleEl.id = 'title';
      document.body.appendChild(this.titleEl);
    }
    this.artistEl = document.getElementById('artist');
    if (!this.artistEl) {
      this.artistEl = document.createElement('div');
      this.artistEl.id = 'artist';
      document.body.appendChild(this.artistEl);
    }
    this.socket = io.connect(SomaPlayerConfig.scrobbler_api_url);
  }

  SomaPlayerBackground.prototype.play = function(station) {
    console.debug('playing station', station);
    this.resetTrackInfoIfNecessary(station);
    this.subscribe(station);
    console.debug('adding track listener');
    this.socket.on('track', this.onTrack);
    this.audio.src = SomaPlayerConfig.somafm_station_url + station;
    this.audio.setAttribute('data-station', station);
    this.audio.removeAttribute('data-paused');
  };

  SomaPlayerBackground.prototype.resetTrackInfoIfNecessary = function(station) {
    if (this.audio.getAttribute('data-station') === station) {
      return;
    }
    console.debug('changed station from',
                  this.audio.getAttribute('data-station'), 'to', station,
                  ', clearing current track info');
    this.titleEl.textContent = '';
    this.artistEl.textContent = '';
  };

  SomaPlayerBackground.prototype.getCurrentTrackInfo = function(station) {
    SomaPlayerUtil.getCurrentTrackInfo(station, track => {
      this.titleEl.textContent = track.title;
      this.artistEl.textContent = track.artist;
    });
  };

  SomaPlayerBackground.prototype.subscribe = function(station) {
    const emitSubscribe = () => {
      console.debug('subscribing to', station, '...');
      this.socket.emit('subscribe', station, response => {
        if (response.subscribed) {
          console.debug('subscribed to', station);
          this.getCurrentTrackInfo(station);
        } else {
          console.error('failed to subscribe to', station, response);
        }
      });
    };
    if (this.socket.connected) {
      emitSubscribe();
    } else {
      this.socket.on('connect', () => {
        emitSubscribe();
      });
    }
  };

  SomaPlayerBackground.prototype.onTrack = function(track) {
    console.debug('new track:', track);
    this.titleEl.textContent = track.title;
    this.artistEl.textContent = track.artist;
    SomaPlayerUtil.getOptions(opts => {
      this.notifyOfTrack(track, opts);
      this.scrobbleTrack(track, opts);
    });
  };

  SomaPlayerBackground.prototype.notifyOfTrack = function(track, opts) {
    if (opts.notifications === false) {
      return;
    }
    const notificationOpts = {
      type: 'basic',
      title: track.artist,
      message: track.title,
      iconUrl: 'icon48.png'
    };
    return chrome.notifications.create('', notificationOpts, () => {});
  };

  SomaPlayerBackground.prototype.scrobbleTrack = function(track, opts) {
    if (!(opts.lastfm_session_key && opts.lastfm_user && opts.scrobbling)) {
      return;
    }
    console.debug('scrobbling track for Last.fm user', opts.lastfm_user, track);
    const scrobbleData = {
      artist: (track.artist || '').replace(/"/g, "'"),
      track: (track.title || '').replace(/"/g, "'"),
      user: opts.lastfm_user,
      chosenByUser: 0,
      timestamp: Math.round((new Date()).getTime() / 1000)
    };
    return this.lastfm.track.scrobble(scrobbleData, {
      key: opts.lastfm_session_key
    }, {
      success() {
        let e;
        try {
          const iframeWin = document.querySelector('iframe').contentWindow;
          const iframeDoc = iframeWin.contentDocument ? iframeWin.contentDocument : iframeWin.contentWindow.document;
          iframeDoc.querySelector('form').submit();
          return console.debug('scrobbled track');
        } catch (_error) {
          e = _error;
          if (e.name !== 'SecurityError') {
            throw e;
          }
        }
      },
      error(data) {
        return console.error('failed to scrobble track; response:', data);
      }
    });
  };

  SomaPlayerBackground.prototype.pause = function(station) {
    console.debug('pausing station', station);
    this.unsubscribe(station);
    this.audio.pause();
    this.audio.currentTime = 0;
    this.audio.setAttribute('data-paused', 'true');
  };

  SomaPlayerBackground.prototype.clear = function() {
    const info = this.getInfo();
    this.unsubscribe(info.station);
    this.audio.pause();
    this.audio.currentTime = 0;
    this.audio.setAttribute('data-station', '');
    this.audio.removeAttribute('data-paused');
  };

  SomaPlayerBackground.prototype.unsubscribe = function(station) {
    if (!(typeof station === 'string' && station.length > 0)) {
      return;
    }
    console.debug('unsubscribing from', station, '...');
    this.socket.emit('unsubscribe', station, response => {
      if (response.unsubscribed) {
        console.debug('unsubscribed from', station);
      } else {
        console.error('failed to unsubscribe from', station);
      }
    });
    console.debug('removing track listener');
    this.socket.removeListener('track', this.onTrack);
  };

  SomaPlayerBackground.prototype.getInfo = function() {
    let station = '';
    if (this.audio) {
      station = this.audio.getAttribute('data-station') || '';
    }
    return {
      station,
      artist: this.artistEl.textContent,
      title: this.titleEl.textContent,
      paused: this.audio.hasAttribute('data-paused') || station === ''
    };
  };

  SomaPlayerBackground.prototype.setStations = function(stations) {
    console.debug('set stations', stations);
    SomaPlayerUtil.getOptions(opts => {
      opts.stations = stations;
      SomaPlayerUtil.setOptions(opts);
    });
  };

  SomaPlayerBackground.prototype.getStations = function(callback) {
    SomaPlayerUtil.getOptions(opts => {
      return callback(opts.stations);
    });
  };

  SomaPlayerBackground.prototype.fetchStations = function(callback) {
    const url = `${SomaPlayerConfig.somafm_api_url}channels.json`;
    console.debug(`fetching channels list from ${url}`);
    return window.fetch(url).then(response => {
      return response.json();
    }).then(this.onStationsFetched.bind(this, callback)).
       catch(this.onStationFetchError.bind(this, callback));
  };

  SomaPlayerBackground.prototype.onStationsFetched = function(callback, data) {
    console.debug('fetched stations list', data);
    const stations = data.channels;
    const simpleStations = [];
    for (let i = 0; i < stations.length; i++) {
      simpleStations.push({
        id: stations[i].id,
        title: stations[i].title
      });
    }
    this.setStations(simpleStations);
    callback(simpleStations);
  };

  SomaPlayerBackground.prototype.onStationFetchError = function(callback, xhr, status, error) {
    console.error('failed to fetch stations list', error);
    callback(null, true);
  };

  return SomaPlayerBackground;
})();

document.addEventListener('DOMContentLoaded', () => {
  somaPlayerBG = new SomaPlayerBackground();
});

SomaPlayerUtil.receiveMessage((request, sender, sendResponse) => {
  let info;
  console.debug('received message in background:', request);
  if (request.action === 'play') {
    somaPlayerBG.play(request.station);
    sendResponse();
    return true;
  }
  if (request.action === 'pause') {
    somaPlayerBG.pause(request.station);
    sendResponse();
    return true;
  }
  if (request.action === 'info') {
    info = somaPlayerBG.getInfo();
    console.debug('info:', info);
    sendResponse(info);
    return true;
  }
  if (request.action === 'clear') {
    somaPlayerBG.clear();
    sendResponse();
    return true;
  }
  if (request.action === 'fetch_stations') {
    somaPlayerBG.fetchStations((stations, error) => {
      return sendResponse(stations, error);
    });
    return true;
  }
  if (request.action === 'get_stations') {
    somaPlayerBG.getStations(stations => {
      console.debug('got saved list of stations:', stations);
      return sendResponse(stations);
    });
    return true;
  }
});
<commit_msg>Fix scrobbling via iframe<commit_after>const __bind = function(fn, me) {
  return function() {
    return fn.apply(me, arguments);
  };
};

let somaPlayerBG;

const SomaPlayerBackground = (function() {
  function SomaPlayerBackground() {
    this.onTrack = __bind(this.onTrack, this);
    console.debug('initializing SomaPlayer background script');
    this.lastfm = SomaPlayerUtil.getLastfmConnection();
    this.audio = document.querySelector('audio');
    if (!this.audio) {
      console.debug('adding new audio tag');
      this.audio = document.createElement('audio');
      this.audio.setAttribute('autoplay', 'true');
      this.audio.setAttribute('data-station', '');
      document.body.appendChild(this.audio);
    }
    this.titleEl = document.getElementById('title');
    if (!this.titleEl) {
      this.titleEl = document.createElement('div');
      this.titleEl.id = 'title';
      document.body.appendChild(this.titleEl);
    }
    this.artistEl = document.getElementById('artist');
    if (!this.artistEl) {
      this.artistEl = document.createElement('div');
      this.artistEl.id = 'artist';
      document.body.appendChild(this.artistEl);
    }
    this.socket = io.connect(SomaPlayerConfig.scrobbler_api_url);
  }

  SomaPlayerBackground.prototype.play = function(station) {
    console.debug('playing station', station);
    this.resetTrackInfoIfNecessary(station);
    this.subscribe(station);
    console.debug('adding track listener');
    this.socket.on('track', this.onTrack);
    this.audio.src = SomaPlayerConfig.somafm_station_url + station;
    this.audio.setAttribute('data-station', station);
    this.audio.removeAttribute('data-paused');
  };

  SomaPlayerBackground.prototype.resetTrackInfoIfNecessary = function(station) {
    if (this.audio.getAttribute('data-station') === station) {
      return;
    }
    console.debug('changed station from',
                  this.audio.getAttribute('data-station'), 'to', station,
                  ', clearing current track info');
    this.titleEl.textContent = '';
    this.artistEl.textContent = '';
  };

  SomaPlayerBackground.prototype.getCurrentTrackInfo = function(station) {
    SomaPlayerUtil.getCurrentTrackInfo(station, track => {
      this.titleEl.textContent = track.title;
      this.artistEl.textContent = track.artist;
    });
  };

  SomaPlayerBackground.prototype.subscribe = function(station) {
    const emitSubscribe = () => {
      console.debug('subscribing to', station, '...');
      this.socket.emit('subscribe', station, response => {
        if (response.subscribed) {
          console.debug('subscribed to', station);
          this.getCurrentTrackInfo(station);
        } else {
          console.error('failed to subscribe to', station, response);
        }
      });
    };
    if (this.socket.connected) {
      emitSubscribe();
    } else {
      this.socket.on('connect', () => {
        emitSubscribe();
      });
    }
  };

  SomaPlayerBackground.prototype.onTrack = function(track) {
    console.debug('new track:', track);
    this.titleEl.textContent = track.title;
    this.artistEl.textContent = track.artist;
    SomaPlayerUtil.getOptions(opts => {
      this.notifyOfTrack(track, opts);
      this.scrobbleTrack(track, opts);
    });
  };

  SomaPlayerBackground.prototype.notifyOfTrack = function(track, opts) {
    // Default to showing notifications, so if user has not saved preferences,
    // assume they want notifications.
    if (opts.notifications === false) {
      return;
    }
    const notificationOpts = {
      type: 'basic',
      title: track.artist,
      message: track.title,
      iconUrl: 'icon48.png'
    };
    return chrome.notifications.create('', notificationOpts, () => {});
  };

  SomaPlayerBackground.prototype.scrobbleTrack = function(track, opts) {
    if (!(opts.lastfm_session_key && opts.lastfm_user && opts.scrobbling)) {
      return;
    }
    console.debug('scrobbling track for Last.fm user', opts.lastfm_user, track);
    const scrobbleData = {
      artist: (track.artist || '').replace(/"/g, "'"),
      track: (track.title || '').replace(/"/g, "'"),
      user: opts.lastfm_user,
      chosenByUser: 0,
      timestamp: Math.round((new Date()).getTime() / 1000)
    };
    return this.lastfm.track.scrobble(scrobbleData, {
      key: opts.lastfm_session_key
    }, {
      success() {
        let e;
        try {
          const iframeWin = document.querySelector('iframe').contentWindow;
          iframeWin.document.querySelector('form').submit();
          console.debug('scrobbled track', scrobbleData);
        } catch (_error) {
          // Mysterious second submit after scrobble form has already POSTed
          // to Last.fm and the iframe has its origin changed to
          // ws.audioscrobbler.com, which can't be touched by the extension.
          e = _error;
          if (e.name !== 'SecurityError') {
            throw e;
          }
        }
      },
      error(data) {
        console.error('failed to scrobble track', track, 'response:', data);
      }
    });
  };

  SomaPlayerBackground.prototype.pause = function(station) {
    console.debug('pausing station', station);
    this.unsubscribe(station);
    this.audio.pause();
    this.audio.currentTime = 0;
    this.audio.setAttribute('data-paused', 'true');
  };

  SomaPlayerBackground.prototype.clear = function() {
    const info = this.getInfo();
    this.unsubscribe(info.station);
    this.audio.pause();
    this.audio.currentTime = 0;
    this.audio.setAttribute('data-station', '');
    this.audio.removeAttribute('data-paused');
  };

  SomaPlayerBackground.prototype.unsubscribe = function(station) {
    if (!(typeof station === 'string' && station.length > 0)) {
      return;
    }
    console.debug('unsubscribing from', station, '...');
    this.socket.emit('unsubscribe', station, response => {
      if (response.unsubscribed) {
        console.debug('unsubscribed from', station);
      } else {
        console.error('failed to unsubscribe from', station);
      }
    });
    console.debug('removing track listener');
    this.socket.removeListener('track', this.onTrack);
  };

  SomaPlayerBackground.prototype.getInfo = function() {
    let station = '';
    if (this.audio) {
      station = this.audio.getAttribute('data-station') || '';
    }
    return {
      station,
      artist: this.artistEl.textContent,
      title: this.titleEl.textContent,
      paused: this.audio.hasAttribute('data-paused') || station === ''
    };
  };

  SomaPlayerBackground.prototype.setStations = function(stations) {
    console.debug('set stations', stations);
    SomaPlayerUtil.getOptions(opts => {
      opts.stations = stations;
      SomaPlayerUtil.setOptions(opts);
    });
  };

  SomaPlayerBackground.prototype.getStations = function(callback) {
    SomaPlayerUtil.getOptions(opts => {
      return callback(opts.stations);
    });
  };

  SomaPlayerBackground.prototype.fetchStations = function(callback) {
    const url = `${SomaPlayerConfig.somafm_api_url}channels.json`;
    console.debug(`fetching channels list from ${url}`);
    return window.fetch(url).then(response => {
      return response.json();
    }).then(this.onStationsFetched.bind(this, callback)).
       catch(this.onStationFetchError.bind(this, callback));
  };

  SomaPlayerBackground.prototype.onStationsFetched = function(callback, data) {
    console.debug('fetched stations list', data);
    const stations = data.channels;
    const simpleStations = [];
    for (let i = 0; i < stations.length; i++) {
      simpleStations.push({
        id: stations[i].id,
        title: stations[i].title
      });
    }
    this.setStations(simpleStations);
    callback(simpleStations);
  };

  SomaPlayerBackground.prototype.onStationFetchError = function(callback, xhr, status, error) {
    console.error('failed to fetch stations list', error);
    callback(null, true);
  };

  return SomaPlayerBackground;
})();

document.addEventListener('DOMContentLoaded', () => {
  somaPlayerBG = new SomaPlayerBackground();
});

SomaPlayerUtil.receiveMessage((request, sender, sendResponse) => {
  let info;
  console.debug('received message in background:', request);
  if (request.action === 'play') {
    somaPlayerBG.play(request.station);
    sendResponse();
    return true;
  }
  if (request.action === 'pause') {
    somaPlayerBG.pause(request.station);
    sendResponse();
    return true;
  }
  if (request.action === 'info') {
    info = somaPlayerBG.getInfo();
    console.debug('info:', info);
    sendResponse(info);
    return true;
  }
  if (request.action === 'clear') {
    somaPlayerBG.clear();
    sendResponse();
    return true;
  }
  if (request.action === 'fetch_stations') {
    somaPlayerBG.fetchStations((stations, error) => {
      return sendResponse(stations, error);
    });
    return true;
  }
  if (request.action === 'get_stations') {
    somaPlayerBG.getStations(stations => {
      console.debug('got saved list of stations:', stations);
      return sendResponse(stations);
    });
    return true;
  }
});

{'fraction_non_alphanumeric': 0.14468322634663688, 'fraction_numerical': 0.0012280212112754675, 'mean_word_length': 3.1520278099652375, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5181106', 'n_tokens_mistral': 5383, 'n_tokens_neox': 5315, 'n_words': 932}