<commit_before>/*
 * Copyright 2015 Stephen Herbein <stephen272@gmail.com>
 * Copyright 2015 Steffen Coenen <steffen@steffen-coenen.de>
 * Copyright 2016 Jiří Janoušek <janousek.jiri@gmail.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

"use strict";

(function(Nuvola)
{

// Translations
var _ = Nuvola.Translate.gettext;
var C_ = Nuvola.Translate.pgettext;

var COUNTRY_VARIANT = "app.country_variant";
var HOME_PAGE = "https://music.amazon.{1}/";
var COUNTRY_VARIANTS = [
    ["de", C_("Amazon variant", "Germany")],
    ["fr", C_("Amazon variant", "France")],
    ["co.uk", C_("Amazon variant", "United Kingdom")],
    ["com", C_("Amazon variant", "United States")]
];

// Create media player component
var player = Nuvola.$object(Nuvola.MediaPlayer);

// Handy aliases
var PlaybackState = Nuvola.PlaybackState;
var PlayerAction = Nuvola.PlayerAction;

// Create new WebApp prototype
var WebApp = Nuvola.$WebApp();

// Have we read the volume yet?
var volumeKnown = false;

// Countdown number of "update" cycles before closing volume controls.
var autoCloseVolume = 0;

// Initialization routines
WebApp._onInitWebWorker = function(emitter)
{
    Nuvola.WebApp._onInitWebWorker.call(this, emitter);
    Nuvola.config.setDefault(COUNTRY_VARIANT, "");
    this.state = PlaybackState.UNKNOWN;

    var state = document.readyState;
    if (state === "interactive" || state === "complete")
        this._onPageReady();
    else
        document.addEventListener("DOMContentLoaded", this._onPageReady.bind(this));
}

// Page is ready for magic
WebApp._onPageReady = function()
{
    // TODO: only set this after the user has logged in
    // Connect handler for signal ActionActivated
    Nuvola.actions.connect("ActionActivated", this);

    // Start update routine
    this.update();
}

WebApp._onInitializationForm = function(emitter, values, entries)
{
    if (!Nuvola.config.hasKey(COUNTRY_VARIANT))
        this.appendPreferences(values, entries);
}

WebApp.appendPreferences = function(values, entries)
{
    values[COUNTRY_VARIANT] = Nuvola.config.get(COUNTRY_VARIANT);
    entries.push(["header", _("Amazon Cloud Player")]);
    entries.push(["label", _("Preferred national variant")]);
    for (var i = 0; i < COUNTRY_VARIANTS.length; i++)
        entries.push(["option", COUNTRY_VARIANT, COUNTRY_VARIANTS[i][0], COUNTRY_VARIANTS[i][1]]);
}

WebApp._onInitAppRunner = function(emitter)
{
    Nuvola.core.connect("InitializationForm", this);
    Nuvola.core.connect("PreferencesForm", this);
}

WebApp._onPreferencesForm = function(emitter, values, entries)
{
    this.appendPreferences(values, entries);
}

WebApp._onHomePageRequest = function(emitter, result)
{
    result.url = Nuvola.format(HOME_PAGE, Nuvola.config.get(COUNTRY_VARIANT));
}

// Extract data from the web page
WebApp.update = function()
{
    var track = {
        title: null,
        artist: null,
        album: null,
        artLocation: null,
        length: null
    }

    var timeElapsed = null;
    try {
        var elm = document.querySelector("#dragonflyTransport .trackTitle");
        track.title = elm ? elm.textContent : null;
        elm = document.querySelector("#dragonflyTransport .trackArtist");
        track.artist = elm ? elm.textContent : null;
        elm = document.querySelector("#dragonflyTransport .trackAlbumArt img");
        track.artLocation = elm ? elm.src : null;
        elm = document.querySelector('tr.currentlyPlaying td.albumCell');
        track.album = elm ? elm.title : null;
        elm = document.querySelector('.timeRemaining');
        timeElapsed = document.querySelector('.timeElapsed');
        if (elm && timeElapsed) {
            track.length = Nuvola.parseTimeUsec(elm ? elm.textContent : null)
			   + Nuvola.parseTimeUsec(timeElapsed ? timeElapsed.textContent : null);
	    timeElapsed = timeElapsed.textContent;
	}
    } catch (e) {
        //~ console.log("Failed to get track info");
        //~ console.log(e.message);
    }

    player.setTrack(track);

    var playButton = this._getPlayButton();
    var pauseButton = this._getPauseButton();
    if (pauseButton)
        this.state = PlaybackState.PLAYING;
    else if (playButton)
        this.state = PlaybackState.PAUSED;
    else
        this.state = PlaybackState.UNKNOWN;

    if (Nuvola.checkVersion && Nuvola.checkVersion(4, 4, 18)) { // @API 4.5
        player.setTrackPosition(timeElapsed);
        player.setCanSeek(this.state !== PlaybackState.UNKNOWN);

        var elm = document.querySelector(".volumeHandle");
        if (elm) {
	  player.updateVolume(elm.style.bottom.split("%")[0] / 100.0);
	  player.setCanChangeVolume(true);

	  // Close volume control, if we opened it.
	  if (!this.volumeKnown) {
	      elm = document.querySelector(".volume");
	      if (elm)
		Nuvola.clickOnElement(elm);
	      this.volumeKnown = true;
	  }
	} else if (!this.volumeKnown) {
	  // Open volume control to read the setting.
	  elm = document.querySelector(".volume");
	  if (elm)
	    Nuvola.clickOnElement(elm);
	}
        if (autoCloseVolume > 0) {
	  autoCloseVolume--;
	  if (autoCloseVolume == 0) {
	      // Close volume slider now
	      elm = document.querySelector(".volume");
	      if (elm)
		  Nuvola.clickOnElement(elm);
	  }
	}
    }

    player.setPlaybackState(this.state);
    player.setCanPause(!!pauseButton);
    player.setCanPlay(!!playButton);
    player.setCanGoPrev(!!this._getPrevButton);
    player.setCanGoNext(!!this._getNextButton);

    // Schedule the next update
    setTimeout(this.update.bind(this), 500);
}

WebApp._getButton = function(selector)
{
    var elm = document.querySelector(selector);
    return elm ? (elm.classList.contains("disabled") ? null : elm) : null;
}

WebApp._getPlayButton = function()
{
    return this._getButton(".playButton.playerIconPlay");
}

WebApp._getPauseButton = function()
{
    return this._getButton(".playButton.playerIconPause");
}

WebApp._getPrevButton = function()
{
    return this._getButton(".previousButton");
}

WebApp._getNextButton = function()
{
    return this._getButton(".nextButton");
}

WebApp._onActionActivated = function(emitter, name, param)
{
    switch (name) {
    /* Base media player actions */
    case PlayerAction.TOGGLE_PLAY:
        var button = this._getPauseButton();
        if (!button)
            button = this._getPlayButton();
        if (button)
            Nuvola.clickOnElement(button);
        break;
    case PlayerAction.PLAY:
        var button = this._getPlayButton();
        if (button)
            Nuvola.clickOnElement(button);
        break;
    case PlayerAction.PAUSE:
    case PlayerAction.STOP:
        var button = this._getPauseButton();
        if (button)
            Nuvola.clickOnElement(button);
        break;
    case PlayerAction.PREV_SONG:
        var button = this._getPrevButton();
        if (button)
            Nuvola.clickOnElement(button);
        break;
    case PlayerAction.NEXT_SONG:
        var button = this._getNextButton();
        if (button)
            Nuvola.clickOnElement(button);
        break;
    case PlayerAction.SEEK:  // @API 4.5: undefined & ignored in Nuvola < 4.5
        var timeRemaining = document.querySelector('.timeRemaining');
        var timeElapsed = document.querySelector('.timeElapsed');
        var total = Nuvola.parseTimeUsec(timeRemaining ? timeRemaining.textContent : null)
		    + Nuvola.parseTimeUsec(timeElapsed ? timeElapsed.textContent : null);
        if (param > 0 && param <= total)
            Nuvola.clickOnElement(document.querySelector(".sliderTrack"), param/total, 0.5);
        break;
    case PlayerAction.CHANGE_VOLUME:  // @API 4.5: undefined & ignored in Nuvola < 4.5
	var control = document.querySelector(".volumeTrack");
	if (!control) {
	    // Try opening the control, and try again.
	    var elm = document.querySelector(".volume");
	    if (elm)
		Nuvola.clickOnElement(elm);
	    control = document.querySelector(".volumeTrack");

	    // Start the close count down
	    autoCloseVolume = 1;
	}
	if (control)
	{
	    // A regular clickOnElement is not effective here.
	    var y = 1.0 - param;
	    Nuvola.triggerMouseEvent(control, "mouseenter", 0.5, y);
	    Nuvola.triggerMouseEvent(control, "mousedown", 0.5, y);
	    Nuvola.triggerMouseEvent(control, "mouseup", 0.5, y);
	    Nuvola.triggerMouseEvent(control, "mouseout", 0.5, y);

	    // Reset the close count down every time the volume changes
	    if (autoCloseVolume > 0)
	        autoCloseVolume = 5;
	}
        break;
    }
}

