<commit_before>"use strict";

var similarArtists = {
  results: [{ "name": undefined }],
};

var currentPlayerInfo = {
  artist: function () {
    if (similarArtists.results.length > 0) {
      return similarArtists.results[similarArtists.artistPosition].name;
    }
  }
};

var listenHistory = {
  getCurrentPlayerInfo: function () {
    this.previousVideos.push({
      artist: currentPlayerInfo.artist(),
      videoTitle: currentPlayerInfo.videoTitle,
      videoID: currentPlayerInfo.videoID,
      videoDescription: currentPlayerInfo.videoDescription
    });
    var regex = new RegExp("[^0-9a-z]|" + currentPlayerInfo.artist(), "gi");
    this.titlesOnly.push(currentPlayerInfo.videoTitle.replace(regex, ''));
  },
  previousVideos: [],
  titlesOnly: [],
  mixtape: []
};

$("#initialSearchInput").click(function () {
  initialSearchInput.value = "";
  $(".searchBtn").removeClass("disabled");
});

$(".searchBtn").click(function () {
  runSearch();
});

$("#initialSearchInput").keydown(function (key) {
  if (key.keyCode === 13) {
    runSearch();
    this.blur();
  }
});

function runSearch() {
  var searchInput = $("#initialSearchInput").val().toLowerCase();
  if (searchInput !== similarArtists.results[0].name && searchInput !== "") {
    similarArtists.artistPosition = 0;
    similarArtists.results = [{ "name": searchInput, "frequency": 100, "artistImage": undefined }];
    runSpotifySearch(searchInput); // Located in spotifyMethod.js
    $(".subheading").hide();
    $(".nowPlayingSection").show();
    $(".customPlayerUI").css("visibility", "visible");
    $(".relevanceColorScale").show();
  }
}