WebApp.start();

})(this);  // function(Nuvola)
<commit_msg>Check parseTimeUsec exists.<commit_after>/*
 * Copyright 2015 Stephen Herbein <stephen272@gmail.com>
 * Copyright 2015 Steffen Coenen <steffen@steffen-coenen.de>
 * Copyright 2016 Jiří Janoušek <janousek.jiri@gmail.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

"use strict";

(function(Nuvola)
{

// Translations
var _ = Nuvola.Translate.gettext;
var C_ = Nuvola.Translate.pgettext;

var COUNTRY_VARIANT = "app.country_variant";
var HOME_PAGE = "https://music.amazon.{1}/";
var COUNTRY_VARIANTS = [
    ["de", C_("Amazon variant", "Germany")],
    ["fr", C_("Amazon variant", "France")],
    ["co.uk", C_("Amazon variant", "United Kingdom")],
    ["com", C_("Amazon variant", "United States")]
];

// Create media player component
var player = Nuvola.$object(Nuvola.MediaPlayer);

// Handy aliases
var PlaybackState = Nuvola.PlaybackState;
var PlayerAction = Nuvola.PlayerAction;

// Create new WebApp prototype
var WebApp = Nuvola.$WebApp();

// Have we read the volume yet?
var volumeKnown = false;

// Countdown number of "update" cycles before closing volume controls.
var autoCloseVolume = 0;

// Initialization routines
WebApp._onInitWebWorker = function(emitter)
{
    Nuvola.WebApp._onInitWebWorker.call(this, emitter);
    Nuvola.config.setDefault(COUNTRY_VARIANT, "");
    this.state = PlaybackState.UNKNOWN;

    var state = document.readyState;
    if (state === "interactive" || state === "complete")
        this._onPageReady();
    else
        document.addEventListener("DOMContentLoaded", this._onPageReady.bind(this));
}

// Page is ready for magic
WebApp._onPageReady = function()
{
    // TODO: only set this after the user has logged in
    // Connect handler for signal ActionActivated
    Nuvola.actions.connect("ActionActivated", this);

    // Start update routine
    this.update();
}

WebApp._onInitializationForm = function(emitter, values, entries)
{
    if (!Nuvola.config.hasKey(COUNTRY_VARIANT))
        this.appendPreferences(values, entries);
}

WebApp.appendPreferences = function(values, entries)
{
    values[COUNTRY_VARIANT] = Nuvola.config.get(COUNTRY_VARIANT);
    entries.push(["header", _("Amazon Cloud Player")]);
    entries.push(["label", _("Preferred national variant")]);
    for (var i = 0; i < COUNTRY_VARIANTS.length; i++)
        entries.push(["option", COUNTRY_VARIANT, COUNTRY_VARIANTS[i][0], COUNTRY_VARIANTS[i][1]]);
}

WebApp._onInitAppRunner = function(emitter)
{
    Nuvola.core.connect("InitializationForm", this);
    Nuvola.core.connect("PreferencesForm", this);
}

WebApp._onPreferencesForm = function(emitter, values, entries)
{
    this.appendPreferences(values, entries);
}

WebApp._onHomePageRequest = function(emitter, result)
{
    result.url = Nuvola.format(HOME_PAGE, Nuvola.config.get(COUNTRY_VARIANT));
}

// Extract data from the web page
WebApp.update = function()
{
    var track = {
        title: null,
        artist: null,
        album: null,
        artLocation: null,
        length: null
    }

    var timeElapsed = null;
    try {
        var elm = document.querySelector("#dragonflyTransport .trackTitle");
        track.title = elm ? elm.textContent : null;
        elm = document.querySelector("#dragonflyTransport .trackArtist");
        track.artist = elm ? elm.textContent : null;
        elm = document.querySelector("#dragonflyTransport .trackAlbumArt img");
        track.artLocation = elm ? elm.src : null;
        elm = document.querySelector('tr.currentlyPlaying td.albumCell');
        track.album = elm ? elm.title : null;
        elm = document.querySelector('.timeRemaining');
        timeElapsed = document.querySelector('.timeElapsed');
        if (Nuvola.parseTimeUsec && elm && timeElapsed) {
            track.length = Nuvola.parseTimeUsec(elm ? elm.textContent : null)
			   + Nuvola.parseTimeUsec(timeElapsed ? timeElapsed.textContent : null);
	    timeElapsed = timeElapsed.textContent;
	}
    } catch (e) {
        //~ console.log("Failed to get track info");
        //~ console.log(e.message);
    }

    player.setTrack(track);

    var playButton = this._getPlayButton();
    var pauseButton = this._getPauseButton();
    if (pauseButton)
        this.state = PlaybackState.PLAYING;
    else if (playButton)
        this.state = PlaybackState.PAUSED;
    else
        this.state = PlaybackState.UNKNOWN;

    if (Nuvola.checkVersion && Nuvola.checkVersion(4, 4, 18)) { // @API 4.5
        player.setTrackPosition(timeElapsed);
        player.setCanSeek(this.state !== PlaybackState.UNKNOWN);

        var elm = document.querySelector(".volumeHandle");
        if (elm) {
	  player.updateVolume(elm.style.bottom.split("%")[0] / 100.0);
	  player.setCanChangeVolume(true);

	  // Close volume control, if we opened it.
	  if (!this.volumeKnown) {
	      elm = document.querySelector(".volume");
	      if (elm)
		Nuvola.clickOnElement(elm);
	      this.volumeKnown = true;
	  }
	} else if (!this.volumeKnown) {
	  // Open volume control to read the setting.
	  elm = document.querySelector(".volume");
	  if (elm)
	    Nuvola.clickOnElement(elm);
	}
        if (autoCloseVolume > 0) {
	  autoCloseVolume--;
	  if (autoCloseVolume == 0) {
	      // Close volume slider now
	      elm = document.querySelector(".volume");
	      if (elm)
		  Nuvola.clickOnElement(elm);
	  }
	}
    }

    player.setPlaybackState(this.state);
    player.setCanPause(!!pauseButton);
    player.setCanPlay(!!playButton);
    player.setCanGoPrev(!!this._getPrevButton);
    player.setCanGoNext(!!this._getNextButton);

    // Schedule the next update
    setTimeout(this.update.bind(this), 500);
}

WebApp._getButton = function(selector)
{
    var elm = document.querySelector(selector);
    return elm ? (elm.classList.contains("disabled") ? null : elm) : null;
}

WebApp._getPlayButton = function()
{
    return this._getButton(".playButton.playerIconPlay");
}

WebApp._getPauseButton = function()
{
    return this._getButton(".playButton.playerIconPause");
}

WebApp._getPrevButton = function()
{
    return this._getButton(".previousButton");
}

WebApp._getNextButton = function()
{
    return this._getButton(".nextButton");
}

WebApp._onActionActivated = function(emitter, name, param)
{
    switch (name) {
    /* Base media player actions */
    case PlayerAction.TOGGLE_PLAY:
        var button = this._getPauseButton();
        if (!button)
            button = this._getPlayButton();
        if (button)
            Nuvola.clickOnElement(button);
        break;
    case PlayerAction.PLAY:
        var button = this._getPlayButton();
        if (button)
            Nuvola.clickOnElement(button);
        break;
    case PlayerAction.PAUSE:
    case PlayerAction.STOP:
        var button = this._getPauseButton();
        if (button)
            Nuvola.clickOnElement(button);
        break;
    case PlayerAction.PREV_SONG:
        var button = this._getPrevButton();
        if (button)
            Nuvola.clickOnElement(button);
        break;
    case PlayerAction.NEXT_SONG:
        var button = this._getNextButton();
        if (button)
            Nuvola.clickOnElement(button);
        break;
    case PlayerAction.SEEK:  // @API 4.5: undefined & ignored in Nuvola < 4.5
        var timeRemaining = document.querySelector('.timeRemaining');
        var timeElapsed = document.querySelector('.timeElapsed');
        var total = Nuvola.parseTimeUsec(timeRemaining ? timeRemaining.textContent : null)
		    + Nuvola.parseTimeUsec(timeElapsed ? timeElapsed.textContent : null);
        if (param > 0 && param <= total)
            Nuvola.clickOnElement(document.querySelector(".sliderTrack"), param/total, 0.5);
        break;
    case PlayerAction.CHANGE_VOLUME:  // @API 4.5: undefined & ignored in Nuvola < 4.5
	var control = document.querySelector(".volumeTrack");
	if (!control) {
	    // Try opening the control, and try again.
	    var elm = document.querySelector(".volume");
	    if (elm)
		Nuvola.clickOnElement(elm);
	    control = document.querySelector(".volumeTrack");

	    // Start the close count down
	    autoCloseVolume = 1;
	}
	if (control)
	{
	    // A regular clickOnElement is not effective here.
	    var y = 1.0 - param;
	    Nuvola.triggerMouseEvent(control, "mouseenter", 0.5, y);
	    Nuvola.triggerMouseEvent(control, "mousedown", 0.5, y);
	    Nuvola.triggerMouseEvent(control, "mouseup", 0.5, y);
	    Nuvola.triggerMouseEvent(control, "mouseout", 0.5, y);

	    // Reset the close count down every time the volume changes
	    if (autoCloseVolume > 0)
	        autoCloseVolume = 5;
	}
        break;
    }
}

WebApp.start();

})(this);  // function(Nuvola)
<|endoftext|>


<commit_before>const deleteButton = document.querySelector('.delete-circle');
const searchInput = document.querySelector('.search');
const tabList = document.querySelector('.tab-list');

function initializeTabs() {
  const tabs = browser.tabs.query({ currentWindow: true });
  return () => tabs;
}

// Store all the bad favIcons so we don't get loading jank if a favIcon !exist
function badFavIconCache() {
  const badIconCache = [];
  return () => badIconCache;
}

const badFavIcons = badFavIconCache();
const getAllTabs = initializeTabs();

// Initialize tabs
populateTabList();

// Set a timeout on focus so input is focused on popup
document.addEventListener('DOMContentLoaded', () =>
  setTimeout(() => searchInput.focus(), 150)
);
// Any (non-navigating) keydown should activate the search field,
window.addEventListener('keydown', handleKeyDown);
searchInput.addEventListener('change', updateSearch);
searchInput.addEventListener('keyup', updateSearch);
deleteButton.addEventListener('click', clearInput);

function handleKeyDown(event) {
  switch (event.key) {
    case "Tab":
    case "ArrowDown":
    case "ArrowUp":
      event.preventDefault();
    case "ArrowRight":
    case "ArrowLeft":
      navigateResults(event.key);
      break;
    case "Enter":
      if (document.activeElement.nodeName === "INPUT") {
        tabList.querySelector('.tab-object').focus();
      }
      switchActiveTab(document.activeElement.dataset.id);
      break;
    case "Escape":
      // This only works in chrome, in firefox it always closes the window
      if (searchInput.value.length === 0) {
        window.close();
      } else {
        clearInput();
      }
      break;
    default:
      searchInput.focus();
      break;
  }
}

function navigateResults(direction) {
  if (document.activeElement.nodeName === 'INPUT') {
    document.querySelector('.tab-object').focus();
    return;
  }

  switch (direction) {
    case 'Tab':
    case 'ArrowRight':
    case 'ArrowDown':
      const nextSibling = document.activeElement.nextElementSibling;
      if (nextSibling) {
        nextSibling.focus();
      } else {
        // Return to top if next !exist
        tabList.querySelector('.tab-object').focus();
      }
      break;
    case 'ArrowLeft':
    case 'ArrowUp':
      const prevSibling = document.activeElement.previousElementSibling;
      if (prevSibling) {
        prevSibling.focus()
      } else {
        searchInput.focus();
      };
      break;
  }
}

function updateSearch(event) {
  // If input is empty hide the button
  if (searchInput.value.length === 0) {
    deleteButton.classList.add('hidden');
  } else {
    deleteButton.classList.remove('hidden');
  }

  const query = event.target.value.toLowerCase();
  const tabFilter = (tab) => {
    // Check if tab has the query in title, url
    const queryInTitle = tab.title.toLowerCase().includes(query);
    const queryInUrl = tab.url.toLowerCase().includes(query);
    return (queryInTitle || queryInUrl);
  };
  return getAllTabs()
    .then(tabArray => tabArray.filter(tabFilter))
    .then(injectTabsInList);
}

function injectTabsInList(tabArray) {
  const wasNoResult = tabList.querySelectorAll('.tab-object').length === 0;
  const showNoResult = tabArray.length === 0;
  // Don't update dom if we're going to show no results again
  if (wasNoResult && showNoResult) return;

  clearChildren(tabList);

  // Add nodes to tabList & attach event listeners
  if (showNoResult) {
    tabList.appendChild(noResultNode());
  } else {
    tabArray.map(tabToTag).forEach(tabNode => {
      tabNode.addEventListener('click', switchTabs, true);
      tabNode.addEventListener('focus', scrollIfNeeded);
      tabList.appendChild(tabNode);
    });
  }
}

function tabToTag({ favIconUrl, title, id, url }) {
  const fallBackSvg = '/assets/file.svg';
  const favIconLink = favIconUrl
    && !isChromeLink(favIconUrl)
    && !badFavIcons().includes(favIconUrl)
      ? favIconUrl
      : fallBackSvg;
  // Just check if it's a url for now so we can shorten it
  const tabTitle = isURL(title)
    ? getBasePath(title)
    : title;

  // Create the parent div
  //

  const tabObjectNode = document.createElement('div');
  tabObjectNode.setAttribute('data-id', id);
  tabObjectNode.setAttribute('tabindex', '0');
  tabObjectNode.classList.add('tab-object');

  // favicon
  //
  const favIconNode = document.createElement('img');
  favIconNode.setAttribute('src', favIconLink);
  favIconNode.onerror = function(event) {
    badFavIcons().push(this.src);
    this.src = fallBackSvg;
  };

  // tab-info
  //

  const tabInfoNode = document.createElement('div');
  tabInfoNode.setAttribute('class', 'tab-info');

  //      ${title}
  const titleNode = document.createElement('strong');
  titleNode.classList.add('tab-title');
  titleNode.appendChild(document.createTextNode(tabTitle));

  //
${url}

  const urlNode = document.createElement('p');
  urlNode.appendChild(document.createTextNode(url));

  // Append all block elements
  tabInfoNode.appendChild(titleNode);
  tabInfoNode.appendChild(urlNode);

  tabObjectNode.appendChild(favIconNode);
  tabObjectNode.appendChild(tabInfoNode);

  return tabObjectNode;
}

function scrollIfNeeded(event) {
  // If the height + offset of the selected tab is greater than the height
  // of the tabList, we need to scroll down
  const {
    parentHeight,
    selectedBottom,
    selectedTop,
  } = getNodePositions(event.target.parentNode, event.target);
  const shouldScrollDown = selectedBottom > parentHeight;
  const shouldScrollUp = selectedTop <= 0;
  if (shouldScrollDown) {
    event.target.scrollIntoView(false);
  } else if (shouldScrollUp) {
    event.target.scrollIntoView(true);
  }
}

// Get focused node's position relative to the current scrolled view
function getNodePositions(parentNode, selectedNode) {
  return {
    parentHeight: parentNode.offsetHeight,
    selectedBottom: selectedNode.offsetHeight + (
      selectedNode.offsetTop - parentNode.offsetTop
    ) - parentNode.scrollTop,
    selectedTop: (
      selectedNode.offsetTop - parentNode.offsetTop
    ) - parentNode.scrollTop,
  };
}

function noResultNode() {
  //

  //   <object type="image/svg+xml" data="/assets/alert-circle.svg"></object>
  //   No tabs found
  //

  const noResultNode = document.createElement('div');
  noResultNode.classList.add('no-result');

  const alertCircleNode = document.createElement('object');
  alertCircleNode.setAttribute('type', 'image/svg+xml');
  alertCircleNode.setAttribute('data', '/assets/alert-circle.svg');

  const strongMsgNode = document.createElement('strong');
  strongMsgNode.appendChild(document.createTextNode('No tabs found'));

  noResultNode.appendChild(alertCircleNode);
  noResultNode.appendChild(strongMsgNode);
  return noResultNode;
}

function clearChildren(node) {
  while (node.firstChild) {
    node.removeChild(node.firstChild);
  };
}

function shortenString(str) {
  return str.length >= 120
    ? str.slice(0, 90) + '...'
    : str;
}

function isChromeLink(src) {
  return /chrome:\/\//.test(src);
}

function isURL(src) {
  return /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/.test(src);
}

function getBasePath(src) {
  const url = new URL(src);
  return url.origin;
}

function switchTabs() {
  // this: should be the tab-object node
  const tabId = this.dataset.id;
  switchActiveTab(tabId);
}

function switchActiveTab(id) {
  const numId = parseInt(id);
  browser.tabs.update(numId, { active: true });
  window.close();
}

function clearInput() {
  searchInput.value = '';
  populateTabList();
}

function populateTabList() {
  getAllTabs().then(injectTabsInList);
}

function focusNode(node) {
  node.focus();
}
<commit_msg>Tweak scroll boundary to be exclusive<commit_after>const deleteButton = document.querySelector('.delete-circle');
const searchInput = document.querySelector('.search');
const tabList = document.querySelector('.tab-list');

function initializeTabs() {
  const tabs = browser.tabs.query({ currentWindow: true });
  return () => tabs;
}

// Store all the bad favIcons so we don't get loading jank if a favIcon !exist
function badFavIconCache() {
  const badIconCache = [];
  return () => badIconCache;
}

const badFavIcons = badFavIconCache();
const getAllTabs = initializeTabs();

// Initialize tabs
populateTabList();

// Set a timeout on focus so input is focused on popup
document.addEventListener('DOMContentLoaded', () =>
  setTimeout(() => searchInput.focus(), 150)
);
// Any (non-navigating) keydown should activate the search field,
window.addEventListener('keydown', handleKeyDown);
searchInput.addEventListener('change', updateSearch);
searchInput.addEventListener('keyup', updateSearch);
deleteButton.addEventListener('click', clearInput);

function handleKeyDown(event) {
  switch (event.key) {
    case "Tab":
    case "ArrowDown":
    case "ArrowUp":
      event.preventDefault();
    case "ArrowRight":
    case "ArrowLeft":
      navigateResults(event.key);
      break;
    case "Enter":
      if (document.activeElement.nodeName === "INPUT") {
        tabList.querySelector('.tab-object').focus();
      }
      switchActiveTab(document.activeElement.dataset.id);
      break;
    case "Escape":
      // This only works in chrome, in firefox it always closes the window
      if (searchInput.value.length === 0) {
        window.close();
      } else {
        clearInput();
      }
      break;
    default:
      searchInput.focus();
      break;
  }
}

function navigateResults(direction) {
  if (document.activeElement.nodeName === 'INPUT') {
    document.querySelector('.tab-object').focus();
    return;
  }

  switch (direction) {
    case 'Tab':
    case 'ArrowRight':
    case 'ArrowDown':
      const nextSibling = document.activeElement.nextElementSibling;
      if (nextSibling) {
        nextSibling.focus();
      } else {
        // Return to top if next !exist
        tabList.querySelector('.tab-object').focus();
      }
      break;
    case 'ArrowLeft':
    case 'ArrowUp':
      const prevSibling = document.activeElement.previousElementSibling;
      if (prevSibling) {
        prevSibling.focus()
      } else {
        searchInput.focus();
      };
      break;
  }
}

function updateSearch(event) {
  // If input is empty hide the button
  if (searchInput.value.length === 0) {
    deleteButton.classList.add('hidden');
  } else {
    deleteButton.classList.remove('hidden');
  }

  const query = event.target.value.toLowerCase();
  const tabFilter = (tab) => {
    // Check if tab has the query in title, url
    const queryInTitle = tab.title.toLowerCase().includes(query);
    const queryInUrl = tab.url.toLowerCase().includes(query);
    return (queryInTitle || queryInUrl);
  };
  return getAllTabs()
    .then(tabArray => tabArray.filter(tabFilter))
    .then(injectTabsInList);
}

function injectTabsInList(tabArray) {
  const wasNoResult = tabList.querySelectorAll('.tab-object').length === 0;
  const showNoResult = tabArray.length === 0;
  // Don't update dom if we're going to show no results again
  if (wasNoResult && showNoResult) return;

  clearChildren(tabList);

  // Add nodes to tabList & attach event listeners
  if (showNoResult) {
    tabList.appendChild(noResultNode());
  } else {
    tabArray.map(tabToTag).forEach(tabNode => {
      tabNode.addEventListener('click', switchTabs, true);
      tabNode.addEventListener('focus', scrollIfNeeded);
      tabList.appendChild(tabNode);
    });
  }
}

function tabToTag({ favIconUrl, title, id, url }) {
  const fallBackSvg = '/assets/file.svg';
  const favIconLink = favIconUrl
    && !isChromeLink(favIconUrl)
    && !badFavIcons().includes(favIconUrl)
      ? favIconUrl
      : fallBackSvg;
  // Just check if it's a url for now so we can shorten it
  const tabTitle = isURL(title)
    ? getBasePath(title)
    : title;

  // Create the parent div
  //

  const tabObjectNode = document.createElement('div');
  tabObjectNode.setAttribute('data-id', id);
  tabObjectNode.setAttribute('tabindex', '0');
  tabObjectNode.classList.add('tab-object');

  // favicon
  //
  const favIconNode = document.createElement('img');
  favIconNode.setAttribute('src', favIconLink);
  favIconNode.onerror = function(event) {
    badFavIcons().push(this.src);
    this.src = fallBackSvg;
  };

  // tab-info
  //

  const tabInfoNode = document.createElement('div');
  tabInfoNode.setAttribute('class', 'tab-info');

  //      ${title}
  const titleNode = document.createElement('strong');
  titleNode.classList.add('tab-title');
  titleNode.appendChild(document.createTextNode(tabTitle));

  //
${url}

  const urlNode = document.createElement('p');
  urlNode.appendChild(document.createTextNode(url));

  // Append all block elements
  tabInfoNode.appendChild(titleNode);
  tabInfoNode.appendChild(urlNode);

  tabObjectNode.appendChild(favIconNode);
  tabObjectNode.appendChild(tabInfoNode);

  return tabObjectNode;
}

function scrollIfNeeded(event) {
  // If the selected tab isn't completely visible in the scrolled view,
  // force scroll
  const {
    parentHeight,
    selectedBottom,
    selectedTop,
  } = getNodePositions(event.target.parentNode, event.target);
  const shouldScrollDown = selectedBottom > parentHeight;
  const shouldScrollUp = selectedTop < 0;

  if (!shouldScrollDown && !shouldScrollUp) return;

  if (shouldScrollDown) {
    event.target.scrollIntoView(false);
  } else if (shouldScrollUp) {
    event.target.scrollIntoView(true);
  }
}

// Get focused node's position relative to the current scrolled view
function getNodePositions(parentNode, selectedNode) {
  return {
    parentHeight: parentNode.offsetHeight,
    selectedBottom: selectedNode.offsetHeight + (
      selectedNode.offsetTop - parentNode.offsetTop
    ) - parentNode.scrollTop,
    selectedTop: (
      selectedNode.offsetTop - parentNode.offsetTop
    ) - parentNode.scrollTop,
  };
}

function noResultNode() {
  //

  //   <object type="image/svg+xml" data="/assets/alert-circle.svg"></object>
  //   No tabs found
  //

  const noResultNode = document.createElement('div');
  noResultNode.classList.add('no-result');

  const alertCircleNode = document.createElement('object');
  alertCircleNode.setAttribute('type', 'image/svg+xml');
  alertCircleNode.setAttribute('data', '/assets/alert-circle.svg');

  const strongMsgNode = document.createElement('strong');
  strongMsgNode.appendChild(document.createTextNode('No tabs found'));

  noResultNode.appendChild(alertCircleNode);
  noResultNode.appendChild(strongMsgNode);
  return noResultNode;
}

function clearChildren(node) {
  while (node.firstChild) {
    node.removeChild(node.firstChild);
  };
}

function shortenString(str) {
  return str.length >= 120
    ? str.slice(0, 90) + '...'
    : str;
}

function isChromeLink(src) {
  return /chrome:\/\//.test(src);
}

function isURL(src) {
  return /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/.test(src);
}

function getBasePath(src) {
  const url = new URL(src);
  return url.origin;
}

function switchTabs() {
  // this: should be the tab-object node
  const tabId = this.dataset.id;
  switchActiveTab(tabId);
}

function switchActiveTab(id) {
  const numId = parseInt(id);
  browser.tabs.update(numId, { active: true });
  window.close();
}

function clearInput() {
  searchInput.value = '';
  populateTabList();
}