function displayResults() {
  $(".allSearchResults").html("");
  var forEachCount = 0;
  similarArtists.results.forEach(function (result) {
    forEachCount++;
    var relevance = assignRelevanceClassForColorScale(result.frequency);
    var individualResultBtnHTMLSuffix = relevance + "'>" + result.name + "<button class='deleteArtistResultBtn btn btn-sm btn-default'>✖</button>
";
    var individualResultBtnHTML = "<li class='individualResult " + individualResultBtnHTMLSuffix;

    if (forEachCount <= similarArtists.artistPosition) {
      individualResultBtnHTML = "<li class='individualResult previousResults " + individualResultBtnHTMLSuffix;
    } else if (forEachCount === similarArtists.artistPosition + 1) {
      individualResultBtnHTML = "
 similarArtists.artistPosition + 10) {
      individualResultBtnHTML = "
= 5) {
    return "relevance5of5";
  } else if (frequency === 4) {
    return "relevance4of5";
  } else if (frequency === 3) {
    return "relevance3of5";
  } else if (frequency === 2) {
    return "relevance2of5";
  } else {
    return "relevance1of5";
  }
}

function adjustPreviousAndAdditionalResultsBtnState() {
  if ($(".previousResults").length > 0) {
    $(".showPreviousResultsBtn").html("Show previous results").show();
  } else {
    $(".showPreviousResultsBtn").hide();
  }
  if ($(".additionalResults").length > 0) {
    $(".showAdditionalResultsBtn").html("Show more results").show();
  } else {
    $(".showAdditionalResultsBtn").hide();
  }
}

$(".showPreviousResultsBtn").click(function () {
  if ($(".previousResults").css("display") === "none") {
    $(".previousResults").slideDown("slow", function () {
      $(".showPreviousResultsBtn").html("Hide previous results");
    });
  } else {
    $(".previousResults").slideUp("slow", function () {
      $(".showPreviousResultsBtn").html("Show previous results");
    });
  }
});

$(".showAdditionalResultsBtn").click(function () {
  if ($(".additionalResults").css("display") === "none") {
    $(".additionalResults").slideDown("slow");
    $(".showAdditionalResultsBtn").html("Show less results");
  } else {
    $(".additionalResults").slideUp("slow");
    $(".showAdditionalResultsBtn").html("Show more results");
  }
});

(function skipToThisArtist() {
  $("ol").on("click", "li:not(.mixtapeTitle)", function (event) {
    event.stopPropagation();
    if ($(this).html().indexOf("") !== -1) {
      similarArtists.artistPosition = $(this).index();
      findAndPlayVideo(); // Located in search.js
    }
  });
})();

(function deleteThisArtistFromSimilarArtists() {
  $(".allSearchResults").on("click", "button", function (event) {
    event.stopPropagation(event);
    if ($(this).html() === "✖") {
      var locationOfArtistClicked = $(this).parent().index();
      similarArtists.results.splice(locationOfArtistClicked, 1);
      $(this).parent().fadeOut("slow", function () {
        if (locationOfArtistClicked === similarArtists.artistPosition) {
          similarArtists.artistPosition--;
          queNextVideo();
        } else {
          $(document).ready(() => displayResults()); // QUESTION: Does this make sense?
        }
      });
    }
  });
})();

function queNextVideo() {
  if ($(".lockArtistBtn").hasClass("btn-default")) { // "Lock artist" is disabled
    $(".allSearchResults li").eq(similarArtists.artistPosition).slideUp("fast");
    similarArtists.artistPosition++;
  }
  findAndPlayVideo();
  clearInterval(currentPlayerInfo.playbackTimer);
  currentPlayerInfo.playbackTimer = null;
  $(".currentTime").html("0:00");
  $(".trackLength").html(" / 0:00");
  $(".addToMixtapeBtn").removeClass("disabled").html("Add to Mixtape");
}

(function setupDragAndDropForOrderedLists() {
  $("ol").sortable({
    placeholder: {
      element: function () {
        return $("

");
      },
      update: function () {
        return;
      }
    }
  });
  $("ol").disableSelection();

  var ignoreQuickClicksTimer;

  $("ol").on("click", "li", function () {
    clearTimeout(ignoreQuickClicksTimer);
  });

  $("ol").on("mousedown", "li", function () {
    var elementClicked = $(this);
    ignoreQuickClicksTimer = setTimeout(function () {
      elementClicked.addClass("itemBeingDragged", 100); // jQuery UI feature, animates CSS class style differences
      var originalPosition = elementClicked.index();
      var newPosition;

      $("ol").on("mousemove", "li", function () {
        newPosition = $(".placeholderForDrag").index();
      });

      $("ol").on("mouseup", "li", function () {
        $("ol").off("mousemove");
        $("ol").off("mouseup");
        if (newPosition === undefined) {
          newPosition = originalPosition;
        }
        // similarArtists

        if (elementClicked.hasClass("individualResult")) {
          var artistObjectBeingMoved = similarArtists.results.splice(originalPosition, 1)[0];
          similarArtists.results.splice(newPosition, 0, artistObjectBeingMoved);
          if (originalPosition === similarArtists.artistPosition) {
            similarArtists.artistPosition = newPosition;
          }
        }
        // mixtape

        if (!elementClicked.hasClass("individualResult")) {
          var mixtapeTitleBeingMoved = listenHistory.mixtape.splice(originalPosition, 1)[0];
          listenHistory.mixtape.splice(newPosition, 0, mixtapeTitleBeingMoved);
        }
        elementClicked.removeClass("itemBeingDragged", 75, function () {
          $(document).ready(() => displayResults());
        });
      });
    }, 200);
  });
})();

function displayMixtapeSection() {
  $(".mixtapeViewableList").html("");
  if (listenHistory.mixtape.length > 0) {
    listenHistory.mixtape.forEach(function (track) {
      var mixtapeTrackHTML = "
" + track + "<button class='deleteVideoFromHistoryBtn btn btn-sm btn-info'>✖</button>
"
      $(".mixtapeViewableList").append(mixtapeTrackHTML);
    });
    $(".createMixtapeBtn").show();
    $(".clearMixtapeBtn").show();
    if (!currentPlayerInfo.userLoggedIn) {
      $(".pre-auth").show();
    }
  } else {
    $(".mixtapePlaceholder").show();
    $(".createMixtapeBtn").hide();
    $(".clearMixtapeBtn").hide();
  }
}

(function assignDeleteVideoFromHistoryBtnFunctionality() {
  $(".mixtapeViewableList").on("click", "button", function () {
    listenHistory.mixtape.splice($(this).parent("li").index(), 1);
    $(this).parent("li").fadeOut("fast", function () {
      displayMixtapeSection();
      $(".addToMixtapeBtn").removeClass("disabled").html("Add to Mixtape");
    });
  });
})();

$(".addToMixtapeBtn").click(function () {
  if (!$(this).hasClass("disabled") && currentPlayerInfo.videoTitle !== undefined) {
    $(".addToMixtapeBtn").addClass("disabled");
    listenHistory.mixtape.push(currentPlayerInfo.videoTitle);
    displayMixtapeSection();
  }
});

$(".createMixtapeBtn").click(function () {
  if (currentPlayerInfo.userLoggedIn) {
    autoCreatePlaylist();
    $(this).addClass("disabled");
    $(this).html("Now creating your Mixtape");
  }
});

$(".clearMixtapeBtn").click(function () {
  listenHistory.mixtape = [];
  $(".mixtapeViewableList").slideUp("slow", function () {
    $(".createMixtapeBtn").fadeOut("slow");
    $(".viewMixtape").fadeOut("slow");
    $(".clearMixtapeBtn").fadeOut("slow", function () {
      $(".mixtapeViewableList").html("").show();
      displayMixtapeSection();
    });
  });
});

// Player Controls
$(".pauseVideoBtn").click(function () {
  if ($(".pauseVideoBtn span").hasClass("glyphicon-pause")) {
    $(".pauseVideoBtn span").removeClass("glyphicon-pause").addClass("glyphicon-play");
    currentPlayerInfo.player.pauseVideo();
  } else {
    $(".pauseVideoBtn span").removeClass("glyphicon-play").addClass("glyphicon-pause");
    currentPlayerInfo.player.playVideo();
  }
});

$(".nextVideoBtn").click(function () {
  queNextVideo();
});

$(".lockArtistBtn").click(function () {
  if ($(this).hasClass("btn-default")) {
    $(this).removeClass("btn-default").addClass("btn-warning"); // Enable
  } else {
    $(this).removeClass("btn-warning").addClass("btn-default"); // Disable
  }
});

$(".showPlayerBtn").click(function () {
  if ($(".videoWrapper").css("display") === "none") {
    $(".videoWrapper").slideDown("slow", function () {
      $(".showPlayerBtn").html(" Hide player");
    });
  } else {
    $(".videoWrapper").slideUp("slow", function () {
      $(".showPlayerBtn").html(" Show player");
    });
  }
});

$("button").click(function () {
  $(this).blur();
});
<commit_msg>"Make addToMixtapeBtn class 'disabled' consistent"<commit_after>"use strict";

var similarArtists = {
  results: [{ "name": undefined }],
};

var currentPlayerInfo = {
  artist: function () {
    if (similarArtists.results.length > 0) {
      return similarArtists.results[similarArtists.artistPosition].name;
    }
  }
};

var listenHistory = {
  getCurrentPlayerInfo: function () {
    this.previousVideos.push({
      artist: currentPlayerInfo.artist(),
      videoTitle: currentPlayerInfo.videoTitle,
      videoID: currentPlayerInfo.videoID,
      videoDescription: currentPlayerInfo.videoDescription
    });
    var regex = new RegExp("[^0-9a-z]|" + currentPlayerInfo.artist(), "gi");
    this.titlesOnly.push(currentPlayerInfo.videoTitle.replace(regex, ''));
  },
  previousVideos: [],
  titlesOnly: [],
  mixtape: []
};

$("#initialSearchInput").click(function () {
  initialSearchInput.value = "";
  $(".searchBtn").removeClass("disabled");
});

$(".searchBtn").click(function () {
  runSearch();
});

$("#initialSearchInput").keydown(function (key) {
  if (key.keyCode === 13) {
    runSearch();
    this.blur();
  }
});

function runSearch() {
  var searchInput = $("#initialSearchInput").val().toLowerCase();
  if (searchInput !== similarArtists.results[0].name && searchInput !== "") {
    similarArtists.artistPosition = 0;
    similarArtists.results = [{ "name": searchInput, "frequency": 100, "artistImage": undefined }];
    runSpotifySearch(searchInput); // Located in spotifyMethod.js
    $(".subheading").hide();
    $(".nowPlayingSection").show();
    $(".customPlayerUI").css("visibility", "visible");
    $(".relevanceColorScale").show();
  }
}

function displayResults() {
  $(".allSearchResults").html("");
  var forEachCount = 0;
  similarArtists.results.forEach(function (result) {
    forEachCount++;
    var relevance = assignRelevanceClassForColorScale(result.frequency);
    var individualResultBtnHTMLSuffix = relevance + "'>" + result.name + "<button class='deleteArtistResultBtn btn btn-sm btn-default'>✖</button>
";
    var individualResultBtnHTML = "<li class='individualResult " + individualResultBtnHTMLSuffix;

    if (forEachCount <= similarArtists.artistPosition) {
      individualResultBtnHTML = "<li class='individualResult previousResults " + individualResultBtnHTMLSuffix;
    } else if (forEachCount === similarArtists.artistPosition + 1) {
      individualResultBtnHTML = "
 similarArtists.artistPosition + 10) {
      individualResultBtnHTML = "
= 5) {
    return "relevance5of5";
  } else if (frequency === 4) {
    return "relevance4of5";
  } else if (frequency === 3) {
    return "relevance3of5";
  } else if (frequency === 2) {
    return "relevance2of5";
  } else {
    return "relevance1of5";
  }
}

function adjustPreviousAndAdditionalResultsBtnState() {
  if ($(".previousResults").length > 0) {
    $(".showPreviousResultsBtn").html("Show previous results").show();
  } else {
    $(".showPreviousResultsBtn").hide();
  }
  if ($(".additionalResults").length > 0) {
    $(".showAdditionalResultsBtn").html("Show more results").show();
  } else {
    $(".showAdditionalResultsBtn").hide();
  }
}

$(".showPreviousResultsBtn").click(function () {
  if ($(".previousResults").css("display") === "none") {
    $(".previousResults").slideDown("slow", function () {
      $(".showPreviousResultsBtn").html("Hide previous results");
    });
  } else {
    $(".previousResults").slideUp("slow", function () {
      $(".showPreviousResultsBtn").html("Show previous results");
    });
  }
});

$(".showAdditionalResultsBtn").click(function () {
  if ($(".additionalResults").css("display") === "none") {
    $(".additionalResults").slideDown("slow");
    $(".showAdditionalResultsBtn").html("Show less results");
  } else {
    $(".additionalResults").slideUp("slow");
    $(".showAdditionalResultsBtn").html("Show more results");
  }
});

(function skipToThisArtist() {
  $("ol").on("click", "li:not(.mixtapeTitle)", function (event) {
    event.stopPropagation();
    if ($(this).html().indexOf("") !== -1) {
      similarArtists.artistPosition = $(this).index();
      findAndPlayVideo(); // Located in search.js
    }
  });
})();

(function deleteThisArtistFromSimilarArtists() {
  $(".allSearchResults").on("click", "button", function (event) {
    event.stopPropagation(event);
    if ($(this).html() === "✖") {
      var locationOfArtistClicked = $(this).parent().index();
      similarArtists.results.splice(locationOfArtistClicked, 1);
      $(this).parent().fadeOut("slow", function () {
        if (locationOfArtistClicked === similarArtists.artistPosition) {
          similarArtists.artistPosition--;
          queNextVideo();
        } else {
          $(document).ready(() => displayResults()); // QUESTION: Does this make sense?
        }
      });
    }
  });
})();

function queNextVideo() {
  if ($(".lockArtistBtn").hasClass("btn-default")) { // "Lock artist" is disabled
    $(".allSearchResults li").eq(similarArtists.artistPosition).slideUp("fast");
    similarArtists.artistPosition++;
  }
  findAndPlayVideo();
  clearInterval(currentPlayerInfo.playbackTimer);
  currentPlayerInfo.playbackTimer = null;
  $(".currentTime").html("0:00");
  $(".trackLength").html(" / 0:00");
  $(".addToMixtapeBtn").removeClass("disabled");
}

(function setupDragAndDropForOrderedLists() {
  $("ol").sortable({
    placeholder: {
      element: function () {
        return $("

");
      },
      update: function () {
        return;
      }
    }
  });
  $("ol").disableSelection();

  var ignoreQuickClicksTimer;

  $("ol").on("click", "li", function () {
    clearTimeout(ignoreQuickClicksTimer);
  });

  $("ol").on("mousedown", "li", function () {
    var elementClicked = $(this);
    ignoreQuickClicksTimer = setTimeout(function () {
      elementClicked.addClass("itemBeingDragged", 100); // jQuery UI feature, animates CSS class style differences
      var originalPosition = elementClicked.index();
      var newPosition;

      $("ol").on("mousemove", "li", function () {
        newPosition = $(".placeholderForDrag").index();
      });

      $("ol").on("mouseup", "li", function () {
        $("ol").off("mousemove");
        $("ol").off("mouseup");
        if (newPosition === undefined) {
          newPosition = originalPosition;
        }
        // similarArtists

        if (elementClicked.hasClass("individualResult")) {
          var artistObjectBeingMoved = similarArtists.results.splice(originalPosition, 1)[0];
          similarArtists.results.splice(newPosition, 0, artistObjectBeingMoved);
          if (originalPosition === similarArtists.artistPosition) {
            similarArtists.artistPosition = newPosition;
          }
        }
        // mixtape

        if (!elementClicked.hasClass("individualResult")) {
          var mixtapeTitleBeingMoved = listenHistory.mixtape.splice(originalPosition, 1)[0];
          listenHistory.mixtape.splice(newPosition, 0, mixtapeTitleBeingMoved);
        }
        elementClicked.removeClass("itemBeingDragged", 75, function () {
          $(document).ready(() => displayResults());
        });
      });
    }, 200);
  });
})();

function displayMixtapeSection() {

  if (listenHistory.mixtape.length > 0) {
    $(".mixtapeViewableList").html("");
    listenHistory.mixtape.forEach(function (track) {
      var mixtapeTrackHTML = "
" + track + "<button class='deleteVideoFromHistoryBtn btn btn-sm btn-info'>✖</button>
"
      $(".mixtapeViewableList").append(mixtapeTrackHTML);
    });
    $(".createMixtapeBtn").show();
    $(".clearMixtapeBtn").show();
    if (!currentPlayerInfo.userLoggedIn) {
      $(".pre-auth").show();
    }
  } else {
    $(".mixtapePlaceholder").show();
    $(".createMixtapeBtn").hide();
    $(".clearMixtapeBtn").hide();
  }
}

(function assignDeleteVideoFromHistoryBtnFunctionality() {
  $(".mixtapeViewableList").on("click", "button", function () {
    listenHistory.mixtape.splice($(this).parent("li").index(), 1);
    $(this).parent("li").fadeOut("fast", function () {
      displayMixtapeSection();
      $(".addToMixtapeBtn").removeClass("disabled");
    });
  });
})();

$(".addToMixtapeBtn").click(function () {
  if (!$(this).hasClass("disabled") && currentPlayerInfo.videoTitle !== undefined) {
    $(".addToMixtapeBtn").addClass("disabled");
    listenHistory.mixtape.push(currentPlayerInfo.videoTitle);
    displayMixtapeSection();
  }
});

$(".createMixtapeBtn").click(function () {
  if (currentPlayerInfo.userLoggedIn) {
    autoCreatePlaylist();
    $(this).addClass("disabled");
    $(this).html("Now creating your Mixtape");
  }
});

$(".clearMixtapeBtn").click(function () {
  listenHistory.mixtape = [];
  $(".mixtapeViewableList").slideUp("slow", function () {
    $(".createMixtapeBtn").fadeOut("slow");
    $(".viewMixtape").fadeOut("slow");
    $(".pre-auth").fadeOut("slow");
    $(".clearMixtapeBtn").fadeOut("slow", function () {
      $(".addToMixtapeBtn").removeClass("disabled");
      $(".mixtapeViewableList").html("").show();
      displayMixtapeSection();
    });
  });
});

// Player Controls
$(".pauseVideoBtn").click(function () {
  if ($(".pauseVideoBtn span").hasClass("glyphicon-pause")) {
    $(".pauseVideoBtn span").removeClass("glyphicon-pause").addClass("glyphicon-play");
    currentPlayerInfo.player.pauseVideo();
  } else {
    $(".pauseVideoBtn span").removeClass("glyphicon-play").addClass("glyphicon-pause");
    currentPlayerInfo.player.playVideo();
  }
});

$(".nextVideoBtn").click(function () {
  queNextVideo();
});

$(".lockArtistBtn").click(function () {
  if ($(this).hasClass("btn-default")) {
    $(this).removeClass("btn-default").addClass("btn-warning"); // Enable
  } else {
    $(this).removeClass("btn-warning").addClass("btn-default"); // Disable
  }
});

$(".showPlayerBtn").click(function () {
  if ($(".videoWrapper").css("display") === "none") {
    $(".videoWrapper").slideDown("slow", function () {
      $(".showPlayerBtn").html(" Hide player");
    });
  } else {
    $(".videoWrapper").slideUp("slow", function () {
      $(".showPlayerBtn").html(" Show player");
    });
  }
});

$("button").click(function () {
  $(this).blur();
});
<|endoftext|>


<commit_before>const url = require('url')
const menuTemplateBuilder = require('./lib/menu')
const { app, BrowserWindow, Menu, shell } = require('electron')

// adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')()

// prevent window being garbage collected
let mainWindow

function onClosed () {
  // dereference the window
  // for multiple windows store them in an array
  mainWindow = null
}

// Open external links on default browser
function onNewWindow (event, newURL, frameName, disposition, options) {
  const urlObject = url.parse(newURL, true)

  // Change width and height to something better
  options.width = 1280
  options.height = 800

  // Messenger has some interesting views for some pages
  if (urlObject.hostname === 'l.messenger.com') {
    const targetURL = url.parse(urlObject.query.u)

    // Allowed targets
    if (targetURL.hostname === 'media.giphy.com' ||       // Giphy
      targetURL.href.match(/google\.\w{1,3}\/maps\//i) || // Google Maps
      targetURL.href.match(/goo.gl\/maps\//i)             // Google Maps
    ) {
      return true
    }
  // Show YouTube video in an embedded window
  } else if (urlObject.hostname === 'www.youtube.com') {
    event.preventDefault()

    // Create a new window for the video
    const win = new BrowserWindow(options)
    win.loadURL(`https://www.youtube.com/embed/${urlObject.query.v}`)
    win.on('closed', onClosed)

    return true
  }

  // Any external link will be sent to the default browser
  event.preventDefault()
  return shell.openExternal(urlObject.href)
}

function createMainWindow () {
  const win = new BrowserWindow({
    width: 1280,
    height: 800,
    webPreferences: {
      nodeIntegration: false
    }
  })

  win.loadURL('https://messenger.com')

  const menu = menuTemplateBuilder(app, shell)
  Menu.setApplicationMenu(Menu.buildFromTemplate(menu))

  win.on('closed', onClosed)
  win.webContents.on('new-window', onNewWindow)

  return win
}

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  if (!mainWindow) {
    mainWindow = createMainWindow()
  }
})

app.on('ready', () => {
  mainWindow = createMainWindow()
})
<commit_msg>Fixed YouTube links not embedding video<commit_after>const url = require('url')
const menuTemplateBuilder = require('./lib/menu')
const { app, BrowserWindow, Menu, shell } = require('electron')

// adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')()

// prevent window being garbage collected
let mainWindow

function onClosed () {
  // dereference the window
  // for multiple windows store them in an array
  mainWindow = null
}

// Open external links on default browser
function onNewWindow (event, newURL, frameName, disposition, options) {
  const urlObject = url.parse(newURL, true)

  // Change width and height to something better
  options.width = 1280
  options.height = 720

  // Messenger has some interesting views for some pages
  if (urlObject.hostname === 'l.messenger.com') {
    const targetURL = url.parse(urlObject.query.u, true)

    // Allowed targets
    if (targetURL.hostname === 'media.giphy.com' ||         // Giphy
        targetURL.href.match(/google\.\w{1,3}\/maps\//i) || // Google Maps
        targetURL.href.match(/goo.gl\/maps\//i)             // Google Maps
    ) {
      return true
    } else if (targetURL.hostname === 'www.youtube.com') {
      event.preventDefault()
      return createYouTubeWindow(targetURL, options)
    }
  // Show YouTube video in an embedded window
  } else if (urlObject.hostname === 'www.youtube.com') {
    event.preventDefault()
    return createYouTubeWindow(urlObject, options)
  }

  // Any external link will be sent to the default browser
  event.preventDefault()
  return shell.openExternal(urlObject.href)
}

function createMainWindow () {
  const win = new BrowserWindow({
    width: 1280,
    height: 800,
    webPreferences: {
      nodeIntegration: false
    }
  })

  win.loadURL('https://messenger.com')

  const menu = menuTemplateBuilder(app, shell)
  Menu.setApplicationMenu(Menu.buildFromTemplate(menu))

  win.on('closed', onClosed)
  win.webContents.on('new-window', onNewWindow)

  return win
}

function createYouTubeWindow (urlObject, options) {
  const win = new BrowserWindow(options)

  win.loadURL(`https://www.youtube.com/embed/${urlObject.query.v}`)
  win.on('closed', onClosed)

  return win
}

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  if (!mainWindow) {
    mainWindow = createMainWindow()
  }
})

app.on('ready', () => {
  mainWindow = createMainWindow()
})
<|endoftext|>


<commit_before>$(".document").ready(function() {

  let timerID;

  let data = {
    sequence: null,
    compSeq: [],
    humanSeq: [],
    ms: [1200, 1000, 800],
    colors: [green, red, yellow, blue],
    turns: 0
  };

  let controller = {

    init: function() {
      view.init();
    },
/*
    play: function(){
      // Add function: check for winner, call victory() and return true
      simon.playSeq(data.compSeq);
      events.activateHumanEvents();
    },
*/

    start: function() {
      data.sequence = simon.makeSequence();
      //data.compSeq.push(data.sequence[0]);
      simon.turn();
    },

    strict: function() {
      $('#strict-display').toggleClass('strict-on');
    }
  };

  // This function should togggle all event listeners on and off
  let events = {

    activateEvents: function() {
      $('#start-button').on('mousedown', controller.start);
      $('#strict-button').on('click', controller.strict);
      $('#green').on('mousedown', press.green);
      $('#red').on('mousedown', press.red);
      $('#yellow').on('mousedown', press.yellow);
      $('#blue').on('mousedown', press.blue);
    },

    deactivateEvents: function() {
      $('#test').off('click', controller.init);
      $('#start-button').off('click', controller.start);
      $('#strict-button').off('click', controller.strict);
      $('#green').off('mousedown', press.green);
      $('#red').off('mousedown', press.red);
      $('#yellow').off('mousedown', press.yellow);
      $('#blue').off('mousedown', press.blue);
    },

    activateHumanEvents: function() {
      $('.button').on('mousedown', simon.humanResponse);
      $('.button').toggleClass('lockout');
    },

    deactivateHumanEvents: function() {
      $('.button').off('mousedown', simon.humanResponse);
      $('.button').toggleClass('lockout');
    },

    togglePower: function() {
      this.checked ? events.activateEvents() : events.deactivateEvents();
    }

  };

  let press = {
    green: function() {
      let greenSound = document.getElementById('green-sound');
      let newSound = greenSound.cloneNode();
      newSound.play();
      newSound.remove();
      $('#green').addClass('press');
      setTimeout(() => {press.removeOpacity('#green')}, 200);
    },

    red: function() {
      let redSound = document.getElementById('red-sound');
      let newSound = redSound.cloneNode();
      newSound.play();
      newSound.remove();
      $('#red').addClass('press');
      setTimeout(() => {press.removeOpacity('#red')}, 200)
    },

    yellow: function() {
      let yellowSound = document.getElementById('yellow-sound');
      let newSound = yellowSound.cloneNode();
      newSound.play();
      newSound.remove();
      $('#yellow').addClass('press');
      setTimeout(() => {press.removeOpacity('#yellow')}, 200)
    },

    blue: function() {
      let blueSound = document.getElementById('blue-sound');
      let newSound = blueSound.cloneNode();
      newSound.play();
      newSound.remove();
      $('#blue').addClass('press');
      setTimeout(() => {press.removeOpacity('#blue')}, 200)
    },

    removeOpacity: function(x) {
      $(x).removeClass('press').bind(press.green);
    },
  };

  // The 'simon' object contains helper functions
  let simon = {

    getColorCode: function(button){
      return $(button).data().button;
    },

    hasWinner: function(humArr, compArr){
      return humArr.every((value, index) => compArr[index] === humArr[index]);
    },

    humanResponse: function(){
      let x = simon.getColorCode(this);
      data.humanSeq.push(x);
      console.log("humanResponse", "compSeq", data.compSeq);
      console.log("humanResponse", "humanSeq", data.humanSeq);
      console.log("humanResponse", "turns", data.turns);
      // If both arrays are equal...
      if (data.humanSeq.every(function isMatching(el, i) {
        return el === data.compSeq[i];
      })) {
        console.log('match');
        /*
        if (data.humanSeq.length === data.compSeq.length) {
          console.log('nice!');
          data.turns++;
          console.log('turns', data.turns)
          data.humanSeq = [];
          return controller.play();

        }*/
        /*
        if (data.humanSeq.length === data.turns) {
          console.log('nice!');
          data.turns++;
          data.humanSeq = [];
          events.deactivateHumanEvents();
          controller.play();
        }
        } else {
        data.humanSeq = [];
        events.deactivateHumanEvents();
        controller.play();*/
      }

      // Reset humanSeq
    },

    makeSequence: function() {
      //Return an array of 20 numbers between 0 and 3
      let arr = new Array(20);
      for (let i = 0; i < arr.length; i++) {
        let rand = Math.floor(Math.random() * 4);
        arr[i] = rand;
      }
      return arr;
    },

    playback: function(){

    },

    pushButton: function(value, index, array) {
      if (value === 0) press.green();
      else if (value === 1) press.red();
      else if (value === 2) press.yellow()
      else press.blue();
    },

    translateToColor: function(num) {
      return data.colors[num];
    },

    turn: function(arr) { // arr is data.compSeq
      let index = 0;
      data.compSeq.push(data.sequence[data.turns]); // Push new number to compSeq
      // Adjust playback speed, depending on number of turns
      events.deactivateHumanEvents(); // Turn off human input during playback
      timerID = setInterval(function(){
        index++;
        console.log('turns', data.turns, 'index', index);
        // Playback logic
        simon.pushButton(data.compSeq[index]);
        if (index === data.turns + 1) {
          clearInterval(timerID);
        }
      }, 1000);
      events.activateHumanEvents();
      simon.humanResponse();
    }
  }

  let view = {
    init: function() {
      $('#power').change(events.togglePower);
    }
  };

  controller.init();
});
<commit_msg>Remove actions from humanResponse()<commit_after>$(".document").ready(function() {

  let timerID;

  let data = {
    sequence: null,
    compSeq: [],
    humanSeq: [],
    ms: [1200, 1000, 800],
    colors: [green, red, yellow, blue],
    turns: 0
  };

  let controller = {

    init: function() {
      view.init();
    },
/*
    play: function(){
      // Add function: check for winner, call victory() and return true
      simon.playSeq(data.compSeq);
      events.activateHumanEvents();
    },
*/

    start: function() {
      data.sequence = simon.makeSequence();
      //data.compSeq.push(data.sequence[0]);
      simon.turn();
    },

    strict: function() {
      $('#strict-display').toggleClass('strict-on');
    }
  };

  // This function should togggle all event listeners on and off
  let events = {

    activateEvents: function() {
      $('#start-button').on('mousedown', controller.start);
      $('#strict-button').on('click', controller.strict);
      $('#green').on('mousedown', press.green);
      $('#red').on('mousedown', press.red);
      $('#yellow').on('mousedown', press.yellow);
      $('#blue').on('mousedown', press.blue);
    },

    deactivateEvents: function() {
      $('#test').off('click', controller.init);
      $('#start-button').off('click', controller.start);
      $('#strict-button').off('click', controller.strict);
      $('#green').off('mousedown', press.green);
      $('#red').off('mousedown', press.red);
      $('#yellow').off('mousedown', press.yellow);
      $('#blue').off('mousedown', press.blue);
    },

    activateHumanEvents: function() {
      $('.button').on('mousedown', simon.humanResponse);
      $('.button').toggleClass('lockout');
    },

    deactivateHumanEvents: function() {
      $('.button').off('mousedown', simon.humanResponse);
      $('.button').toggleClass('lockout');
    },

    togglePower: function() {
      this.checked ? events.activateEvents() : events.deactivateEvents();
    }

  };

  let press = {
    green: function() {
      let greenSound = document.getElementById('green-sound');
      let newSound = greenSound.cloneNode();
      newSound.play();
      newSound.remove();
      $('#green').addClass('press');
      setTimeout(() => {press.removeOpacity('#green')}, 200);
    },

    red: function() {
      let redSound = document.getElementById('red-sound');
      let newSound = redSound.cloneNode();
      newSound.play();
      newSound.remove();
      $('#red').addClass('press');
      setTimeout(() => {press.removeOpacity('#red')}, 200)
    },

    yellow: function() {
      let yellowSound = document.getElementById('yellow-sound');
      let newSound = yellowSound.cloneNode();
      newSound.play();
      newSound.remove();
      $('#yellow').addClass('press');
      setTimeout(() => {press.removeOpacity('#yellow')}, 200)
    },

    blue: function() {
      let blueSound = document.getElementById('blue-sound');
      let newSound = blueSound.cloneNode();
      newSound.play();
      newSound.remove();
      $('#blue').addClass('press');
      setTimeout(() => {press.removeOpacity('#blue')}, 200)
    },

    removeOpacity: function(x) {
      $(x).removeClass('press').bind(press.green);
    },
  };

  // The 'simon' object contains helper functions
  let simon = {

    getColorCode: function(button){
      return $(button).data().button;
    },

    hasWinner: function(humArr, compArr){
      return humArr.every((value, index) => compArr[index] === humArr[index]);
    },

    humanResponse: function(){
      let x = simon.getColorCode(this);
      data.humanSeq.push(x);
      console.log("humanResponse", "compSeq", data.compSeq);
      console.log("humanResponse", "humanSeq", data.humanSeq);
      console.log("humanResponse", "turns", data.turns);
      // If both arrays are equal...
      if (data.humanSeq.every(function isMatching(el, i) {
        return el === data.compSeq[i];
      })) {
        console.log('match');
      }
    },

    makeSequence: function() {
      //Return an array of 20 numbers between 0 and 3
      let arr = new Array(20);
      for (let i = 0; i < arr.length; i++) {
        let rand = Math.floor(Math.random() * 4);
        arr[i] = rand;
      }
      return arr;
    },

    playback: function(){

    },

    pushButton: function(value, index, array) {
      if (value === 0) press.green();
      else if (value === 1) press.red();
      else if (value === 2) press.yellow()
      else press.blue();
    },

    translateToColor: function(num) {
      return data.colors[num];
    },

    turn: function(arr) { // arr is data.compSeq
      let index = 0;
      data.compSeq.push(data.sequence[data.turns]);
      // Adjust playback speed, depending on number of turns
      events.deactivateHumanEvents();
      timerID = setInterval(function(){
        index++;
        console.log('turns', data.turns, 'index', index);
        // Playback logic
        simon.pushButton(data.compSeq[index]);
        if (index === data.turns + 1) {
          clearInterval(timerID);
        }
      }, 1000);
      events.activateHumanEvents();
      simon.humanResponse();
    }
  }

  let view = {
    init: function() {
      $('#power').change(events.togglePower);
    }
  };

  controller.init();
});
<|endoftext|>


<commit_before>(function() {

var gui = require('nw.gui');
var fs = require('fs');
var path = require('path');
var exec = require('child_process').exec;
var clj_fuzzy = require('clj-fuzzy');
var xdg = require('./xdg');
var win = gui.Window.get();

// Useful DOM objects:
var searchTextbox = document.getElementById("search");
var results = document.getElementById("results");
var footer = document.getElementById("footer");

// Execute arbitrary command, removing the XDG .desktop file specific stuff
// until we figure out what to do with them.
function executeCommand(path) {
	path = path.replace(/%[UufF]/g, "");
	console.log("Executing: " + path);
	exec(path);  // dun dun dun
	win.close();
}

// Reads a desktop file. Returns an object with an "exists" attribute as false
// if not found (because screw the idea of null!)
function fileToEntry(filename) {
	if (filename.indexOf(".desktop") != -1) {
		var contents = fs.readFileSync(filename).toString();
		var executable = xdg.extractKey(contents, "Exec");
		var title = xdg.extractKey(contents, "Name");
		var icon = xdg.extractKey(contents, "Icon");
		return {
			"title": title,
			"icon": xdg.iconToPath(icon),
			"path": executable,
			"exists": true
		}
	}
	return {"exists": false};
}

// Searches applciations directory under the data for .desktop files, and adds
// entry objects for each of them.
function addEntries(entries, dataDir) {
	var dir = path.join(dataDir, 'applications');
	if (fs.existsSync(dir)) {
		var desktopFiles = fs.readdirSync(dir);
		for (var j= 0, len = desktopFiles.length; j < len; j++) {
			entries.push(fileToEntry(path.join(dir, desktopFiles[j])));
		}
	}
}

// Filters out the first 5 entries that match the query.
function getQueryResults(entries, query) {
	if (query === "") {""
		return [];
	}
	var lowerQuery = query.toLowerCase();

	function addScore(entry) {
		var lowerTitle = entry.title.toLowerCase();
		entry.score = clj_fuzzy.metrics.jaccard(lowerTitle, lowerQuery);
		return entry;
	}

	function entryMatches(entry) {
		if (!entry.exists) {
			return false;
		}
		var lowerTitle = entry.title.toLowerCase();
		var contains = lowerQuery.indexOf(lowerTitle) != -1;
		var wildcardRegex = new RegExp(lowerQuery.split("").join(".*"), "i");
		var wildcardMatch = wildcardRegex.test(lowerTitle);
		return contains || wildcardMatch;
	}

	return entries.filter(entryMatches)
	              .map(addScore)
	              .sort(function (a, b) { return a.score - b.score; })
	              .slice(0, 5);
}

// Converts an entry number into a display string for the  appropriate shortcut.
// Currently hardcoded as "Return" for the first entry, and Alt+n for the nth
// entry. Should be configurable in the future.
function getShortcut(entryNumber) {
	if (entryNumber == 0) {
		return "\u23CE";  // return symbol
	} else {
		return "Alt+" + (entryNumber + 1);
	}
}

// Converts an entry object to HTML for display purposes
function entryToHTML(queryResult, entryNumber) {
	var elem = document.createElement("li");
	elem.className = "menu-item";

	var icon = document.createElement("img");
	icon.className = "icon";
	icon.src = queryResult.icon;

	var text = document.createElement("div");
	text.className = "text";

	var title = document.createElement("span");
	title.className = "title";
	title.appendChild(document.createTextNode(queryResult.title));
	text.appendChild(title);

	var path = document.createElement("span");
	path.className = "path";
	path.appendChild(document.createTextNode(queryResult.path));
	text.appendChild(path);

	var shortcut = document.createElement("span");
	shortcut.className = "shortcut";
	shortcut.appendChild(document.createTextNode(getShortcut(entryNumber)));

	elem.appendChild(icon);
	elem.appendChild(text);
	elem.appendChild(shortcut);

	elem.addEventListener('click', function (e) {
		executeCommand(queryResult.path);
	}, true);
	return elem;

}

// Updates the DOM with the entry objects
function updateResults(queryResults) {
	// Change elements
	while (results.hasChildNodes()) {
		results.removeChild(results.lastChild);
	}
	for (var i = 0, len = queryResults.length; i < len; i++) {
		var queryResult = queryResults[i];
		results.appendChild(entryToHTML(queryResult, i));
	}

	// Don't display footer if 1 or fewer elements.
	if (queryResults.length > 1) {
		footer.style.display = "block";
	} else {
		footer.style.display = "none";
	}
}

// Value to use as "negative"
function clearSelect() {
	return -1;
}

// User hit down, so increase the index value (maxing out at # of results)
function selectDown(highlighted) {
	return Math.min(highlighted + 1, results.childNodes.length - 1);
}

// User hit up, so decrease the index value (stopping at 0)
function selectUp(highlighted) {
	return Math.max(highlighted - 1, 0);
}

// Gets the selected application to run. If the user hit enter and something was
// highlighted via arrow keys, use that one. Otherwise, use the firs tone.
// If the user hit Alt+n, return n.
function getNumSelected(highlighted, e) {
	var selected = clearSelect();
	// Enter
	if (e.keyCode == 13) {
		selected = highlighted == clearSelect() ? 1 : highlighted + 1;
	}
	// Alt + number
	var numPressed = parseInt(String.fromCharCode(e.keyCode), 10);
	if (e.altKey && !isNaN(numPressed)) {
		selected = numPressed;
	}
	return selected;
}

// Updates the DOM to reflect the current highlighted entry.
function updateHighlighted(highlighted) {
	var numEntries = results.childNodes.length;
	for (var i = 0; i < numEntries; i++) {
		results.childNodes[i].className = 'menu-item';
		if (i == highlighted) {
			results.childNodes[i].className += ' selected';
		}
	}
}

function main() {
	document.addEventListener('keyup', function (e) {
		if (e.keyCode == 27) {  // escape
			var win = gui.Window.get();
			win.close();
		}
	}, true);

	var highlighted = -1;  // none
	var entries = [];
	xdg.forEachDataDir(function (dataDir) {
		addEntries(entries, dataDir);
	});

	searchTextbox.addEventListener('keyup', function (e) {
		var query = e.target.value;
		var queryResults = getQueryResults(entries, query);
		updateResults(queryResults);

		if (e.keyCode == 40) {  // down arrow
			highlighted = selectDown(highlighted);
		} else if (e.keyCode == 38) {  // up arrow
			highlighted = selectUp(highlighted);
		} else if (e.keyCode != 13) {  // anything but Enter clears the selected
			highlighted = clearSelect();
		}
		updateHighlighted(highlighted);

		var numSelected = getNumSelected(highlighted, e);  // 1-based
		if (numSelected != -1 && queryResults.length >= numSelected) {
			var selectedEntry = queryResults[numSelected - 1];
			var path = selectedEntry.path;
			executeCommand(path);
		}
	}, true);

	win.on('blur', function () {
		win.close();
	}, true);
}

main();

})();<commit_msg>Move listener for escape to separate function.<commit_after>(function() {

var gui = require('nw.gui');
var fs = require('fs');
var path = require('path');
var exec = require('child_process').exec;
var clj_fuzzy = require('clj-fuzzy');
var xdg = require('./xdg');
var win = gui.Window.get();

// Useful DOM objects:
var searchTextbox = document.getElementById("search");
var results = document.getElementById("results");
var footer = document.getElementById("footer");

// Execute arbitrary command, removing the XDG .desktop file specific stuff
// until we figure out what to do with them.
function executeCommand(path) {
	path = path.replace(/%[UufF]/g, "");
	console.log("Executing: " + path);
	exec(path);  // dun dun dun
	win.close();
}

// Reads a desktop file. Returns an object with an "exists" attribute as false
// if not found (because screw the idea of null!)
function fileToEntry(filename) {
	if (filename.indexOf(".desktop") != -1) {
		var contents = fs.readFileSync(filename).toString();
		var executable = xdg.extractKey(contents, "Exec");
		var title = xdg.extractKey(contents, "Name");
		var icon = xdg.extractKey(contents, "Icon");
		return {
			"title": title,
			"icon": xdg.iconToPath(icon),
			"path": executable,
			"exists": true
		}
	}
	return {"exists": false};
}

// Searches applciations directory under the data for .desktop files, and adds
// entry objects for each of them.
function addEntries(entries, dataDir) {
	var dir = path.join(dataDir, 'applications');
	if (fs.existsSync(dir)) {
		var desktopFiles = fs.readdirSync(dir);
		for (var j= 0, len = desktopFiles.length; j < len; j++) {
			entries.push(fileToEntry(path.join(dir, desktopFiles[j])));
		}
	}
}

// Filters out the first 5 entries that match the query.
function getQueryResults(entries, query) {
	if (query === "") {""
		return [];
	}
	var lowerQuery = query.toLowerCase();

	function addScore(entry) {
		var lowerTitle = entry.title.toLowerCase();
		entry.score = clj_fuzzy.metrics.jaccard(lowerTitle, lowerQuery);
		return entry;
	}

	function entryMatches(entry) {
		if (!entry.exists) {
			return false;
		}
		var lowerTitle = entry.title.toLowerCase();
		var contains = lowerQuery.indexOf(lowerTitle) != -1;
		var wildcardRegex = new RegExp(lowerQuery.split("").join(".*"), "i");
		var wildcardMatch = wildcardRegex.test(lowerTitle);
		return contains || wildcardMatch;
	}

	return entries.filter(entryMatches)
	              .map(addScore)
	              .sort(function (a, b) { return a.score - b.score; })
	              .slice(0, 5);
}

// Converts an entry number into a display string for the  appropriate shortcut.
// Currently hardcoded as "Return" for the first entry, and Alt+n for the nth
// entry. Should be configurable in the future.
function getShortcut(entryNumber) {
	if (entryNumber == 0) {
		return "\u23CE";  // return symbol
	} else {
		return "Alt+" + (entryNumber + 1);
	}
}

// Converts an entry object to HTML for display purposes
function entryToHTML(queryResult, entryNumber) {
	var elem = document.createElement("li");
	elem.className = "menu-item";

	var icon = document.createElement("img");
	icon.className = "icon";
	icon.src = queryResult.icon;

	var text = document.createElement("div");
	text.className = "text";

	var title = document.createElement("span");
	title.className = "title";
	title.appendChild(document.createTextNode(queryResult.title));
	text.appendChild(title);

	var path = document.createElement("span");
	path.className = "path";
	path.appendChild(document.createTextNode(queryResult.path));
	text.appendChild(path);

	var shortcut = document.createElement("span");
	shortcut.className = "shortcut";
	shortcut.appendChild(document.createTextNode(getShortcut(entryNumber)));

	elem.appendChild(icon);
	elem.appendChild(text);
	elem.appendChild(shortcut);

	elem.addEventListener('click', function (e) {
		executeCommand(queryResult.path);
	}, true);
	return elem;

}

// Updates the DOM with the entry objects
function updateResults(queryResults) {
	// Change elements
	while (results.hasChildNodes()) {
		results.removeChild(results.lastChild);
	}
	for (var i = 0, len = queryResults.length; i < len; i++) {
		var queryResult = queryResults[i];
		results.appendChild(entryToHTML(queryResult, i));
	}

	// Don't display footer if 1 or fewer elements.
	if (queryResults.length > 1) {
		footer.style.display = "block";
	} else {
		footer.style.display = "none";
	}
}

// Value to use as "negative"
function clearSelect() {
	return -1;
}

// User hit down, so increase the index value (maxing out at # of results)
function selectDown(highlighted) {
	return Math.min(highlighted + 1, results.childNodes.length - 1);
}

// User hit up, so decrease the index value (stopping at 0)
function selectUp(highlighted) {
	return Math.max(highlighted - 1, 0);
}

// Gets the selected application to run. If the user hit enter and something was
// highlighted via arrow keys, use that one. Otherwise, use the firs tone.
// If the user hit Alt+n, return n.
function getNumSelected(highlighted, e) {
	var selected = clearSelect();
	// Enter
	if (e.keyCode == 13) {
		selected = highlighted == clearSelect() ? 1 : highlighted + 1;
	}
	// Alt + number
	var numPressed = parseInt(String.fromCharCode(e.keyCode), 10);
	if (e.altKey && !isNaN(numPressed)) {
		selected = numPressed;
	}
	return selected;
}

// Updates the DOM to reflect the current highlighted entry.
function updateHighlighted(highlighted) {
	var numEntries = results.childNodes.length;
	for (var i = 0; i < numEntries; i++) {
		results.childNodes[i].className = 'menu-item';
		if (i == highlighted) {
			results.childNodes[i].className += ' selected';
		}
	}
}

function listenForEscapeToClose() {
	document.addEventListener('keyup', function (e) {
		if (e.keyCode == 27) {  // escape
			var win = gui.Window.get();
			win.close();
		}
	}, true);
}

function main() {
	listenForEscapeToClose();

	var highlighted = -1;  // none
	var entries = [];
	xdg.forEachDataDir(function (dataDir) {
		addEntries(entries, dataDir);
	});

	searchTextbox.addEventListener('keyup', function (e) {
		var query = e.target.value;
		var queryResults = getQueryResults(entries, query);
		updateResults(queryResults);

		if (e.keyCode == 40) {  // down arrow
			highlighted = selectDown(highlighted);
		} else if (e.keyCode == 38) {  // up arrow
			highlighted = selectUp(highlighted);
		} else if (e.keyCode != 13) {  // anything but Enter clears the selected
			highlighted = clearSelect();
		}
		updateHighlighted(highlighted);

		var numSelected = getNumSelected(highlighted, e);  // 1-based
		if (numSelected != -1 && queryResults.length >= numSelected) {
			var selectedEntry = queryResults[numSelected - 1];
			var path = selectedEntry.path;
			executeCommand(path);
		}
	}, true);

	win.on('blur', function () {
		win.close();
	}, true);
}

main();

})();<|endoftext|>


<commit_before>$(document).ready(function() {
	init();
});

function init() {
	timeInit();
	weatherInit();
	busInit();
	trainInit();
	noteInit();
}

// function nextMinute() {
// 	var now = new Date();
// 	var nextMin = (59 - now.getSeconds()) * 1000 + (1000 - now.getMilliseconds()); // Get time until next minute
// 	return nextMin;
// }

function schedule(fun, mins) {
	var now = new Date();
	var till = (59 - now.getSeconds()) * 1000 + (1000 - now.getMilliseconds()) + (mins - 1) * 60 * 1000; // Get time until next minute

	console.log('hello '+mins);

	var timeout = setTimeout(function() {
		fun();
	}, till);
}
<commit_msg>Removed some old code and test code.<commit_after>$(document).ready(function() {
	init();
});

function init() {
	timeInit();
	weatherInit();
	busInit();
	trainInit();
	noteInit();
}

function schedule(fun, mins) {
	var now = new Date();
	var till = (59 - now.getSeconds()) * 1000 + (1000 - now.getMilliseconds()) + (mins - 1) * 60 * 1000; // Get time until next minute

	var timeout = setTimeout(function() {
		fun();
	}, till);
}
<|endoftext|>


<commit_before>/* wim -- modal text editor */
'use strict';

/* Testing -- write a string to the canvas */
const render=c=>{
    c.clearRect(0,0,c.canvas.width,c.canvas.height);
    const lines=[
        'TODO keyboard input',
        '- inputs accumulate until they can be recognized as \'operations\', then are pushed to history stack',
        '- \'insert mode\' inputs aren\'t saved.'
    ].join('\n');
    c.fillText(lines,20,30);
};

/* flatten : {k1:{v1,v2,vN},k2:{v1,v2,vN}} -> [[v1],[v2],[vN]]
   object -> 2d array with 'column' names */
const flatten=o=>{
    let oo=Object.keys(o);
    if(oo.length<1){return null;}
    let oa=[Object.keys(o[oo[0]])];/* headers */
    for(let i in o){let ia=[]; for(let j in o[i]){ia.push(o[i][j]);} oa.push(ia);}
    return oa;
};

const flat2=o=>{
    let oa=Object.keys(o).reduce((x,y)=>{x.push(o[y]);return x;},[]),
        ok=Object.keys(oa[0]);
    let ii=ok.reduce((x,y,i,a)=>{

    },[[ok]]);
    return ii;
};
/* sort_by : 'key' -> DaKeys -> [RawKey]
   DaKeys sorted by 'key' */
const sort_by=(str,dk)=>{
    let d=flatten(dk), key=d[0].indexOf(str);
    return d.slice(1).sort((x,y)=>(x[key]>y[key])?1:(x[key]<y[key])?-1:0);
};

/* combo : 'str' -> DaKeys -> ms -> Bool
   Returns true if DaKeys contains the keystrokes 'str'.
   A positive delta means the keystrokes can't have more than `ms` milliseconds between them. */
const combo=(str,dk,delta=null)=>{
    console.log(delta);
};

/* update : AnyEvent -> Action */
const update=perf_timestamp=>{
    /* what kind of keyboard input did we get? */
    const keystrokes=sort_by('timestamp',DaKeys);
    console.table(keystrokes);
};

/* Events -- keyboard and mouse */
const DaKeys={};/* customized KeyboardEvents */
const key_handler=x=>{
    const kf={/* KeyboardEvents, filtered */
        key:x.key,
        code:x.code,
        timestamp:x.timeStamp|0,
        type:x.type==='keydown'|0,
        mod:['altKey','ctrlKey','metaKey','shiftKey']
            .map(y=>x[y]|0)
            .reduce((a,b,i,arr)=>a+b*Math.pow(2,arr.length-1-i),0)
    };
    DaKeys[x.code]=kf;
    if(kf.type==='keydown'){
        let allow_default={
            'KeyI':[5,10],
            'KeyR':[2,4],
        }[kf.code];
        /* call preventDefault() on everything EXCEPT the chords listed above. */
        allow_default?allow_default.every(m=>kf.mod!==m):true && x.preventDefault();
    }
    requestAnimationFrame(update);
};
window.addEventListener('keydown',key_handler);
window.addEventListener('keyup',key_handler);

/* Window -- load, resize */
const pixel_ratio_fix=(c,cc)=>{
    const dpr=window.devicePixelRatio, h=window.innerHeight, w=window.innerWidth;
    [cc.height,cc.width]=[h,w].map(x=>dpr*x);
    [cc.style.height,cc.style.width]=[h,w].map(x=>x+'px');
    //c.font='16px monospace';/* Must set AFTER modifying canvas! */
    c.font='16px "Source Code Pro for Powerline"';/* Must set AFTER modifying canvas! */
    c.scale(dpr,dpr);
};
const canvas=document.getElementById('c'), ctx=canvas.getContext('2d');
window.addEventListener('resize',()=>pixel_ratio_fix(ctx,canvas));
window.addEventListener('load',()=>{
    pixel_ratio_fix(ctx,canvas);
    render(ctx);// testing
});
<commit_msg>working on combos<commit_after>/* wim -- modal text editor */
'use strict';
/* Testing -- write a string to the canvas */
const render=c=>{
    c.clearRect(0,0,c.canvas.width,c.canvas.height);
    const lines=[
        'TODO keyboard input',
        '- inputs accumulate until they can be recognized as \'operations\', then are pushed to history stack',
        '- \'insert mode\' inputs aren\'t saved.'
    ].join('\n');
    c.fillText(lines,20,30);
};

/* invert : DaKeys -> InvertedKeys
   Object-of-Objects into 2d array where rows are instances and columns are fields */
const invert=o=>{
    let oa=Object.keys(o).reduce((x,y)=>{x.push(o[y]);return x;},[]);
    return Object.keys(oa[0]).map(w=>oa.reduce((x,y)=>{x.push(y[w]);return x;},[w]));
};

/* sort_by : String -> InvertedKeys -> SortedInvertedKeys
   1. invert DaKeys
   2. get column index from string
   3. strip column name and sort by column number
   4. restore column names */
const sort_by=(str,ik)=>{
    let d=zip(ik), key=d[0].indexOf(str);
    return [d[0]].concat(d.slice(1).sort((x,y)=>(x[key]>y[key])?1:(x[key]<y[key])?-1:0));
};

/* combo : String -> SortedInvertedKeys -> MilliSeconds -> Bool
   str is suffix of FlatSortedKeys with less than timeout ms between each of str? */
const combo=(str,sik,timeout=Infinity)=>{
    let suffix=zip(sik.slice(-(str.length)));
    if(suffix[0].join('')===str){
        let idx=sik[0].indexOf('timestamp'),
            ss=suffix[idx];
        TODO
    }
    return false;
};

/* update : AnyEvent -> Action */
const update=perf_timestamp=>{
    /* what kind of keyboard input did we just get? */
    let ks=invert(DaKeys);
    const keystrokes=sort_by('timestamp',ks);
    (combo('fds',keystrokes,500));
};

/* Events -- keyboard and mouse */
const DaKeys={};/* customized KeyboardEvents */
const key_handler=x=>{
    const kf={/* KeyboardEvents, filtered */
        key:x.key,
        code:x.code,
        timestamp:x.timeStamp|0,
        type:x.type==='keydown'|0,
        mod:['altKey','ctrlKey','metaKey','shiftKey']
            .map(y=>x[y]|0)
            .reduce((a,b,i,arr)=>a+b*Math.pow(2,arr.length-1-i),0)
    };
    DaKeys[kf.code]=kf;
    if(kf.type){
        let ok_default={
            'KeyI':[5,10],/* [Cmd Opt i] [Ctrl i] */
            'KeyR':[2,4],/* [Cmd r] [Ctrl r] */
            /* optionally whitelist more keyboard shortcuts here */
        }[kf.code];
        /* call preventDefault() on everything EXCEPT the chords listed above. */
        ok_default?ok_default.every(m=>kf.mod!==m):true && x.preventDefault();
        requestAnimationFrame(update);
    }
};
window.addEventListener('keydown',key_handler);
window.addEventListener('keyup',key_handler);

/* Window -- load, resize */
const pixel_ratio_fix=(c,cc)=>{
    const dpr=window.devicePixelRatio, h=window.innerHeight, w=window.innerWidth;
    [cc.height,cc.width]=[h,w].map(x=>dpr*x);
    [cc.style.height,cc.style.width]=[h,w].map(x=>x+'px');
    //c.font='16px monospace';/* Must set AFTER modifying canvas! */
    c.font='16px "Source Code Pro for Powerline"';/* Must set AFTER modifying canvas! */
    c.scale(dpr,dpr);
};
const canvas=document.getElementById('c'), ctx=canvas.getContext('2d');
window.addEventListener('resize',()=>pixel_ratio_fix(ctx,canvas));
window.addEventListener('load',()=>{
    pixel_ratio_fix(ctx,canvas);
    render(ctx);// testing
});
<|endoftext|>


<commit_before>var c = document.querySelector('canvas'),
    ctx = c.getContext('2d'),
    screenWidth = 0,
    screenHeight = 0,
    particleCount = 0,
		particleRadius = 10,
    averageSpeed = 4,
    friction = 0.9,
    colorChangeSpeed = 0.04,
    backgroundColor = "rgba(0, 0, 0, 0.2)",
    startingColor = {
      r: 100,
      g: 100,
      b: 100
    },
    selectedColor = {
      r: 255,
      g: 255,
      b: 255
    },
    particles = [];

var mouseDown = false,
    mouseX = 0,
    mouseY = 0;

window.requestAnimFrame = (function () {
    return window.requestAnimationFrame       ||
           window.webkitRequestAnimationFrame ||
           window.mozRequestAnimationFrame    ||
           window.oRequestAnimationFrame      ||
           window.msRequestAnimationFrame     ||
           function (callback) {
               window.setTimeout(callback, 1000/60);
           };
})();

// function distance (x1, y1, x2, y2) {
//     return Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));
// }

function Particle (x, y) {
    this.x = x;
    this.y = y;
    this.dx = (Math.random()*averageSpeed)-(averageSpeed/2);
    this.dy = (Math.random()*averageSpeed)-(averageSpeed/2);
    this.color = {
      r: startingColor.r,
      g: startingColor.g,
      b: startingColor.b
    };
    this.targetColor = {
      r: selectedColor.r,
      g: selectedColor.g,
      b: selectedColor.b
    };
    this.radius = particleRadius;
}

Particle.prototype.draw = function () {
  // var grd;
  ctx.save();
  // grd = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.radius);
  // grd.addColorStop(1, 'rgba('+Math.round(this.color.r)+','+Math.round(this.color.g)+','+Math.round(this.color.b)+',1.0)');
  // grd.addColorStop(0, "rgba(0, 0, 0, 0)");
  ctx.beginPath();
  ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
  ctx.fillStyle = 'rgba('+Math.round(this.color.r)+','+Math.round(this.color.g)+','+Math.round(this.color.b)+',1.0)';
  // ctx.fillStyle = grd;
  ctx.fill();
  ctx.restore();
};

(function() {

  var Controls = function () {
    this.reset = function () {
      particles = [];
    }
    this.selectedColor = [255, 255, 255];
    this.friction = 1.0-friction;
    this.radius = particleRadius;
    this.particleCount = 0;
    this.addParticles = function () {
      var n = particles.length * 0.2;
      if (n == 0) {
        particle = new Particle(Math.random()*screenWidth, Math.random()*screenHeight);
        particles.push(particle);
      }
      else {
        for (var i = 0 ; i < n ; i++) {
          particle = new Particle(Math.random()*screenWidth, Math.random()*screenHeight);
          particles.push(particle);
        }
      }
    }
    this.removeParticles = function () {
      var n = particles.length * 0.2;
      for (i = 0 ; i < n ; i++) {
        particles.pop();
      }
    }
  };

  var controls, n, i, particle;

  function init () {
    // GUI
    controls = new Controls();
    var gui = new dat.GUI();
    var qualities = gui.addFolder("Qualities");
    qualities.add(controls, "radius", 0.1, 30).name("Radius").onFinishChange(function () {
      particleRadius = controls.radius;
    });
    qualities.add(controls, "friction", -0.1, 1.0).name("Viscosity").onChange(function () {
      friction = 1.0 - controls.friction;
    });
    qualities.addColor(controls, "selectedColor").name("Color").onFinishChange(function () {
      selectedColor.r = controls.selectedColor[0];
      selectedColor.g = controls.selectedColor[1];
      selectedColor.b = controls.selectedColor[2];
    });

    var quantities = gui.addFolder("Quantities");
    quantities.add(controls, "addParticles").name("Add");
    quantities.add(controls, "removeParticles").name("Remove");
    quantities.add(controls, "reset").name("Reset");

  }

  function resize () {
    screenWidth = c.width = window.innerWidth;
    screenHeight = c.height = window.innerHeight;
    init();
  }

  function draw () {
    ctx.save();
    ctx.fillStyle = backgroundColor;
		ctx.fillRect(0, 0, screenWidth, screenHeight);

    update();

    for (var i = 0, len = particles.length ; i < len ; i++) {
      particles[i].draw();
    }

    window.requestAnimFrame(draw);

  }

  function update () {

    if (mouseDown) {
      particle = new Particle(mouseX, mouseY);
      particles.push(particle);
      controls.particleCount++;
    }

    var particle1, particle2, i, j, len, min_distance, overlap, possible_x_bounds, possible_y_bounds, min_x, max_x, min_y, max_y;

    for (i = 0, len = particles.length; i < len ; i++) {
      particle1 = particles[i];

      // Collision detection with other particles:
      for (j = 0 ; j < len ; j++) {
        particle2 = particles[j];
        min_distance = (particle1.radius + particle2.radius) // * 0.8; // The 0.8 is to reduce the hitbox square to be smaller than the rendered circle image, so that circles that don't visually touch don't reflect off of invisible hitboxes.
        if (particle1 !== particle2 && Math.abs(particle2.x - particle1.x) < min_distance && Math.abs(particle2.y - particle1.y) < min_distance) {
          possible_x_bounds = [particle1.x-particle1.radius, particle1.x+particle1.radius, particle2.x-particle2.radius, particle2.x+particle2.radius];
          min_x = Math.min(...possible_x_bounds);
          max_x = Math.max(...possible_x_bounds);
          possible_y_bounds = [particle1.y-particle1.radius, particle1.y+particle1.radius, particle2.y-particle2.radius, particle2.y+particle2.radius];
          min_y = Math.min(...possible_y_bounds);
          max_y = Math.max(...possible_y_bounds);
          overlap = {
            x: ( (max_x-min_x) / min_distance ) * 0.1,
            y: ( (max_y-min_y) / min_distance ) * 0.1 // Comment out one of these 0.1's for a cool effect on one (or both) of the axes.
          };
          if (particle1.x < particle2.x ) overlap.x = -overlap.x;
					if (particle1.y < particle2.y ) overlap.y = -overlap.y;
          particle1.dx += overlap.x;
          particle1.dy += overlap.y;

          particle1.targetColor = particle2.targetColor;
        }
      }

      particle1.dx *= friction;
      particle1.dy *= friction;

      particle1.x += particle1.dx;
      particle1.y += particle1.dy;

      // Collision detection with the walls:
      if (particle1.x < particle1.radius || particle1.x+particle1.radius > screenWidth) {
        particle1.dx = -particle1.dx;
      }
      if (particle1.y < particle1.radius || particle1.y+particle1.radius > screenHeight) {
        particle1.dy = -particle1.dy;
      }
      particle1.x = Math.min( Math.max( particle1.x, particle1.radius ), 0 + screenWidth - particle1.radius );
			particle1.y = Math.min( Math.max( particle1.y, particle1.radius ), 0 + screenHeight - particle1.radius );

      // Color changing
      particle1.color.r += ( particle1.targetColor.r - particle1.color.r ) * colorChangeSpeed;
			particle1.color.g += ( particle1.targetColor.g - particle1.color.g ) * colorChangeSpeed;
			particle1.color.b += ( particle1.targetColor.b - particle1.color.b ) * colorChangeSpeed;
    }
  }

  // Event handlers:

  function handleClick (event) {
    // var particle = new Particle(event.clientX, event.clientY);
    // particles.push(particle);

    mouseDown = true;
  }
  function handleMove (event) {
    mouseX = event.clientX;
    mouseY = event.clientY;
  }
  function handleUp (event) {
    mouseDown = false;
  }

  window.addEventListener("resize", resize);

  c.addEventListener("mousedown", handleClick, false);
  c.addEventListener("mousemove", handleMove, false);
  c.addEventListener("mouseup", handleUp, false);

  resize();
  draw();

})();
<commit_msg>Fixed a bug where the GUI duplicated with every resize of the screen<commit_after>var c = document.querySelector('canvas'),
    ctx = c.getContext('2d'),
    screenWidth = 0,
    screenHeight = 0,
    particleCount = 0,
		particleRadius = 10,
    averageSpeed = 4,
    friction = 0.9,
    colorChangeSpeed = 0.04,
    backgroundColor = "rgba(0, 0, 0, 0.2)",
    startingColor = {
      r: 100,
      g: 100,
      b: 100
    },
    selectedColor = {
      r: 255,
      g: 255,
      b: 255
    },
    particles = [];

var mouseDown = false,
    mouseX = 0,
    mouseY = 0;

window.requestAnimFrame = (function () {
    return window.requestAnimationFrame       ||
           window.webkitRequestAnimationFrame ||
           window.mozRequestAnimationFrame    ||
           window.oRequestAnimationFrame      ||
           window.msRequestAnimationFrame     ||
           function (callback) {
               window.setTimeout(callback, 1000/60);
           };
})();

// function distance (x1, y1, x2, y2) {
//     return Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));
// }

function Particle (x, y) {
    this.x = x;
    this.y = y;
    this.dx = (Math.random()*averageSpeed)-(averageSpeed/2);
    this.dy = (Math.random()*averageSpeed)-(averageSpeed/2);
    this.color = {
      r: startingColor.r,
      g: startingColor.g,
      b: startingColor.b
    };
    this.targetColor = {
      r: selectedColor.r,
      g: selectedColor.g,
      b: selectedColor.b
    };
    this.radius = particleRadius;
}

Particle.prototype.draw = function () {
  // var grd;
  ctx.save();
  // grd = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.radius);
  // grd.addColorStop(1, 'rgba('+Math.round(this.color.r)+','+Math.round(this.color.g)+','+Math.round(this.color.b)+',1.0)');
  // grd.addColorStop(0, "rgba(0, 0, 0, 0)");
  ctx.beginPath();
  ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
  ctx.fillStyle = 'rgba('+Math.round(this.color.r)+','+Math.round(this.color.g)+','+Math.round(this.color.b)+',1.0)';
  // ctx.fillStyle = grd;
  ctx.fill();
  ctx.restore();
};

(function() {

  var Controls = function () {
    this.reset = function () {
      particles = [];
    }
    this.selectedColor = [255, 255, 255];
    this.friction = 1.0-friction;
    this.radius = particleRadius;
    this.particleCount = 0;
    this.addParticles = function () {
      var n = particles.length * 0.2;
      if (n == 0) {
        particle = new Particle(Math.random()*screenWidth, Math.random()*screenHeight);
        particles.push(particle);
      }
      else {
        for (var i = 0 ; i < n ; i++) {
          particle = new Particle(Math.random()*screenWidth, Math.random()*screenHeight);
          particles.push(particle);
        }
      }
    }
    this.removeParticles = function () {
      var n = particles.length * 0.2;
      for (i = 0 ; i < n ; i++) {
        particles.pop();
      }
    }
  };

  var controls, n, i, particle;

  function initGUI () {
    controls = new Controls();
    var gui = new dat.GUI();
    var qualities = gui.addFolder("Qualities");
    qualities.add(controls, "radius", 0.1, 30).name("Radius").onFinishChange(function () {
      particleRadius = controls.radius;
    });
    qualities.add(controls, "friction", -0.1, 1.0).name("Viscosity").onChange(function () {
      friction = 1.0 - controls.friction;
    });
    qualities.addColor(controls, "selectedColor").name("Color").onFinishChange(function () {
      selectedColor.r = controls.selectedColor[0];
      selectedColor.g = controls.selectedColor[1];
      selectedColor.b = controls.selectedColor[2];
    });

    var quantities = gui.addFolder("Quantities");
    quantities.add(controls, "addParticles").name("Add");
    quantities.add(controls, "removeParticles").name("Remove");
    quantities.add(controls, "reset").name("Reset");

  }

  function resize () {
    screenWidth = c.width = window.innerWidth;
    screenHeight = c.height = window.innerHeight;
  }

  function draw () {
    ctx.save();
    ctx.fillStyle = backgroundColor;
		ctx.fillRect(0, 0, screenWidth, screenHeight);

    update();

    for (var i = 0, len = particles.length ; i < len ; i++) {
      particles[i].draw();
    }

    window.requestAnimFrame(draw);

  }

  function update () {

    if (mouseDown) {
      particle = new Particle(mouseX, mouseY);
      particles.push(particle);
      controls.particleCount++;
    }

    var particle1, particle2, i, j, len, min_distance, overlap, possible_x_bounds, possible_y_bounds, min_x, max_x, min_y, max_y;

    for (i = 0, len = particles.length; i < len ; i++) {
      particle1 = particles[i];

      // Collision detection with other particles:
      for (j = 0 ; j < len ; j++) {
        particle2 = particles[j];
        min_distance = (particle1.radius + particle2.radius) // * 0.8; // The 0.8 is to reduce the hitbox square to be smaller than the rendered circle image, so that circles that don't visually touch don't reflect off of invisible hitboxes.
        if (particle1 !== particle2 && Math.abs(particle2.x - particle1.x) < min_distance && Math.abs(particle2.y - particle1.y) < min_distance) {
          possible_x_bounds = [particle1.x-particle1.radius, particle1.x+particle1.radius, particle2.x-particle2.radius, particle2.x+particle2.radius];
          min_x = Math.min(...possible_x_bounds);
          max_x = Math.max(...possible_x_bounds);
          possible_y_bounds = [particle1.y-particle1.radius, particle1.y+particle1.radius, particle2.y-particle2.radius, particle2.y+particle2.radius];
          min_y = Math.min(...possible_y_bounds);
          max_y = Math.max(...possible_y_bounds);
          overlap = {
            x: ( (max_x-min_x) / min_distance ) * 0.1,
            y: ( (max_y-min_y) / min_distance ) * 0.1 // Comment out one of these 0.1's for a cool effect on one (or both) of the axes.
          };
          if (particle1.x < particle2.x ) overlap.x = -overlap.x;
					if (particle1.y < particle2.y ) overlap.y = -overlap.y;
          particle1.dx += overlap.x;
          particle1.dy += overlap.y;

          particle1.targetColor = particle2.targetColor;
        }
      }

      particle1.dx *= friction;
      particle1.dy *= friction;

      particle1.x += particle1.dx;
      particle1.y += particle1.dy;

      // Collision detection with the walls:
      if (particle1.x < particle1.radius || particle1.x+particle1.radius > screenWidth) {
        particle1.dx = -particle1.dx;
      }
      if (particle1.y < particle1.radius || particle1.y+particle1.radius > screenHeight) {
        particle1.dy = -particle1.dy;
      }
      particle1.x = Math.min( Math.max( particle1.x, particle1.radius ), 0 + screenWidth - particle1.radius );
			particle1.y = Math.min( Math.max( particle1.y, particle1.radius ), 0 + screenHeight - particle1.radius );

      // Color changing
      particle1.color.r += ( particle1.targetColor.r - particle1.color.r ) * colorChangeSpeed;
			particle1.color.g += ( particle1.targetColor.g - particle1.color.g ) * colorChangeSpeed;
			particle1.color.b += ( particle1.targetColor.b - particle1.color.b ) * colorChangeSpeed;
    }
  }

  // Event handlers:

  function handleClick (event) {
    // var particle = new Particle(event.clientX, event.clientY);
    // particles.push(particle);

    mouseDown = true;
  }
  function handleMove (event) {
    mouseX = event.clientX;
    mouseY = event.clientY;
  }
  function handleUp (event) {
    mouseDown = false;
  }

  window.addEventListener("resize", resize);

  c.addEventListener("mousedown", handleClick, false);
  c.addEventListener("mousemove", handleMove, false);
  c.addEventListener("mouseup", handleUp, false);

  initGUI();
  resize();
  draw();

})();
<|endoftext|>