function populateTabList() {
  getAllTabs().then(injectTabsInList);
}

function focusNode(node) {
  node.focus();
}
<|endoftext|>


<commit_before>'use strict';

function handleArrayInput (operator, a, b) {
    if (Array.isArray(b)) {
        for (var i = 0; i < b.length; i++) {
            if (b === a.length) return undefined;
            a[i] = operations[operator](a[i], b[i]);
        }
    }
    else {
        for (var j = 0; j < a.length; j++) {
            a[j] = operations[operator](a[j], b);
        }
    }

    return a;
}

// Check for number/string/array
function isTypeValid(input) {
    return !isNaN(input) || typeof(input) === 'string' || Array.isArray(input);
}

// Convenient state operators.
var operations = {
    '+': function(a, b) {
        return a + b;
    },
    '-': function(a, b) {
        return a - b;
    },
    '*': function(a, b) {
        return a * b;
    },
    '/': function(a, b) {
        return a / b;
    },
    'pow': function(a, b) {
        return Math.pow(a, b);
    },
    'sqrt': function(a) {
        return Math.sqrt(a);
    },
    'abs': function(a) {
        return Math.abs(a);
    },
    'sin': function(a) {
        return Math.sin(a);
    },
    'cos': function(a) {
        return Math.cos(a);
    },
    'tan': function(a) {
        return Math.tan(a);
    },
    'ceil': function(a) {
        return Math.floor(a);
    },
    'floor': function(a) {
        return Math.floor(a);
    },
    'concat': function(a, b) {
        return a.concat(b);
    },
    'substring': function(a, b) {
        return a.substring(b[0], b[1]);
    },
    'toLower': function(a) {
        return a.toLowerCase();
    },
    'toUpper': function(a) {
        return a.toUpperCase();
    },
    'flip': function(a) {
        return !a;
    },
    'toInt': function(a) {
        return a ? 1 : 0;
    }
};

 module.exports = {
    operate: function(operator, currentValue, newValue) {
        if (newValue) {
            if (!(isTypeValid(currentValue) && isTypeValid(newValue))) {
                console.warn('<currentValue :', currentValue, '> or <newValue: ', newValue, '> is not a valid input type');
                throw new Error('Invalid input');
            }
            else if (!Array.isArray(currentValue) && Array.isArray(newValue)) {
                if (typeof(currentValue) !== 'string') { // special case for 'substring' operation
                    console.warn('An array can not be used as input to operate on a non-array');
                    throw new Error('Invalid input');
                }
            }
        }

        if (Array.isArray(currentValue)) {
            return handleArrayInput(operator, currentValue, newValue);
        }
        else {
            return operations[operator](currentValue, newValue);
        }
     },
     addOperation: function(name, func) {
        operations[name] = func;
     }
 };

<commit_msg>Fix bug: Math.floor -> Math.ceil<commit_after>'use strict';

function handleArrayInput (operator, a, b) {
    if (Array.isArray(b)) {
        for (var i = 0; i < b.length; i++) {
            if (b === a.length) return undefined;
            a[i] = operations[operator](a[i], b[i]);
        }
    }
    else {
        for (var j = 0; j < a.length; j++) {
            a[j] = operations[operator](a[j], b);
        }
    }

    return a;
}

// Check for number/string/array
function isTypeValid(input) {
    return !isNaN(input) || typeof(input) === 'string' || Array.isArray(input);
}

// Convenient state operators.
var operations = {
    '+': function(a, b) {
        return a + b;
    },
    '-': function(a, b) {
        return a - b;
    },
    '*': function(a, b) {
        return a * b;
    },
    '/': function(a, b) {
        return a / b;
    },
    'pow': function(a, b) {
        return Math.pow(a, b);
    },
    'sqrt': function(a) {
        return Math.sqrt(a);
    },
    'abs': function(a) {
        return Math.abs(a);
    },
    'sin': function(a) {
        return Math.sin(a);
    },
    'cos': function(a) {
        return Math.cos(a);
    },
    'tan': function(a) {
        return Math.tan(a);
    },
    'ceil': function(a) {
        return Math.ceil(a);
    },
    'floor': function(a) {
        return Math.floor(a);
    },
    'concat': function(a, b) {
        return a.concat(b);
    },
    'substring': function(a, b) {
        return a.substring(b[0], b[1]);
    },
    'toLower': function(a) {
        return a.toLowerCase();
    },
    'toUpper': function(a) {
        return a.toUpperCase();
    },
    'flip': function(a) {
        return !a;
    },
    'toInt': function(a) {
        return a ? 1 : 0;
    }
};

 module.exports = {
    operate: function(operator, currentValue, newValue) {
        if (newValue) {
            if (!(isTypeValid(currentValue) && isTypeValid(newValue))) {
                console.warn('<currentValue :', currentValue, '> or <newValue: ', newValue, '> is not a valid input type');
                throw new Error('Invalid input');
            }
            else if (!Array.isArray(currentValue) && Array.isArray(newValue)) {
                if (typeof(currentValue) !== 'string') { // special case for 'substring' operation
                    console.warn('An array can not be used as input to operate on a non-array');
                    throw new Error('Invalid input');
                }
            }
        }

        if (Array.isArray(currentValue)) {
            return handleArrayInput(operator, currentValue, newValue);
        }
        else {
            return operations[operator](currentValue, newValue);
        }
     },
     addOperation: function(name, func) {
        operations[name] = func;
     }
 };

<|endoftext|>


<commit_before>/*
 * Copyright 2011-2018 Jiří Janoušek <janousek.jiri@gmail.com>
 * Copyright 2014 Martin Pöhlmann <martin.deimos@gmx.de>
 * Copyright 2016 Brian Finley <brian@thefinleys.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

(function (Nuvola) {
  var _ = Nuvola.Translate.gettext
  var C_ = Nuvola.Translate.pgettext

  var State = Nuvola.PlaybackState
  var PlayerAction = Nuvola.PlayerAction
  var player = Nuvola.$object(Nuvola.MediaPlayer)

  var THUMB_NEVER_TOGGLES = 'app.thumb_never_toggles'
  var ACTION_THUMBS_UP = 'thumbs-up'
  var ACTION_THUMBS_DOWN = 'thumbs-down'
  var THUMBS_ACTIONS = [ACTION_THUMBS_UP, ACTION_THUMBS_DOWN]

  var WebApp = Nuvola.$WebApp()

  WebApp._onInitAppRunner = function (emitter) {
    Nuvola.WebApp._onInitAppRunner.call(this, emitter)
    Nuvola.config.setDefaultAsync(THUMB_NEVER_TOGGLES, false).catch(Nuvola.logException)
    Nuvola.core.connect('PreferencesForm', this)
    Nuvola.actions.addAction('playback', 'win', ACTION_THUMBS_UP, C_('Action', 'Thumbs up'), null, null, null, true)
    Nuvola.actions.addAction('playback', 'win', ACTION_THUMBS_DOWN, C_('Action', 'Thumbs down'), null, null, null, true)
  }

  WebApp._onInitWebWorker = function (emitter) {
    Nuvola.WebApp._onInitWebWorker.call(this, emitter)

    Nuvola.actions.connect('ActionActivated', this)
    this.thumbsUp = undefined
    this.thumbsDown = undefined
    this.state = State.UNKNOWN
    player.addExtraActions(THUMBS_ACTIONS)
    var state = document.readyState
    if (state === 'interactive' || state === 'complete') {
      this._onPageReady()
    } else {
      document.addEventListener('DOMContentLoaded', this._onPageReady.bind(this))
    }
  }

  WebApp._onPreferencesForm = function (emitter, values, entries) {
    this.appendPreferences(values, entries)
  }

  WebApp.appendPreferences = function (values, entries) {
    values[THUMB_NEVER_TOGGLES] = Nuvola.config.get(THUMB_NEVER_TOGGLES)
    entries.push(['header', '\nGoogle Play Music'])
    entries.push(['bool', THUMB_NEVER_TOGGLES, _('Treat thumbs up or down selection as a one-way switch,\nnot a toggle.')])
  }

  /**
 * Signal handler for @link{Core::UriChanged}
 */
  WebApp._onUriChanged = function (emitter, uri) {
    /*
     * Users that use the queue page a lot might end up with it as a start-up page. However, this page is always empty
     * and not useful at all, so load Listen now page instead. https://bugs.launchpad.net/nuvola-player/+bug/1306678
     */
    if (uri === 'https://play.google.com/music/listen#/ap/queue') {
      uri = this.meta.home_url
    }

    Nuvola.WebApp._onUriChanged.call(this, emitter, uri)
  }

  WebApp._onLastPageRequest = function (emitter, result) {
    Nuvola.WebApp._onLastPageRequest.call(this, emitter, result)
    if (result.url && result.url.indexOf('file://') === 0) {
      result.url = null
    }
  }

  WebApp._onPageReady = function () {
    if (window.location.protocol === 'file:') {
      return
    }
    player.connect('RatingSet', this)
    Nuvola.config.connect('ConfigChanged', this)
    Nuvola.config.getAsync(THUMB_NEVER_TOGGLES).then((thumbNeverToggles) => {
      this.thumbNeverToggles = thumbNeverToggles
      this.update()
    }).catch(Nuvola.logException)
  }

  WebApp.update = function () {
    var elm
    /* Fix for Google Play Music shows an incomplete queue. https://github.com/tiliado/nuvolaplayer/issues/106 */
    if (!this._queueOverlayFixed && (elm = document.getElementById('queue-overlay')) && elm.hasAttribute('layout')) {
      elm.removeAttribute('layout')
      this._queueOverlayFixed = true
    }

    this.state = State.UNKNOWN
    var prevSong, nextSong
    try {
      var pp = this._getPlayPauseButton()
      if (pp.disabled === true) { this.state = State.UNKNOWN } else if (pp.className.indexOf('playing') !== -1) {
        this.state = State.PLAYING
      } else {
        this.state = State.PAUSED
      }

      if (this.state !== State.UNKNOWN) {
        prevSong = this._getGoPrevButton().disabled === false
        nextSong = this._getGoNextButton().disabled === false
      } else {
        prevSong = nextSong = false
      }
    } catch (e) {
      this.state = State.UNKNOWN
      this.scheduleUpdate()
      return
    }

    player.setPlaybackState(this.state)
    player.setCanPause(this.state === State.PLAYING)
    player.setCanPlay(this.state === State.PAUSED || (this.state === State.UNKNOWN && this._luckyMix()))
    player.setCanGoPrev(prevSong)
    player.setCanGoNext(nextSong)
    player.setCanRate(this.state !== State.UNKNOWN)

    var track = {}
    try {
      track.artLocation = document.querySelector('#playerSongInfo #playerBarArt').src.replace('=s90-', '=s500-')
    } catch (e) {
      track.artLocation = null
    }

    try {
      elm = document.querySelector('#playerSongInfo #currently-playing-title')
      track.title = elm.innerText || elm.textContent
    } catch (e) {
      track.title = null
    }

    try {
      elm = document.getElementById('player-artist').firstChild
      track.artist = elm.innerText || elm.textContent
    } catch (e) {
      track.artist = null
    }

    try {
      elm = document.querySelector('#playerSongInfo .player-album')
      track.album = elm.innerText || elm.textContent
    } catch (e) {
      track.album = null
    }

    elm = document.getElementById('time_container_duration')
    track.length = elm ? elm.innerText || null : null

    var thumbsUp = this._getThumbsUpButton()
    var thumbsDown = this._getThumbsDownButton()
    if (this._isThumbSelected(thumbsUp)) { track.rating = 1.0 } else if (this._isThumbSelected(thumbsDown)) {
      track.rating = 0.20
    } else {
      track.rating = 0.0
    }
    player.setTrack(track)

    var shuffle = this._getShuffleButton()
    var repeat = this._getRepeatStatus(this._getRepeatButton())

    // Extract enabled flag and state from a web page
    var actionsEnabled = {}
    var actionsStates = {}
    actionsStates[ACTION_THUMBS_UP] = this._isThumbSelected(thumbsUp)
    actionsStates[ACTION_THUMBS_DOWN] = this._isThumbSelected(thumbsDown)
    actionsStates[PlayerAction.SHUFFLE] = shuffle && shuffle.classList.contains('active')
    actionsStates[PlayerAction.REPEAT] = repeat || 0
    actionsEnabled[PlayerAction.SHUFFLE] = !!shuffle
    actionsEnabled[PlayerAction.REPEAT] = repeat !== null
    actionsEnabled[ACTION_THUMBS_UP] = !!thumbsUp && !(this.thumbNeverToggles && this._isThumbSelected(thumbsUp))
    actionsEnabled[ACTION_THUMBS_DOWN] = !!thumbsDown && !(this.thumbNeverToggles && this._isThumbSelected(thumbsDown))

    // Compare with previous values and update if necessary
    Nuvola.actions.updateEnabledFlags(actionsEnabled)
    Nuvola.actions.updateStates(actionsStates)

    elm = document.getElementById('time_container_current')
    player.setTrackPosition(elm ? elm.innerText || null : null)
    player.setCanSeek(this.state !== State.UNKNOWN)
    elm = document.querySelector('#volume #sliderBar')
    player.updateVolume(elm ? elm.getAttribute('value') / 100 : null)
    player.setCanChangeVolume(!!elm)
    this.scheduleUpdate()
  }

  WebApp.scheduleUpdate = function () {
    setTimeout(this.update.bind(this), 500)
  }

  WebApp._getButton = function (id) {
    return document.querySelector('[data-id=' + id + ']')
  }

  WebApp._getPlayPauseButton = function () {
    return this._getButton('play-pause')
  }

  WebApp._getGoPrevButton = function () {
    return this._getButton('rewind')
  }

  WebApp._getGoNextButton = function () {
    return this._getButton('forward')
  }

  WebApp._getShuffleButton = function () {
    return this._getButton('shuffle')
  }

  WebApp._getRepeatButton = function () {
    return this._getButton('repeat')
  }

  WebApp._getThumbsUpButton = function () {
    return document.querySelector(".player-rating-container [data-rating='5']")
  }

  WebApp._getThumbsDownButton = function () {
    return document.querySelector(".player-rating-container [data-rating='1']")
  }

  WebApp._isThumbSelected = function (elm) {
    return (elm && elm.icon) ? elm.icon.indexOf('-outline') === -1 : false
  }

  WebApp._getRepeatStatus = function (button) {
    if (!button) {
      return null
    }
    var classes = button.classList
    if (!classes.contains('active')) {
      return Nuvola.PlayerRepeat.NONE
    }
    var icon = button.firstElementChild.firstElementChild.firstElementChild.firstElementChild.getAttribute('d')
    return (icon === 'M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4zm-4-2V9h-1l-2 1v1h1.5v4H13z'
      ? Nuvola.PlayerRepeat.TRACK : Nuvola.PlayerRepeat.PLAYLIST)
  }

  WebApp._setRepeatStatus = function (button, repeat) {
    if (!button) {
      console.log('Do not have repeat button!')
      return
    }
    while (this._getRepeatStatus(button) !== repeat) {
      Nuvola.clickOnElement(button)
    }
  }

  WebApp._onActionActivated = function (emitter, name, param) {
    var prevSong = this._getGoPrevButton()
    var nextSong = this._getGoNextButton()
    var playPause = this._getPlayPauseButton()
    var luckyMix = this._luckyMix()

    switch (name) {
    /* Base media player actions */
      case PlayerAction.TOGGLE_PLAY:
        if (this.state === State.UNKNOWN && luckyMix) {
          luckyMix.click()
        } else {
          playPause.click()
        }
        break
      case PlayerAction.PLAY:
        if (this.state === State.UNKNOWN && luckyMix) {
          luckyMix.click()
        } else if (this.state !== State.PLAYING) {
          playPause.click()
        }
        break
      case PlayerAction.PAUSE:
      case PlayerAction.STOP:
        if (this.state === State.PLAYING) {
          playPause.click()
        }
        break
      case PlayerAction.PREV_SONG:
        if (prevSong) {
          prevSong.click()
        }
        break
      case PlayerAction.NEXT_SONG:
        if (nextSong) {
          nextSong.click()
        }
        break
      case PlayerAction.SEEK:
        var elm = document.getElementById('time_container_duration')
        var total = Nuvola.parseTimeUsec(elm ? elm.innerText : null)
        if (param > 0 && param <= total) {
          Nuvola.clickOnElement(document.getElementById('progressContainer'), param / total, 0.5)
        }
        break
      case PlayerAction.CHANGE_VOLUME:
        elm = document.querySelector('#volume #sliderBar')
        if (elm) {
          Nuvola.clickOnElement(elm, param)
        }
        break
      case PlayerAction.SHUFFLE:
        Nuvola.clickOnElement(this._getShuffleButton())
        break
      case PlayerAction.REPEAT:
        this._setRepeatStatus(this._getRepeatButton(), param)
        break
      /* Custom actions */
      case ACTION_THUMBS_UP:
        this._getThumbsUpButton().click()
        break
      case ACTION_THUMBS_DOWN:
        this._getThumbsDownButton().click()
        break
    }
  }

  // Handler for rating
  WebApp._onRatingSet = function (emitter, rating) {
    Nuvola.log('Rating set: {1}', rating)
    var thumbsUp = this._getThumbsUpButton()
    var thumbsDown = this._getThumbsDownButton()
    if (rating < 0.01) { // Unset rating
      if (this._isThumbSelected(thumbsUp)) {
        thumbsUp.click()
      } else if (this._isThumbSelected(thumbsDown)) {
        thumbsDown.click()
      }
    } else if (rating <= 0.41) { // 0-2 stars
      if (!this._isThumbSelected(thumbsDown)) {
        thumbsDown.click()
      }
    } else if (rating >= 0.79) { // 4-5 stars
      if (!this._isThumbSelected(thumbsUp)) {
        thumbsUp.click()
      }
    } else { // three stars
      window.alert('Invalid rating: ' + rating + '.' +
        "Have you clicked the three-star button? It isn't supported.")
    }
  }

  WebApp._onConfigChanged = function (emitter, key) {
    if (key === THUMB_NEVER_TOGGLES) {
      Nuvola.config.getAsync(THUMB_NEVER_TOGGLES).then((thumbNeverToggles) => {
        this.thumbNeverToggles = thumbNeverToggles
      }).catch(Nuvola.logException)
    }
  }

  WebApp._luckyMix = function () {
    return window.location.hash === '#/now' ? document.querySelector('div[data-type=imfl]') || false : false
  }

  WebApp.start()
})(this) // function(Nuvola)
<commit_msg>Apply standard --fix<commit_after>/*
 * Copyright 2011-2018 Jiří Janoušek <janousek.jiri@gmail.com>
 * Copyright 2014 Martin Pöhlmann <martin.deimos@gmx.de>
 * Copyright 2016 Brian Finley <brian@thefinleys.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

(function (Nuvola) {
  const _ = Nuvola.Translate.gettext
  const C_ = Nuvola.Translate.pgettext

  const State = Nuvola.PlaybackState
  const PlayerAction = Nuvola.PlayerAction
  const player = Nuvola.$object(Nuvola.MediaPlayer)

  const THUMB_NEVER_TOGGLES = 'app.thumb_never_toggles'
  const ACTION_THUMBS_UP = 'thumbs-up'
  const ACTION_THUMBS_DOWN = 'thumbs-down'
  const THUMBS_ACTIONS = [ACTION_THUMBS_UP, ACTION_THUMBS_DOWN]

  const WebApp = Nuvola.$WebApp()

  WebApp._onInitAppRunner = function (emitter) {
    Nuvola.WebApp._onInitAppRunner.call(this, emitter)
    Nuvola.config.setDefaultAsync(THUMB_NEVER_TOGGLES, false).catch(Nuvola.logException)
    Nuvola.core.connect('PreferencesForm', this)
    Nuvola.actions.addAction('playback', 'win', ACTION_THUMBS_UP, C_('Action', 'Thumbs up'), null, null, null, true)
    Nuvola.actions.addAction('playback', 'win', ACTION_THUMBS_DOWN, C_('Action', 'Thumbs down'), null, null, null, true)
  }

  WebApp._onInitWebWorker = function (emitter) {
    Nuvola.WebApp._onInitWebWorker.call(this, emitter)

    Nuvola.actions.connect('ActionActivated', this)
    this.thumbsUp = undefined
    this.thumbsDown = undefined
    this.state = State.UNKNOWN
    player.addExtraActions(THUMBS_ACTIONS)
    const state = document.readyState
    if (state === 'interactive' || state === 'complete') {
      this._onPageReady()
    } else {
      document.addEventListener('DOMContentLoaded', this._onPageReady.bind(this))
    }
  }

  WebApp._onPreferencesForm = function (emitter, values, entries) {
    this.appendPreferences(values, entries)
  }

  WebApp.appendPreferences = function (values, entries) {
    values[THUMB_NEVER_TOGGLES] = Nuvola.config.get(THUMB_NEVER_TOGGLES)
    entries.push(['header', '\nGoogle Play Music'])
    entries.push(['bool', THUMB_NEVER_TOGGLES, _('Treat thumbs up or down selection as a one-way switch,\nnot a toggle.')])
  }

  /**
 * Signal handler for @link{Core::UriChanged}
 */
  WebApp._onUriChanged = function (emitter, uri) {
    /*
     * Users that use the queue page a lot might end up with it as a start-up page. However, this page is always empty
     * and not useful at all, so load Listen now page instead. https://bugs.launchpad.net/nuvola-player/+bug/1306678
     */
    if (uri === 'https://play.google.com/music/listen#/ap/queue') {
      uri = this.meta.home_url
    }

    Nuvola.WebApp._onUriChanged.call(this, emitter, uri)
  }

  WebApp._onLastPageRequest = function (emitter, result) {
    Nuvola.WebApp._onLastPageRequest.call(this, emitter, result)
    if (result.url && result.url.indexOf('file://') === 0) {
      result.url = null
    }
  }

  WebApp._onPageReady = function () {
    if (window.location.protocol === 'file:') {
      return
    }
    player.connect('RatingSet', this)
    Nuvola.config.connect('ConfigChanged', this)
    Nuvola.config.getAsync(THUMB_NEVER_TOGGLES).then((thumbNeverToggles) => {
      this.thumbNeverToggles = thumbNeverToggles
      this.update()
    }).catch(Nuvola.logException)
  }

  WebApp.update = function () {
    let elm
    /* Fix for Google Play Music shows an incomplete queue. https://github.com/tiliado/nuvolaplayer/issues/106 */
    if (!this._queueOverlayFixed && (elm = document.getElementById('queue-overlay')) && elm.hasAttribute('layout')) {
      elm.removeAttribute('layout')
      this._queueOverlayFixed = true
    }

    this.state = State.UNKNOWN
    let prevSong, nextSong
    try {
      const pp = this._getPlayPauseButton()
      if (pp.disabled === true) { this.state = State.UNKNOWN } else if (pp.className.indexOf('playing') !== -1) {
        this.state = State.PLAYING
      } else {
        this.state = State.PAUSED
      }

      if (this.state !== State.UNKNOWN) {
        prevSong = this._getGoPrevButton().disabled === false
        nextSong = this._getGoNextButton().disabled === false
      } else {
        prevSong = nextSong = false
      }
    } catch (e) {
      this.state = State.UNKNOWN
      this.scheduleUpdate()
      return
    }

    player.setPlaybackState(this.state)
    player.setCanPause(this.state === State.PLAYING)
    player.setCanPlay(this.state === State.PAUSED || (this.state === State.UNKNOWN && this._luckyMix()))
    player.setCanGoPrev(prevSong)
    player.setCanGoNext(nextSong)
    player.setCanRate(this.state !== State.UNKNOWN)

    const track = {}
    try {
      track.artLocation = document.querySelector('#playerSongInfo #playerBarArt').src.replace('=s90-', '=s500-')
    } catch (e) {
      track.artLocation = null
    }

    try {
      elm = document.querySelector('#playerSongInfo #currently-playing-title')
      track.title = elm.innerText || elm.textContent
    } catch (e) {
      track.title = null
    }

    try {
      elm = document.getElementById('player-artist').firstChild
      track.artist = elm.innerText || elm.textContent
    } catch (e) {
      track.artist = null
    }

    try {
      elm = document.querySelector('#playerSongInfo .player-album')
      track.album = elm.innerText || elm.textContent
    } catch (e) {
      track.album = null
    }

    elm = document.getElementById('time_container_duration')
    track.length = elm ? elm.innerText || null : null

    const thumbsUp = this._getThumbsUpButton()
    const thumbsDown = this._getThumbsDownButton()
    if (this._isThumbSelected(thumbsUp)) { track.rating = 1.0 } else if (this._isThumbSelected(thumbsDown)) {
      track.rating = 0.20
    } else {
      track.rating = 0.0
    }
    player.setTrack(track)

    const shuffle = this._getShuffleButton()
    const repeat = this._getRepeatStatus(this._getRepeatButton())

    // Extract enabled flag and state from a web page
    const actionsEnabled = {}
    const actionsStates = {}
    actionsStates[ACTION_THUMBS_UP] = this._isThumbSelected(thumbsUp)
    actionsStates[ACTION_THUMBS_DOWN] = this._isThumbSelected(thumbsDown)
    actionsStates[PlayerAction.SHUFFLE] = shuffle && shuffle.classList.contains('active')
    actionsStates[PlayerAction.REPEAT] = repeat || 0
    actionsEnabled[PlayerAction.SHUFFLE] = !!shuffle
    actionsEnabled[PlayerAction.REPEAT] = repeat !== null
    actionsEnabled[ACTION_THUMBS_UP] = !!thumbsUp && !(this.thumbNeverToggles && this._isThumbSelected(thumbsUp))
    actionsEnabled[ACTION_THUMBS_DOWN] = !!thumbsDown && !(this.thumbNeverToggles && this._isThumbSelected(thumbsDown))

    // Compare with previous values and update if necessary
    Nuvola.actions.updateEnabledFlags(actionsEnabled)
    Nuvola.actions.updateStates(actionsStates)

    elm = document.getElementById('time_container_current')
    player.setTrackPosition(elm ? elm.innerText || null : null)
    player.setCanSeek(this.state !== State.UNKNOWN)
    elm = document.querySelector('#volume #sliderBar')
    player.updateVolume(elm ? elm.getAttribute('value') / 100 : null)
    player.setCanChangeVolume(!!elm)
    this.scheduleUpdate()
  }

  WebApp.scheduleUpdate = function () {
    setTimeout(this.update.bind(this), 500)
  }

  WebApp._getButton = function (id) {
    return document.querySelector('[data-id=' + id + ']')
  }

  WebApp._getPlayPauseButton = function () {
    return this._getButton('play-pause')
  }

  WebApp._getGoPrevButton = function () {
    return this._getButton('rewind')
  }

  WebApp._getGoNextButton = function () {
    return this._getButton('forward')
  }

  WebApp._getShuffleButton = function () {
    return this._getButton('shuffle')
  }

  WebApp._getRepeatButton = function () {
    return this._getButton('repeat')
  }

  WebApp._getThumbsUpButton = function () {
    return document.querySelector(".player-rating-container [data-rating='5']")
  }

  WebApp._getThumbsDownButton = function () {
    return document.querySelector(".player-rating-container [data-rating='1']")
  }

  WebApp._isThumbSelected = function (elm) {
    return (elm && elm.icon) ? elm.icon.indexOf('-outline') === -1 : false
  }

  WebApp._getRepeatStatus = function (button) {
    if (!button) {
      return null
    }
    const classes = button.classList
    if (!classes.contains('active')) {
      return Nuvola.PlayerRepeat.NONE
    }
    const icon = button.firstElementChild.firstElementChild.firstElementChild.firstElementChild.getAttribute('d')
    return (icon === 'M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4zm-4-2V9h-1l-2 1v1h1.5v4H13z'
      ? Nuvola.PlayerRepeat.TRACK
      : Nuvola.PlayerRepeat.PLAYLIST)
  }

  WebApp._setRepeatStatus = function (button, repeat) {
    if (!button) {
      console.log('Do not have repeat button!')
      return
    }
    while (this._getRepeatStatus(button) !== repeat) {
      Nuvola.clickOnElement(button)
    }
  }

  WebApp._onActionActivated = function (emitter, name, param) {
    const prevSong = this._getGoPrevButton()
    const nextSong = this._getGoNextButton()
    const playPause = this._getPlayPauseButton()
    const luckyMix = this._luckyMix()

    switch (name) {
    /* Base media player actions */
      case PlayerAction.TOGGLE_PLAY:
        if (this.state === State.UNKNOWN && luckyMix) {
          luckyMix.click()
        } else {
          playPause.click()
        }
        break
      case PlayerAction.PLAY:
        if (this.state === State.UNKNOWN && luckyMix) {
          luckyMix.click()
        } else if (this.state !== State.PLAYING) {
          playPause.click()
        }
        break
      case PlayerAction.PAUSE:
      case PlayerAction.STOP:
        if (this.state === State.PLAYING) {
          playPause.click()
        }
        break
      case PlayerAction.PREV_SONG:
        if (prevSong) {
          prevSong.click()
        }
        break
      case PlayerAction.NEXT_SONG:
        if (nextSong) {
          nextSong.click()
        }
        break
      case PlayerAction.SEEK: {
        const elm = document.getElementById('time_container_duration')
        const total = Nuvola.parseTimeUsec(elm ? elm.innerText : null)
        if (param > 0 && param <= total) {
          Nuvola.clickOnElement(document.getElementById('progressContainer'), param / total, 0.5)
        }
        break
      }
      case PlayerAction.CHANGE_VOLUME: {
        const elm = document.querySelector('#volume #sliderBar')
        if (elm) {
          Nuvola.clickOnElement(elm, param)
        }
        break
      }
      case PlayerAction.SHUFFLE:
        Nuvola.clickOnElement(this._getShuffleButton())
        break
      case PlayerAction.REPEAT:
        this._setRepeatStatus(this._getRepeatButton(), param)
        break
      /* Custom actions */
      case ACTION_THUMBS_UP:
        this._getThumbsUpButton().click()
        break
      case ACTION_THUMBS_DOWN:
        this._getThumbsDownButton().click()
        break
    }
  }

  // Handler for rating
  WebApp._onRatingSet = function (emitter, rating) {
    Nuvola.log('Rating set: {1}', rating)
    const thumbsUp = this._getThumbsUpButton()
    const thumbsDown = this._getThumbsDownButton()
    if (rating < 0.01) { // Unset rating
      if (this._isThumbSelected(thumbsUp)) {
        thumbsUp.click()
      } else if (this._isThumbSelected(thumbsDown)) {
        thumbsDown.click()
      }
    } else if (rating <= 0.41) { // 0-2 stars
      if (!this._isThumbSelected(thumbsDown)) {
        thumbsDown.click()
      }
    } else if (rating >= 0.79) { // 4-5 stars
      if (!this._isThumbSelected(thumbsUp)) {
        thumbsUp.click()
      }
    } else { // three stars
      window.alert('Invalid rating: ' + rating + '.' +
        "Have you clicked the three-star button? It isn't supported.")
    }
  }

  WebApp._onConfigChanged = function (emitter, key) {
    if (key === THUMB_NEVER_TOGGLES) {
      Nuvola.config.getAsync(THUMB_NEVER_TOGGLES).then((thumbNeverToggles) => {
        this.thumbNeverToggles = thumbNeverToggles
      }).catch(Nuvola.logException)
    }
  }

  WebApp._luckyMix = function () {
    return window.location.hash === '#/now' ? document.querySelector('div[data-type=imfl]') || false : false
  }

  WebApp.start()
})(this) // function(Nuvola)
<|endoftext|>


<commit_before>import d3 from "d3"

var height=200,width=1200;

var viz=(stackdataArr,color2,bun,hatsugen,svg,checkedBun) => {
	console.log("func viz start");

	var stack = d3.layout.stack()
	.x(function(d){return 1;})
	.y(function(d){return d.y;})
	.values(function(d){return d;});
	var stackdata = stack(stackdataArr);
	//var max = d3.max(stackdata[stackdata.length-1], function(d){return d.y + d.y0;});
	var scaleX = d3.scale.linear().domain([0,color2.length]).range([width/(color2.length),width]);
	var scaleY = d3.scale.linear().domain([0,6]).range([0,height]);
	var colors = ["#7777ff","#77ff77","#ff7777"];
	var colorBun=["dimgray","#ff7777","#77ff77","#7777ff"];

	var area = d3.svg.area()
	.x(function(d,i){return (i+1) * width/color2.length})
	.y0(function(d){return height})
	.y1(function(d){return height - scaleY(d.y+d.y0)});
	svg.selectAll("path")
	.data(stackdata.reverse())
	.enter()
	.append("path")
	.attr("d", area)
	.attr("fill",function(d,i){return colors[i]});

	var range = d3.range((width)-(width/(color2.length*2)), color2.length-1, -width/(color2.length));
	svg.selectAll("line.v")
	.data(range).enter().append("line")
	.attr("x1", function(d,i){return d;}).attr("y1", 0)
	.attr("x2", function(d,i){return d;}).attr("y2", height);
	svg.selectAll("line")
	.attr("stroke", function(d,i){return color2[color2.length-1-i]})
	.attr("stroke-width", 3)
	.on('mouseover', function(d,i){
		var e = document.getElementById('msg');
		var k,l;
		e.innerHTML = "";
		for(k=-3;k<=3;k++){
			if(2*(color2.length-i-1)+k<0||2*(color2.length-i-1)+k>=hatsugen.length){
				console.log("%d,continue",2*(color2.length-i-1)+k);
				continue;
			}
			//ｸﾞｳ期分け
			if(k==0){
				e.innerHTML += ""+(1+2*(color2.length-i-1))+" "+hatsugen[2*(color2.length-i-1)]+"
";

			}else if(k%2==0){
				e.innerHTML += ""+(1+k+2*(color2.length-i-1))+" "+hatsugen[k+2*(color2.length-i-1)]+"
";

			}else{
				for(l=0;l<bun[k+2*(color2.length-i-1)].length;l++){
					e.innerHTML += ""+(1+k+2*(color2.length-i-1))+" "+bun[k+2*(color2.length-i-1)][l]+"";
				}
				e.innerHTML += "
";
			}

		}

		/*
		if(i==color2.length-1){
		e.innerHTML = ""+(1+2*(color2.length-i-1))+" "+bun[2*(color2.length-i-1)]+"
"+(2+2*(color2.length-i-1))+" "+bun[1+2*(color2.length-i-1)]+"
"+(3+2*(color2.length-i-1))+" "+bun[2+2*(color2.length-i-1)]+"
"+(4+2*(color2.length-i-1))+" "+bun[3+2*(color2.length-i-1)];
	}else if(i==color2.length-2){
	e.innerHTML = (-1+2*(color2.length-i-1))+" "+bun[-2+2*(color2.length-i-1)]+"
"+(2*(color2.length-i-1))+" "+bun[-1+2*(color2.length-i-1)]+"
"+(1+2*(color2.length-i-1))+" "+bun[2*(color2.length-i-1)]+"
"+(2+2*(color2.length-i-1))+" "+bun[1+2*(color2.length-i-1)]+"
"+(3+2*(color2.length-i-1))+" "+bun[2+2*(color2.length-i-1)]+"
"+(4+2*(color2.length-i-1))+" "+bun[3+2*(color2.length-i-1)];
}else if(i==1){
e.innerHTML = (-2+2*(color2.length-i-1))+" "+bun[-3+2*(color2.length-i-1)]+"
"+(-1+2*(color2.length-i-1))+" "+bun[-2+2*(color2.length-i-1)]+"
"+(2*(color2.length-i-1))+" "+bun[-1+2*(color2.length-i-1)]+"
"+(1+2*(color2.length-i-1))+" "+bun[2*(color2.length-i-1)]+"
"+(2+2*(color2.length-i-1))+" "+bun[1+2*(color2.length-i-1)]+"
"+(3+2*(color2.length-i-1))+" "+bun[2+2*(color2.length-i-1)];
}else if(i==0){
e.innerHTML = (-2+2*(color2.length-i-1))+" "+bun[-3+2*(color2.length-i-1)]+"
"+(-1+2*(color2.length-i-1))+" "+bun[-2+2*(color2.length-i-1)]+"
"+(2*(color2.length-i-1))+" "+bun[-1+2*(color2.length-i-1)]+"
"+(1+2*(color2.length-i-1))+" "+bun[2*(color2.length-i-1)]+"";
}else{
e.innerHTML = (-2+2*(color2.length-i-1))+" "+bun[-3+2*(color2.length-i-1)]+"
"+(-1+2*(color2.length-i-1))+" "+bun[-2+2*(color2.length-i-1)]+"
"+(2*(color2.length-i-1))+" "+bun[-1+2*(color2.length-i-1)]+"
"+(1+2*(color2.length-i-1))+" "+bun[2*(color2.length-i-1)]+"
"+(2+2*(color2.length-i-1))+" "+bun[1+2*(color2.length-i-1)]+"
"+(3+2*(color2.length-i-1))+" "+bun[2+2*(color2.length-i-1)]+"
"+(4+2*(color2.length-i-1))+" "+bun[3+2*(color2.length-i-1)];
}
e.style.color = color2[color2.length-1-i];
*/
})
};

var funcChecked = (chboxlist,checked,taiou,chboxlength) => {
	console.log("funcChecked");
	var c;
	for(c=1;c<chboxlength;c++){
		//console.log(document.getElementById(c));
		const radio = document.getElementById(c).children;
		console.log("radio");
		console.log(radio);
		console.log("radio.length=%d",radio.length);
		for(let i = 2, l = radio.length; i < l; i++){
			if(radio[i].control.checked==true){
				console.log("radio[%d].control.checked==true",i);
				if(radio[i].control.value=="1"){
					console.log("radio[%d].control.value==1",i);
					checked[taiou[c]] =1;
					break;
				}else if(radio[i].control.value=="2"){
					console.log("radio[%d].control.value==2",i);
					checked[taiou[c]] =2;
					break;
				}else if(radio[i].control.value=="3"){
					console.log("radio[%d].control.value==3",i);
					checked[taiou[c]] =3;
					break;
				}
			}else{
				checked[taiou[c]] =0;
			}
		}
	}
};

var funcChecked2 = (chboxlist,chboxlist2,checked2,taiou,chboxlength,chboxlength2) => {
	console.log("funcChecked2");
	var c;
	console.log(chboxlength2);
	for(c=1;c<=chboxlength2;c++){
		console.log("c=%d",c);
		console.log(document.getElementById(c-1+chboxlength));
		const radio = document.getElementById(c-1+chboxlength).children;
		console.log(radio);
		console.log("radio.length=%d",radio.length);
		for(let i = 2, l = radio.length; i < l; i++){
			if(radio[i].control.checked==true){
				if(radio[i].control.value=="6"){
					checked2[taiou[c]] =6;
					break;
				}else if(radio[i].control.value=="7"){
					checked2[taiou[c]] =7;
					break;
				}
			}else{
				checked2[taiou[c]] =4;
			}
		}
	}
};

var setForViz = (keitaisokaiseki,chboxlist,chboxlist2,RGBlist,hatsugen,bun,checked,checked2,taiou,chboxlength,chboxlength2) => {

	d3.select("#svgdiv").select("svg").remove();

	var svg = d3.select("#svgdiv").append("svg")
	.attr("height",height)
	.attr("width",width);

	console.log("setForViz");
	var color2=[];
	var stackdataArr = [];
	console.log("chboxlength=%d",chboxlength);
	if(chboxlength>=1){
		funcChecked(chboxlist,checked,taiou,chboxlength);
	}
	console.log("checked");
	console.log(checked);
	console.log("chboxlist");
	console.log(chboxlist);
	if(chboxlength2>=1){
		funcChecked2(chboxlist,chboxlist2,checked2,taiou,chboxlength,chboxlength2);
	}
	console.log("checked2");
	console.log(checked2);

	var h,i,j,c,m,n;

	//console.log(RGBlist);

	for(n=0;n<RGBlist.length;n++){
		RGBlist[n][0]=0;
		RGBlist[n][1]=0;
		RGBlist[n][2]=0;
	}

	var checkedBun=[];

	for(c=1;c<chboxlist.length;c++){

		n=0;
		for(m=1;m<keitaisokaiseki.length;m=m+2){
			checkedBun[m]=[];//svgでの描画ではm→i
			for(i=0;i<keitaisokaiseki[m].length;i++){
				//for(j=0;j<keitaisokaiseki[m][i].length;j++){
				checkedBun[m][i]=0;
				if(bun[m][i]==chboxlist[c][0]){
					if(checked[c-1]==1){
						RGBlist[n][0]=RGBlist[n][0]+1;
						checkedBun[m][i]=1;
					}else if(checked[c-1]==2){
						RGBlist[n][1]=RGBlist[n][1]+1;
						checkedBun[m][i]=2;
					}else if(checked[c-1]==3){
						RGBlist[n][2]=RGBlist[n][2]+1;
						checkedBun[m][i]=3;
					}
				}
				//}//j=0;j<keitaisokaiseki[m][i].length;j++
			}
			n++
		}
	}
	console.log("checkedBun");
	console.log(checkedBun);
	console.log("RGBlist");
	console.log(RGBlist);//←グラフにすなお

	for(c=0;c<checked2.length;c++){
		if(checked2[c]==3){
			color2[c]="#d4d";
		}else if(checked2[c]==5){
			color2[c]="yellow";
		}else if(checked2[c]==4){
			color2[c]="blue";
		}else if(checked2[c]==6){
			color2[c]="red";
		}else{
			color2[c]="silver";
		}
	}

	var RGBmax=[];
	var RGBmaxmax=1;

	console.log("color2");
	console.log(color2.length);

	for(m=0;m<((keitaisokaiseki.length-1)/2);m++){//2個飛ばしにしたら後が面倒くさい。患者 1→0　3→1 長さ9なら番号は8まで
		RGBmax[m]=1;
		for(h=0;h<3;h++){
			RGBmax[m]=RGBmax[m]+RGBlist[m][h];
		}
		if(RGBmaxmax<RGBmax[m]){
			RGBmaxmax=RGBmax[m];
		}
	}

	for(h=0;h<3;h++){
		stackdataArr[h] = [];
		for(m=0;m<((keitaisokaiseki.length-1)/2);m++){//2個飛ばしにしたら後が面倒くさい。患者 1→0　3→1 長さ9なら番号は8まで
			stackdataArr[h][m]=new Object();
			stackdataArr[h][m]= {x:m+1,y:(5*(RGBlist[m][h])/RGBmaxmax)};
			//console.log(stackdataArr[h][m]);
		}
	}

	console.log("stackdataArr");
	console.log(stackdataArr);
	console.log("color2");
	console.log(color2);
	console.log("checkedBun");
	console.log(checkedBun);
	viz(stackdataArr,color2,bun,hatsugen,svg,checkedBun);
	console.log("chboxlist2");
	console.log(chboxlist2);

	return{
		chboxlist:chboxlist,
		chboxlist2:chboxlist2,
		RGBlist:RGBlist,
		checked:checked,
		checked2:checked2,
		chboxlength:chboxlength,
		chboxlength2:chboxlength2
	}
}

export {setForViz};
<commit_msg>A sentence and the correspondence of checkbox are OK. But the correspondence of checkbox and the vertical line is funny<commit_after>import d3 from "d3"

var height=200,width=1200;

var viz=(stackdataArr,color2,bun,hatsugen,svg,checkedBun) => {
	console.log("func viz start");

	var stack = d3.layout.stack()
	.x(function(d){return 1;})
	.y(function(d){return d.y;})
	.values(function(d){return d;});
	var stackdata = stack(stackdataArr);
	//var max = d3.max(stackdata[stackdata.length-1], function(d){return d.y + d.y0;});
	var scaleX = d3.scale.linear().domain([0,color2.length]).range([width/(color2.length),width]);
	var scaleY = d3.scale.linear().domain([0,6]).range([0,height]);
	var colors = ["#7777ff","#77ff77","#ff7777"];
	var colorBun=["dimgray","#ff7777","#77ff77","#7777ff"];

	var area = d3.svg.area()
	.x(function(d,i){return (i+1) * width/color2.length})
	.y0(function(d){return height})
	.y1(function(d){return height - scaleY(d.y+d.y0)});
	svg.selectAll("path")
	.data(stackdata.reverse())
	.enter()
	.append("path")
	.attr("d", area)
	.attr("fill",function(d,i){return colors[i]});

	var range = d3.range((width)-(width/(color2.length*2)), color2.length-1, -width/(color2.length));
	svg.selectAll("line.v")
	.data(range).enter().append("line")
	.attr("x1", function(d,i){return d;}).attr("y1", 0)
	.attr("x2", function(d,i){return d;}).attr("y2", height);
	svg.selectAll("line")
	.attr("stroke", function(d,i){return color2[color2.length-1-i]})
	.attr("stroke-width", 3)
	.on('mouseover', function(d,i){
		var e = document.getElementById('msg');
		var k,l;
		e.innerHTML = "";
		for(k=-3;k<=3;k++){
			if(2*(color2.length-i-1)+k<0||2*(color2.length-i-1)+k>=hatsugen.length){
				console.log("%d,continue",2*(color2.length-i-1)+k);
				continue;
			}
			//ｸﾞｳ期分け
			if(k==0){
				e.innerHTML += ""+(1+2*(color2.length-i-1))+" "+hatsugen[2*(color2.length-i-1)]+"
";

			}else if(k%2==0){
				e.innerHTML += ""+(1+k+2*(color2.length-i-1))+" "+hatsugen[k+2*(color2.length-i-1)]+"
";

			}else{
				for(l=0;l<bun[k+2*(color2.length-i-1)].length;l++){
					e.innerHTML += ""+(1+k+2*(color2.length-i-1))+" "+bun[k+2*(color2.length-i-1)][l]+"";
				}
				e.innerHTML += "
";
			}
		}
	})
};

var funcChecked = (chboxlist,checked,taiou,chboxlength) => {
	console.log("funcChecked");
	var c;
	for(c=1;c<chboxlength;c++){
		//console.log(document.getElementById(c));
		const radio = document.getElementById(c).children;
		console.log("radio");
		console.log(radio);
		console.log("radio.length=%d",radio.length);
		for(let i = 2, l = radio.length; i < l; i++){
			if(radio[i].control.checked==true){
				console.log("radio[%d].control.checked==true",i);
				if(radio[i].control.value=="1"){
					console.log("radio[%d].control.value==1",i);
					checked[taiou[c]] =1;
					break;
				}else if(radio[i].control.value=="2"){
					console.log("radio[%d].control.value==2",i);
					checked[taiou[c]] =2;
					break;
				}else if(radio[i].control.value=="3"){
					console.log("radio[%d].control.value==3",i);
					checked[taiou[c]] =3;
					break;
				}
			}else{
				checked[taiou[c]] =0;
			}
		}
	}
};

var funcChecked2 = (chboxlist,chboxlist2,checked2,taiou,chboxlength,chboxlength2) => {
	console.log("funcChecked2");
	var c;
	console.log(chboxlength2);
	for(c=chboxlength+1;c<=chboxlength+chboxlength2;c++){
		console.log("c=%d",c);
		console.log(document.getElementById(c-1+chboxlength));
		const radio = document.getElementById(c-1+chboxlength).children;
		console.log(radio);
		console.log("radio.length=%d",radio.length);
		for(let i = radio.length-3, l = radio.length; i < l; i++){
			if(radio[i].control.checked==true){
				if(radio[i].control.value=="6"){
					console.log("radio[%d].control.value==6",i);
					checked2[taiou[c]] =6;
					break;
				}else if(radio[i].control.value=="7"){
					console.log("radio[%d].control.value==7",i);
					checked2[taiou[c]] =7;
					break;
				}
			}else{
				checked2[taiou[c]] =4;
			}
		}
	}
};

var setForViz = (keitaisokaiseki,chboxlist,chboxlist2,RGBlist,hatsugen,bun,checked,checked2,taiou,chboxlength,chboxlength2) => {

	d3.select("#svgdiv").select("svg").remove();

	var svg = d3.select("#svgdiv").append("svg")
	.attr("height",height)
	.attr("width",width);

	console.log("setForViz");
	var color2=[];
	var stackdataArr = [];
	console.log("chboxlength=%d",chboxlength);
	if(chboxlength>=1){
		funcChecked(chboxlist,checked,taiou,chboxlength);
	}
	console.log("checked");
	console.log(checked);
	console.log("chboxlist");
	console.log(chboxlist);
	if(chboxlength2>=1){
		funcChecked2(chboxlist,chboxlist2,checked2,taiou,chboxlength,chboxlength2);
	}
	console.log("checked2");
	console.log(checked2);

	var h,i,j,c,m,n;

	//console.log(RGBlist);

	for(n=0;n<RGBlist.length;n++){
		RGBlist[n][0]=0;
		RGBlist[n][1]=0;
		RGBlist[n][2]=0;
	}

	var checkedBun=[];

	for(c=1;c<chboxlist.length;c++){

		n=0;
		for(m=1;m<keitaisokaiseki.length;m=m+2){
			checkedBun[m]=[];//svgでの描画ではm→i
			for(i=0;i<keitaisokaiseki[m].length;i++){
				//for(j=0;j<keitaisokaiseki[m][i].length;j++){
				checkedBun[m][i]=0;
				if(bun[m][i]==chboxlist[c][0]){
					if(checked[c-1]==1){
						RGBlist[n][0]=RGBlist[n][0]+1;
						checkedBun[m][i]=1;
					}else if(checked[c-1]==2){
						RGBlist[n][1]=RGBlist[n][1]+1;
						checkedBun[m][i]=2;
					}else if(checked[c-1]==3){
						RGBlist[n][2]=RGBlist[n][2]+1;
						checkedBun[m][i]=3;
					}
				}
				//}//j=0;j<keitaisokaiseki[m][i].length;j++
			}
			n++
		}
	}
	console.log("checkedBun");
	console.log(checkedBun);
	console.log("RGBlist");
	console.log(RGBlist);//←グラフにすなお

	for(c=0;c<checked2.length;c++){
		if(checked2[c]==3){
			color2[c]="#d4d";
		}else if(checked2[c]==5){
			color2[c]="yellow";
		}else if(checked2[c]==4){
			color2[c]="blue";
		}else if(checked2[c]==6){
			color2[c]="red";
		}else{
			color2[c]="silver";
		}
	}

	var RGBmax=[];
	var RGBmaxmax=1;

	console.log("color2");
	console.log(color2.length);

	for(m=0;m<((keitaisokaiseki.length-1)/2);m++){//2個飛ばしにしたら後が面倒くさい。患者 1→0　3→1 長さ9なら番号は8まで
		RGBmax[m]=1;
		for(h=0;h<3;h++){
			RGBmax[m]=RGBmax[m]+RGBlist[m][h];
		}
		if(RGBmaxmax<RGBmax[m]){
			RGBmaxmax=RGBmax[m];
		}
	}

	for(h=0;h<3;h++){
		stackdataArr[h] = [];
		for(m=0;m<((keitaisokaiseki.length-1)/2);m++){//2個飛ばしにしたら後が面倒くさい。患者 1→0　3→1 長さ9なら番号は8まで
			stackdataArr[h][m]=new Object();
			stackdataArr[h][m]= {x:m+1,y:(5*(RGBlist[m][h])/RGBmaxmax)};
			//console.log(stackdataArr[h][m]);
		}
	}

	console.log("stackdataArr");
	console.log(stackdataArr);
	console.log("color2");
	console.log(color2);
	console.log("checkedBun");
	console.log(checkedBun);
	viz(stackdataArr,color2,bun,hatsugen,svg,checkedBun);
	console.log("chboxlist2");
	console.log(chboxlist2);

	return{
		chboxlist:chboxlist,
		chboxlist2:chboxlist2,
		RGBlist:RGBlist,
		checked:checked,
		checked2:checked2,
		chboxlength:chboxlength,
		chboxlength2:chboxlength2
	}
}

export {setForViz};
<|endoftext|>


<commit_before>/**
 * @class mouse
 * @description Classe responsável por implementar os métodos e disponibilizar a posicao do ponteiro do mouse
 * @author Bruno Araujo <bruno.araujo@gmail.com>, Bruno Richele <bruno.richele@gmail.com>, Cristina Araujo <crisoara@gmail.com>, Raymundo Junior <raymundojunior@gmail.com>
 * @version 1.0
 */
var mouse = {
	x : null,
	y : null,
	currentX : null,
	currentY : null,
	listener : function(root, exec)
	{
		mouse.findPosition(root);
		document.addEventListener('click', mouse.onClick, false); //Ativa quando a tecla é pressionada
	//	document.addEventListener('mousemove', mouse.onMouseMove, false); //Ativa quando a tecla é pressionada
	},
	onClick : function (e)
	{
		mouse.x = e.pageX - mouse.currentX;
		mouse.y = e.pageY - mouse.currentY;

		console.log("mouse x: " + mouse.x + "mouse y: " + mouse.y);

		if(mouse.x > 377 && mouse.x < 501 && mouse.y > 407 && mouse.y < 447){
			game.init();
			console.log("Iniciar novo jogo");
		}else
		if(mouse.x > 521 && mouse.x < 645 && mouse.y > 407 && mouse.y < 447){
			main.inicio();
			console.log("Menu do jogo");
		}
	},
	/*Não remova será utilizado
	onMouseMove : function (e)
	{
		mouse.x = e.pageX - mouse.currentX;
		mouse.y = e.pageY - mouse.currentY;

		console.log("mouse x: " + mouse.x + "mouse y: " + mouse.y);

		if(mouse.x > 377 && mouse.x < 501 && mouse.y > 407 && mouse.y < 447){

			console.log("Iniciar novo jogo");
		}else
		if(mouse.x > 521 && mouse.x < 645 && mouse.y > 407 && mouse.y < 447){

			console.log("Menu do jogo");
		}
	},	*/
	findPosition : function (obj)
	{
		var curleft = curtop = 0;

		if (obj.offsetParent)
		{
			do
			{
				curleft += obj.offsetLeft;
				curtop += obj.offsetTop;
			}
			while (obj = obj.offsetParent);
		}
		mouse.currentX = curleft;
		mouse.currentY = curtop;
	}
};<commit_msg>removendo codigos desnecessarios<commit_after>/**
 * @class mouse
 * @description Classe responsável por implementar os métodos e disponibilizar a posicao do ponteiro do mouse
 * @author Bruno Araujo <bruno.araujo@gmail.com>, Bruno Richele <bruno.richele@gmail.com>, Cristina Araujo <crisoara@gmail.com>, Raymundo Junior <raymundojunior@gmail.com>
 * @version 1.0
 */
var mouse = {
	x : null,
	y : null,
	currentX : null,
	currentY : null,
	listener : function(root)
	{
		mouse.findPosition(root);
		document.addEventListener('click', mouse.onClick, false); //Ativa quando a tecla é pressionada
	},
	onClick : function (e)
	{
		mouse.x = e.pageX - mouse.currentX;
		mouse.y = e.pageY - mouse.currentY;

		console.log("mouse x: " + mouse.x + "mouse y: " + mouse.y);

		if(mouse.x > 377 && mouse.x < 501 && mouse.y > 407 && mouse.y < 447){
			game.init();
			console.log("Iniciar novo jogo");
		}else
		if(mouse.x > 521 && mouse.x < 645 && mouse.y > 407 && mouse.y < 447){
			main.inicio();
			console.log("Menu do jogo");
		}
	},
	findPosition : function (obj)
	{
		var curleft = curtop = 0;

		if (obj.offsetParent)
		{
			do
			{
				curleft += obj.offsetLeft;
				curtop += obj.offsetTop;
			}
			while (obj = obj.offsetParent);
		}
		mouse.currentX = curleft;
		mouse.currentY = curtop;
	}
};<|endoftext|>