<commit_before>// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('../..')(server);
var port = process.env.PORT || 3000;

server.listen(port, function () {
  console.log('Server listening at port %d', port);
});
// Routing
app.use(express.static(__dirname + '/public'));

// *************************************

var nombreDeJoueurTotal = 0;  // Nombre de joueur en ligne

var j1 = "";
var j2 = "";

var idJ1 = "";
var idJ2 = "";

var mots = ["chat", "chien", "groslu"];
var mot = "";

var joueurAttente = []; // Les joueur qui attendent une partie

// Squelette d'un joueur
var joueur = {
  id: "",
  pseudo: ""
}
// Squelette d'une partie
var partie = {
  id: 0, // optionnel
  joueur1: joueur,
  joueur2: joueur,
  motADeviner: "",
  nombreIndice: 1,
  etat: "", // Inactif ? Deco ?
  libre: true
}

var listePartie = [];
// **************************************
io.on('connection', function (socket) {

  socket.on('new message', function (data) {
    socket.broadcast.emit('new message', {
      username: socket.username,
      message: data
    });
  });

  // *****************************************

  // client1 : nouveau joueur
  // serveur : nbjoueur ++
  // client1 :attente ...
  // client2 : nouveau joueur
  // serveur : -> nb joeuur ++
  // serveur : nbjoeuur = 2 => OK

  // serveur : commencer partie
  // serveur : Donner mot client 1
  // serveur : demander de deviner client2

  // ******************************************

  //***************************************
  function getRandomId()
  {
    var randomId = "";
    var randomChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    for( var i=0; i < 16; i++ )
    randomId += randomChar.charAt(Math.floor(Math.random() * randomChar.length));

    return randomId;
  }
  //***************************************

  // ============== TRAITEMENT =====================

  socket.on('add_user', function (username) {
    nombreDeJoueurTotal++;

    if(joueurAttente.length == 0){
      var joueurCourant = {
        id: socket.id,
        pseudo: username,
      }
      joueurAttente.push(joueurCourant);
      socket.id = null;
      socket.broadcast.to(joueurCourant.id).emit('attente', "En attente d'un second joueur");
    }

    else {

      var joueurCourant = {
        id: socket.id,
        pseudo: username,
      }
      socket.id = null;

      // Preparation des informations de la partie
      var partieCourante = {
        id: getRandomId(),
        joueur1: joueurCourant,
        joueur2: joueurAttente.pop(),
        motADeviner: mots[Math.floor(Math.random()*3)],
        nombreIndice: 1,
        etat: "", // Inactif ? Deco ?
        libre: false
      }
      // Ajout de la partie à la liste des parties en cours
      listePartie.push(partieCourante);

      socket.broadcast.to(partieCourante.joueur1.id).emit('attente', "Un joueur à été trouvé!");
      socket.broadcast.to(partieCourante.joueur2.id).emit('attente', "Un joueur à été trouvé!");
      socket.broadcast.to(partieCourante.joueur1.id).emit('attente', "
C'est toi qui commence!");

      socket.broadcast.to(partieCourante.joueur1.id).emit('rdy', {
              idPartie: partieCourante.id,
              adversaire: partieCourante.joueur2.pseudo,
              message: "Vous jouez contre "+partieCourante.joueur2.pseudo+"
"
            });

      socket.broadcast.to(partieCourante.joueur2.id).emit('rdy', {
              idPartie: partieCourante.id,
              adversaire: partieCourante.joueur1.pseudo,
              message: "Vous jouez contre "+partieCourante.joueur1.pseudo+"
"
            });

      socket.broadcast.to(partieCourante.joueur1.id).emit('start', {
              idPartie: partieCourante.id,
              mot: partieCourante.motADeviner,
              message: "Tu dois faire deviner le mot "+partieCourante.motADeviner+"
"
            });

      socket.broadcast.to(partieCourante.joueur2.id).emit('start', {
              idPartie: partieCourante.id,
              mot: "",
              message: "Tu dois essayer de DEVINER.
"
            });

      socket.broadcast.to(partieCourante.joueur1.id).emit('donner_indice', partieCourante.nombreIndice);

    }

  });

  socket.on('faire_deviner_mot', function (data) {
    var trouve = false;
    var iPartie = 0;
    for (i=0; i<listePartie.length; i++){
      if(listePartie[i].id == data.idPartie){
        iPartie=i;
        trouve = true;
        break;
      }
    }

    if(!trouve){
      console.log("SCENARIO D'ERREUR, A TRAITER");
    }
    else{
    socket.broadcast.to(listePartie[iPartie].joueur2.id).emit('recevoir_indice', data.indice);
  }
  });

  socket.on('repondre_mot', function (data) {
    var trouve = false;
    var iPartie = 0;
    for (i=0; i<listePartie.length; i++){
      if(listePartie[i].id == data.idPartie){
        iPartie=i;
        trouve = true;
        break;
      }
    }

    if(!trouve){
      console.log("SCENARIO D'ERREUR, A TRAITER");
    }
    else{

    socket.broadcast.to(listePartie[iPartie].joueur1.id).emit('mot_devine', listePartie[iPartie].joueur2.pseudo+" a répondu : "+data.mot+"");

    if (listePartie[iPartie].motADeviner == data.mot) {
      socket.broadcast.to(listePartie[iPartie].joueur1.id).emit('gagner', listePartie[iPartie].nombreIndice);
      socket.broadcast.to(listePartie[iPartie].joueur2.id).emit('gagner', listePartie[iPartie].nombreIndice);
    }
    else {
      listePartie[iPartie].nombreIndice++;
      socket.broadcast.to(listePartie[iPartie].joueur1.id).emit('donner_indice', listePartie[iPartie].nombreIndice);
    }
  }
  });

});
<commit_msg>Gestion des deconnexion<commit_after>// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('../..')(server);
var port = process.env.PORT || 3000;

server.listen(port, function () {
  console.log('Server listening at port %d', port);
});
// Routing
app.use(express.static(__dirname + '/public'));

// *************************************

var nombreDeJoueurTotal = 0;  // Nombre de joueur en ligne

var mots = ["chat", "chien", "groslu"];
var mot = "";

var joueurAttente = []; // Les joueurs qui attendent une partie

// Squelette d'un joueur
var joueur = {
  id: "",
  pseudo: ""
}
// Squelette d'une partie
var partie = {
  id: 0, // optionnel
  joueur1: joueur,
  joueur2: joueur,
  motADeviner: "",
  nombreIndice: 1,
  etat: "", // Inactif ? Deco ?
  libre: true
}

var listePartie = [];
// **************************************
io.on('connection', function (socket) {

  //***************************************
  function getRandomId()
  {
    var randomId = "";
    var randomChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    for( var i=0; i < 16; i++ )
    randomId += randomChar.charAt(Math.floor(Math.random() * randomChar.length));

    return randomId;
  }
  //***************************************

  // ============== TRAITEMENT =====================

  socket.on('disconnect', function() {
    var trouve = false;
    var iPartie = 0;

    for (i=0; i<joueurAttente.length; i++){
      if(joueurAttente[i].id == socket.id){
        iPartie=i;
        trouve = true;
        break;
      }
      else {
      }

    }

    if(trouve){
      joueurAttente.splice(iPartie, 1);
      nombreDeJoueurTotal--;
      //io.emit('attente', "Un joueur qui attendait s'est deco");
    }

    else {
      var nomJoueur = "";
      for (i=0; i<listePartie.length; i++){
        if(listePartie[i].joueur1.id == socket.id ){
          iPartie=i;
          trouve = true;
          nomJoueur = listePartie[i].joueur1.pseudo;
          break;
        }
        else if (listePartie[i].joueur2.id == socket.id){
          iPartie=i;
          trouve = true;
          nomJoueur = listePartie[i].joueur2.pseudo;
          break;
        }
      }

      if(trouve){
        io.emit('attente', "Un joueur dans une partie s'est deco ( "+nomJoueur+" ).");
        io.emit('attente', "La partie est donc dissoute.");
        nombreDeJoueurTotal--;
        listePartie.splice(iPartie, 1);
      }

      else {
              console.log("N'est pas censé arriver.");
              console.log("Il faut mettre les orphelins dans la file d'attente à nouveau");
      }

    }

  });

  socket.on('add_user', function (username) {
    nombreDeJoueurTotal++;
    var tmpSocketId = socket.id;

    if(joueurAttente.length == 0){
      var joueurCourant = {
        id: socket.id,
        pseudo: username,
      }
      joueurAttente.push(joueurCourant);
      socket.id = null;
      socket.broadcast.to(joueurCourant.id).emit('attente', "En attente d'un second joueur");
    }

    else {

      var joueurCourant = {
        id: socket.id,
        pseudo: username,
      }
      socket.id = null;

      // Preparation des informations de la partie
      var partieCourante = {
        id: getRandomId(),
        joueur1: joueurCourant,
        joueur2: joueurAttente.pop(),
        motADeviner: mots[Math.floor(Math.random()*3)],
        nombreIndice: 1,
        etat: "", // Inactif ? Deco ?
        libre: false
      }
      // Ajout de la partie à la liste des parties en cours
      listePartie.push(partieCourante);

      socket.broadcast.to(partieCourante.joueur1.id).emit('attente', "Un joueur à été trouvé!");
      socket.broadcast.to(partieCourante.joueur2.id).emit('attente', "Un joueur à été trouvé!");
      socket.broadcast.to(partieCourante.joueur1.id).emit('attente', "
C'est toi qui commence!");

      socket.broadcast.to(partieCourante.joueur1.id).emit('rdy', {
        idPartie: partieCourante.id,
        adversaire: partieCourante.joueur2.pseudo,
        message: "Vous jouez contre "+partieCourante.joueur2.pseudo+"
"
      });

      socket.broadcast.to(partieCourante.joueur2.id).emit('rdy', {
        idPartie: partieCourante.id,
        adversaire: partieCourante.joueur1.pseudo,
        message: "Vous jouez contre "+partieCourante.joueur1.pseudo+"
"
      });

      socket.broadcast.to(partieCourante.joueur1.id).emit('start', {
        idPartie: partieCourante.id,
        mot: partieCourante.motADeviner,
        message: "Tu dois faire deviner le mot "+partieCourante.motADeviner+"
"
      });

      socket.broadcast.to(partieCourante.joueur2.id).emit('start', {
        idPartie: partieCourante.id,
        mot: "",
        message: "Tu dois essayer de DEVINER.
"
      });

      socket.broadcast.to(partieCourante.joueur1.id).emit('donner_indice', partieCourante.nombreIndice);

    }
    socket.id = tmpSocketId;

  });

  socket.on('faire_deviner_mot', function (data) {
    var trouve = false;
    var iPartie = 0;
    for (i=0; i<listePartie.length; i++){
      if(listePartie[i].id == data.idPartie){
        iPartie=i;
        trouve = true;
        break;
      }
    }

    if(!trouve){
      console.log("SCENARIO D'ERREUR, A TRAITER");
    }
    else{
      socket.broadcast.to(listePartie[iPartie].joueur2.id).emit('recevoir_indice', data.indice);
    }
  });

  socket.on('repondre_mot', function (data) {
    var trouve = false;
    var iPartie = 0;
    for (i=0; i<listePartie.length; i++){
      if(listePartie[i].id == data.idPartie){
        iPartie=i;
        trouve = true;
        break;
      }
    }

    if(!trouve){
      console.log("SCENARIO D'ERREUR, A TRAITER");
    }
    else{

      socket.broadcast.to(listePartie[iPartie].joueur1.id).emit('mot_devine', listePartie[iPartie].joueur2.pseudo+" a répondu : "+data.mot+"");

      if (listePartie[iPartie].motADeviner == data.mot) {
        socket.broadcast.to(listePartie[iPartie].joueur1.id).emit('gagner', listePartie[iPartie].nombreIndice);
        socket.broadcast.to(listePartie[iPartie].joueur2.id).emit('gagner', listePartie[iPartie].nombreIndice);
      }
      else {
        listePartie[iPartie].nombreIndice++;
        socket.broadcast.to(listePartie[iPartie].joueur1.id).emit('donner_indice', listePartie[iPartie].nombreIndice);
      }
    }
  });

});
<|endoftext|>


<commit_before>angular.module('MyApp', ['ngResource', 'ngMessages', 'ngRoute', 'ngAuth', 'mgcrea.ngStrap'])
  .config(function($routeProvider, AuthProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/home.html',
        controller: 'HomeCtrl'
      })
      .when('/login', {
        templateUrl: 'views/login.html',
        controller: 'LoginCtrl'
      })
      .when('/signup', {
        templateUrl: 'views/signup.html',
        controller: 'SignupCtrl'
      })
      .when('/logout', {
        template: null,
        controller: 'LogoutCtrl'
      })
      .when('/protected', {
        templateUrl: 'views/protected.html',
        controller: 'ProtectedCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });

    AuthProvider.configure({
      apiUrl: '/api'
    });
  });
<commit_msg>Add api example usage inside configure method<commit_after>angular.module('MyApp', ['ngResource', 'ngMessages', 'ngRoute', 'ngAuth', 'mgcrea.ngStrap'])
  .config(function($routeProvider, AuthProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/home.html',
        controller: 'HomeCtrl'
      })
      .when('/login', {
        templateUrl: 'views/login.html',
        controller: 'LoginCtrl'
      })
      .when('/signup', {
        templateUrl: 'views/signup.html',
        controller: 'SignupCtrl'
      })
      .when('/logout', {
        template: null,
        controller: 'LogoutCtrl'
      })
      .when('/protected', {
        templateUrl: 'views/protected.html',
        controller: 'ProtectedCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });

    AuthProvider.configure({
      apiUrl: '/api',
      facebook: {
        clientId: '',
        redirectUri: '',
        scopes: []
      },
      hackerSchool: {
        clientId: '',
        redirectUri: '',
        scopes: [],
        verificationEndpoint: '',
        authorizationEndpoint: ''
      }
    });
  });
<|endoftext|>


<commit_before>;(function() {
  "use strict";

  var BaseController = require("BaseController");

  new BaseController({
    siteName: "Player.fm",
    play: ".fa-play",
    pause: ".fa-pause",
    playNext: ".fa-fast-forward",
    playPrev: ".fa-fast-backward",

    song: ".current-episode-link"
  });
})();
<commit_msg>Update PlayerFmController.js (#522)<commit_after>;(function() {
  "use strict";

  var BaseController = require("BaseController");

  new BaseController({
    siteName: "Player.fm",
    play: ".control.play",
    pause: ".control.pause",
    playNext: ".control.fast-forward",
    playPrev: ".control.fast-backward",

    song: ".current-episode-link"
  });
})();
<|endoftext|>


<commit_before>/**
 * Mailvelope - secure email with OpenPGP encryption for Webmail
 * Copyright (C) 2012  Thomas Oberndörfer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

'use strict';

var mvelo = mvelo || null;

(function() {
  // communication to background page
  var port;
  // shares ID with DecryptFrame
  var id;
  var watermark;
  var spinnerTimer;
  var commonPath;
  var l10n;

  function init() {
    //console.log('init decryptInline.js');
    var qs = jQuery.parseQuerystring();
    id = 'dDialog-' + qs.id;
    // open port to background page
    port = mvelo.extension.connect({name: id});
    port.onMessage.addListener(messageListener);
    port.postMessage({event: 'decrypt-inline-init', sender: id});
    if (mvelo.crx) {
      commonPath = '../../..';
    } else if (mvelo.ffa) {
      commonPath = mvelo.extension._dataPath + 'common';
    }
    addWrapper();
    addAttachmentPanel();
    addSandbox();
    mvelo.extension.sendMessage({event: "get-security-token"}, function(token) {
      $('#watermark').html(mvelo.util.encodeHTML(token.code));
    });
    $(window).on('resize', resizeFont);
    addErrorView();
    // show spinner
    spinnerTimer = setTimeout(showSpinner, 600);
    mvelo.l10n.getMessages([
      'alert_header_error'
    ], function(result) {
      l10n = result;
    });
  }

  function showSpinner() {
    $('body').addClass('spinner');
    if ($('body').height() + 2 > mvelo.LARGE_FRAME) {
      $('body').addClass('spinner-large');
    }
  }

  function addWrapper() {
    var wrapper = $('<div/>', {id: 'wrapper'});
    watermark = $('<div/>', {id: 'watermark'});
    watermark.appendTo(wrapper);
    wrapper.appendTo('body');
  }

  function addAttachmentPanel() {
    var attachments = $('<div/>', {
      id: 'attachments',
      css: {
        position: 'absolute',
        top: '0',
        left: 0,
        right: 0,
        bottom: '0',
        padding: '3px',
        'background-color': 'rgba(0,0,0,0)', // #D7E3FF
        overflow: 'auto'
      }
    });
    $('#wrapper').append(attachments);
  }

  function addSandbox() {
    var sandbox = $('<iframe/>', {
      id: 'decryptmail',
      sandbox: 'allow-same-origin',
      frameBorder: 0
    });
    var content = $('<div/>', {
      id: 'content',
      css: {
        position: 'absolute',
        top: '0',
        left: 0,
        right: 0,
        bottom: 0,
        padding: '3px',
        //'margin-top': '40px',
        'background-color': 'rgba(0,0,0,0)',
        overflow: 'auto'
      }
    });
    var style = $('<link/>', {
      rel: 'stylesheet',
      href: commonPath + '/dep/bootstrap/css/bootstrap.css'
    });
    sandbox.on('load', function() {
      $(this).contents().find('head').append(style);
      $(this).contents().find('body').css('background-color', 'rgba(0,0,0,0)');
      $(this).contents().find('body').append(content);
    });
    $('#wrapper').append(sandbox);
  }

  function addErrorView() {
    var errorbox = $('<div/>', {id: 'errorbox'});
    $('<div/>', {id: 'errorwell', class: 'well span5'}).appendTo(errorbox);
    errorbox.appendTo('body');
    if ($('body').height() + 2 > mvelo.LARGE_FRAME) {
      $('#errorbox').addClass('errorbox-large');
    }
  }

  function showMessageArea() {
    $('html, body').addClass('hide_bg');
    $('body').removeClass('spinner');
    $('#wrapper').addClass('fade-in');
    resizeFont();
  }

  function showErrorMsg(msg) {
    $('body').removeClass('spinner');
    clearTimeout(spinnerTimer);
    $('#errorbox').show();
    $('#errorwell').showAlert(l10n.alert_header_error, msg, 'danger')
                   .find('.alert').prepend($('<button/>', {type: 'button', class: 'close', html: '×'}))
                   .find('button').click(function() {
                      port.postMessage({event: 'decrypt-dialog-cancel', sender: id});
                    });
  }

  function resizeFont() {
    watermark.css('font-size', Math.floor(Math.min(watermark.width() / 3, watermark.height())));
  }

  function addAttachment(filename, content, mimeType, attachmentId) {
    var fileNameNoExt = mvelo.util.extractFileNameWithoutExt(filename);
    var fileExt = mvelo.util.extractFileExtension(filename);
    var extClass = mvelo.util.getExtensionClass(fileExt);

    var extensionButton = $('<span/>', {
      "class": 'label attachmentExtension ' + extClass
    }).append(fileExt);

    var objectURL = "#";

    // workarround until ff36 is out, using addonsdk for file saving
    if (mvelo.crx) {
      var contentLength = Object.keys(content).length;
      var uint8Array = new Uint8Array(contentLength);
      for (var i = 0; i < contentLength; i++) {
        uint8Array[i] = content[i];
      }
      var blob = new Blob([uint8Array], { type: mimeType });
      objectURL = window.URL.createObjectURL(blob);
    }

    var fileUI = $('<a/>', {
        "href": objectURL,
        "class": 'label label-default attachmentButton',
        "download": filename
      })
        .append(extensionButton)
        .append(" " + fileNameNoExt + " ")
        // workarround until ff36 is out, using addonsdk for file saving
        .on("click", function(e) {
          if (mvelo.ffa) {
            e.preventDefault();
            port.postMessage({event: 'get-attachment', sender: id, attachmentId: attachmentId});
          }
        })
      ;

    var $attachments = $('#attachments');
    $attachments.append(fileUI);
    $attachments.append(" ");
  }

  function messageListener(msg) {
    //console.log('decrypt dialog messageListener: ', JSON.stringify(msg));
    switch (msg.event) {
      case 'decrypted-message':
        showMessageArea();
        // js execution is prevented by Content Security Policy directive: "script-src 'self' chrome-extension-resource:"
        msg.message = $.parseHTML(msg.message);
        $('#decryptmail').contents().find('#content').append(msg.message);
        break;
      case 'add-decrypted-attachment':
        //console.log('popup adding decrypted attachment: ', JSON.stringify(msg.message));
        showMessageArea();
        addAttachment(msg.message.filename, msg.message.content, msg.message.mimeType, msg.message.attachmentId);
        break;
      case 'error-message':
        showErrorMsg(msg.error);
        break;
      default:
        console.log('unknown event');
    }
  }

  $(document).ready(init);

}());
<commit_msg>fixing missing styling on attachment buttons<commit_after>/**
 * Mailvelope - secure email with OpenPGP encryption for Webmail
 * Copyright (C) 2012  Thomas Oberndörfer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

'use strict';

var mvelo = mvelo || null;

(function() {
  // communication to background page
  var port;
  // shares ID with DecryptFrame
  var id;
  var watermark;
  var spinnerTimer;
  var commonPath;
  var l10n;

  function init() {
    //console.log('init decryptInline.js');
    var qs = jQuery.parseQuerystring();
    id = 'dDialog-' + qs.id;
    // open port to background page
    port = mvelo.extension.connect({name: id});
    port.onMessage.addListener(messageListener);
    port.postMessage({event: 'decrypt-inline-init', sender: id});
    if (mvelo.crx) {
      commonPath = '../../..';
    } else if (mvelo.ffa) {
      commonPath = mvelo.extension._dataPath + 'common';

      var styleMvelo = $('<link/>', { rel: 'stylesheet', href: commonPath + '/ui/mvelo.css' });
      var styleBootstrap = $('<link/>', { rel: 'stylesheet', href: commonPath + '/dep/bootstrap/css/bootstrap.css' });
      var styleDecryptInline = $('<link/>', { rel: 'stylesheet', href: commonPath + '/ui/inline/dialogs/decryptInline.css' });
      $(document).contents().find('head').append(styleBootstrap);
      $(document).contents().find('head').append(styleMvelo);
      $(document).contents().find('head').append(styleDecryptInline);
    }
    addWrapper();
    addAttachmentPanel();
    addSandbox();
    mvelo.extension.sendMessage({event: "get-security-token"}, function(token) {
      $('#watermark').html(mvelo.util.encodeHTML(token.code));
    });
    $(window).on('resize', resizeFont);
    addErrorView();
    // show spinner
    spinnerTimer = setTimeout(showSpinner, 600);
    mvelo.l10n.getMessages([
      'alert_header_error'
    ], function(result) {
      l10n = result;
    });
  }

  function showSpinner() {
    $('body').addClass('spinner');
    if ($('body').height() + 2 > mvelo.LARGE_FRAME) {
      $('body').addClass('spinner-large');
    }
  }

  function addWrapper() {
    var wrapper = $('<div/>', {id: 'wrapper'});
    watermark = $('<div/>', {id: 'watermark'});
    watermark.appendTo(wrapper);
    wrapper.appendTo('body');
  }

  function addAttachmentPanel() {
    var attachments = $('<div/>', {
      id: 'attachments',
      css: {
        position: 'absolute',
        top: '0',
        left: 0,
        right: 0,
        bottom: '0',
        padding: '3px',
        'background-color': 'rgba(0,0,0,0)', // #D7E3FF
        overflow: 'auto'
      }
    });
    $('#wrapper').append(attachments);
  }

  function addSandbox() {
    var sandbox = $('<iframe/>', {
      id: 'decryptmail',
      sandbox: 'allow-same-origin',
      frameBorder: 0
    });
    var content = $('<div/>', {
      id: 'content',
      css: {
        position: 'absolute',
        top: '0',
        left: 0,
        right: 0,
        bottom: 0,
        padding: '3px',
        //'margin-top': '40px',
        'background-color': 'rgba(0,0,0,0)',
        overflow: 'auto'
      }
    });
    var style = $('<link/>', {
      rel: 'stylesheet',
      href: commonPath + '/dep/bootstrap/css/bootstrap.css'
    });
    sandbox.on('load', function() {
      $(this).contents().find('head').append(style);
      $(this).contents().find('body').css('background-color', 'rgba(0,0,0,0)');
      $(this).contents().find('body').append(content);
    });
    $('#wrapper').append(sandbox);
  }

  function addErrorView() {
    var errorbox = $('<div/>', {id: 'errorbox'});
    $('<div/>', {id: 'errorwell', class: 'well span5'}).appendTo(errorbox);
    errorbox.appendTo('body');
    if ($('body').height() + 2 > mvelo.LARGE_FRAME) {
      $('#errorbox').addClass('errorbox-large');
    }
  }

  function showMessageArea() {
    $('html, body').addClass('hide_bg');
    $('body').removeClass('spinner');
    $('#wrapper').addClass('fade-in');
    resizeFont();
  }

  function showErrorMsg(msg) {
    $('body').removeClass('spinner');
    clearTimeout(spinnerTimer);
    $('#errorbox').show();
    $('#errorwell').showAlert(l10n.alert_header_error, msg, 'danger')
                   .find('.alert').prepend($('<button/>', {type: 'button', class: 'close', html: '×'}))
                   .find('button').click(function() {
                      port.postMessage({event: 'decrypt-dialog-cancel', sender: id});
                    });
  }

  function resizeFont() {
    watermark.css('font-size', Math.floor(Math.min(watermark.width() / 3, watermark.height())));
  }

  function addAttachment(filename, content, mimeType, attachmentId) {
    var fileNameNoExt = mvelo.util.extractFileNameWithoutExt(filename);
    var fileExt = mvelo.util.extractFileExtension(filename);
    var extClass = mvelo.util.getExtensionClass(fileExt);

    var extensionButton = $('<span/>', {
      "class": 'label attachmentExtension ' + extClass
    }).append(fileExt);

    var objectURL = "#";

    // workarround until ff36 is out, using addonsdk for file saving
    if (mvelo.crx) {
      var contentLength = Object.keys(content).length;
      var uint8Array = new Uint8Array(contentLength);
      for (var i = 0; i < contentLength; i++) {
        uint8Array[i] = content[i];
      }
      var blob = new Blob([uint8Array], { type: mimeType });
      objectURL = window.URL.createObjectURL(blob);
    }

    var fileUI = $('<a/>', {
        "href": objectURL,
        "class": 'label label-default attachmentButton',
        "download": filename
      })
        .append(extensionButton)
        .append(" " + fileNameNoExt + " ")
        // workarround until ff36 is out, using addonsdk for file saving
        .on("click", function(e) {
          if (mvelo.ffa) {
            e.preventDefault();
            port.postMessage({event: 'get-attachment', sender: id, attachmentId: attachmentId});
          }
        })
      ;

    var $attachments = $('#attachments');
    $attachments.append(fileUI);
    $attachments.append(" ");
  }

  function messageListener(msg) {
    //console.log('decrypt dialog messageListener: ', JSON.stringify(msg));
    switch (msg.event) {
      case 'decrypted-message':
        showMessageArea();
        // js execution is prevented by Content Security Policy directive: "script-src 'self' chrome-extension-resource:"
        msg.message = $.parseHTML(msg.message);
        $('#decryptmail').contents().find('#content').append(msg.message);
        break;
      case 'add-decrypted-attachment':
        //console.log('popup adding decrypted attachment: ', JSON.stringify(msg.message));
        showMessageArea();
        addAttachment(msg.message.filename, msg.message.content, msg.message.mimeType, msg.message.attachmentId);
        break;
      case 'error-message':
        showErrorMsg(msg.error);
        break;
      default:
        console.log('unknown event');
    }
  }

  $(document).ready(init);

}());
<|endoftext|>


<commit_before>var fs = require("fs");
var path = require("path");
var resolve = require("resolve");
const options = require("./util/options");

module.exports = function (file, api) {
    const j = api.jscodeshift;

    // We use resolve to find the absolute paths of our imports
    // Using the current file name as the base path
    const resolveOptions = {
        paths: [],
        basedir: path.dirname(file.path),
        extensions: [
            ".js",
            ".jsx"
        ],
        moduleDirectory: path.dirname(file.path)
    };

    const markForDeletion = (filePath) => {
        fs.appendFile(__dirname + "/../empty_indexes.txt", "\n" + filePath, function (err) {
            if (err) {
                console.error("Error:", err);
            }
        });
    };

    // Feed the original code to our interpreter
    return j(file.source)
        // Find all imports in the file
        .find(j.ImportDeclaration)
        // And remap them if necessary
        .map(p => {
            try {
                // First, find the canonical path to the import
                // This can be ./path/index.js or ./path.js
                // We don't care, just let Node figure it out
                let absoluteImportPath = resolve.sync(p.node.source.value, resolveOptions);

                // We assume the file is a relative file
                // (Otherwise it will throw an error and bail)
                // Load the contents of the file as a string...
                const importFileSource = fs.readFileSync(absoluteImportPath, "utf8");

                // ...and feed it to the AST interpreter...
                const importStatements = j(importFileSource)
                    // ...and find all the statements inside
                    .find(j.Statement);

                // If only 2 statements exist in this file,
                // let's check that one is an import and one is a default export
                if (importStatements.__paths.length === 2) {
                    const [firstStatement, lastStatement] = importStatements.__paths;

                    // If the first statement is an ImportDeclaration
                    // and the last statement is an ExportDefaultDeclaration
                    if (
                        firstStatement.node.type === "ImportDeclaration" &&
                        lastStatement.node.type === "ExportDefaultDeclaration"
                    ) {
                        const importName = firstStatement.node.specifiers[0].local.name;
                        const exportName = lastStatement.node.declaration.name;

                        // If the module this file is exporting is the module it is importing...
                        if (exportName === importName) {
                            // Mark the index file for deletion
                            markForDeletion(absoluteImportPath);

                            // Then bypass the shell index file entirely
                            // Set the import path to be the import defined in the shell index file
                            absoluteImportPath = resolve.sync(firstStatement.node.source.value, Object.assign({}, resolveOptions, {
                                // Trick the resolver to resolve from the current index file
                                basedir: path.dirname(absoluteImportPath),
                                moduleDirectory: path.dirname(absoluteImportPath)
                            }));
                        }
                    }
                }

                // Finally, make the import relative to the current file...
                let relativeImportPath = path.relative(resolveOptions.basedir, absoluteImportPath);

                // ...and prepend a dot/slash so jspm is happy
                if (!relativeImportPath.startsWith(".")) {
                    // (...only if we need to)
                    relativeImportPath = "./" + relativeImportPath;
                }

                p.node.source.value = relativeImportPath;
            } catch (e) {} // eslint-disable-line no-empty

            // Return the mapped value
            return p;
        })

        // Convert AST to source
        .toSource(options);
};
<commit_msg>Fix a missed arrow function and some lets.<commit_after>var fs = require("fs");
var path = require("path");
var resolve = require("resolve");
const options = require("./util/options");

module.exports = function (file, api) {
    const j = api.jscodeshift;

    // We use resolve to find the absolute paths of our imports
    // Using the current file name as the base path
    const resolveOptions = {
        paths: [],
        basedir: path.dirname(file.path),
        extensions: [
            ".js",
            ".jsx"
        ],
        moduleDirectory: path.dirname(file.path)
    };

    const markForDeletion = (filePath) => {
        fs.appendFile(__dirname + "/../empty_indexes.txt", "\n" + filePath, function (err) {
            if (err) {
                console.error("Error:", err);
            }
        });
    };

    // Feed the original code to our interpreter
    return j(file.source)
        // Find all imports in the file
        .find(j.ImportDeclaration)
        // And remap them if necessary
        .map(function (p) {
            try {
                // First, find the canonical path to the import
                // This can be ./path/index.js or ./path.js
                // We don't care, just let Node figure it out
                var absoluteImportPath = resolve.sync(p.node.source.value, resolveOptions);

                // We assume the file is a relative file
                // (Otherwise it will throw an error and bail)
                // Load the contents of the file as a string...
                const importFileSource = fs.readFileSync(absoluteImportPath, "utf8");

                // ...and feed it to the AST interpreter...
                const importStatements = j(importFileSource)
                    // ...and find all the statements inside
                    .find(j.Statement);

                // If only 2 statements exist in this file,
                // let's check that one is an import and one is a default export
                if (importStatements.__paths.length === 2) {
                    const [firstStatement, lastStatement] = importStatements.__paths;

                    // If the first statement is an ImportDeclaration
                    // and the last statement is an ExportDefaultDeclaration
                    if (
                        firstStatement.node.type === "ImportDeclaration" &&
                        lastStatement.node.type === "ExportDefaultDeclaration"
                    ) {
                        const importName = firstStatement.node.specifiers[0].local.name;
                        const exportName = lastStatement.node.declaration.name;

                        // If the module this file is exporting is the module it is importing...
                        if (exportName === importName) {
                            // Mark the index file for deletion
                            markForDeletion(absoluteImportPath);

                            // Then bypass the shell index file entirely
                            // Set the import path to be the import defined in the shell index file
                            absoluteImportPath = resolve.sync(firstStatement.node.source.value, Object.assign({}, resolveOptions, {
                                // Trick the resolver to resolve from the current index file
                                basedir: path.dirname(absoluteImportPath),
                                moduleDirectory: path.dirname(absoluteImportPath)
                            }));
                        }
                    }
                }

                // Finally, make the import relative to the current file...
                var relativeImportPath = path.relative(resolveOptions.basedir, absoluteImportPath);

                // ...and prepend a dot/slash so jspm is happy
                if (!relativeImportPath.startsWith(".")) {
                    // (...only if we need to)
                    relativeImportPath = "./" + relativeImportPath;
                }

                p.node.source.value = relativeImportPath;
            } catch (e) {} // eslint-disable-line no-empty

            // Return the mapped value
            return p;
        })

        // Convert AST to source
        .toSource(options);
};
<|endoftext|>


<commit_before>/* File: schedule.js
 * Organization: Technical Entrepreneurs <Tech-Es>
 * Contributors:
 * Date: 10/28/2014
 * Desc: schedule popup, it handles the popup for upcoming stop times.
 * requires: lib.config, lib.data;
 */

var modal = Ti.UI.currentWindow;
var stopid = modal.stopid;
var regionid = modal.region;

var data = require('/lib/data');
var AppData = new data();

var config = require('/lib/config');

var schedule = {
	stopId: stopid,
	regionId: regionid,
	data: {},
	init: function(){
		this.data = AppData.getSchedule(this.regionId,this.stopId);
		Ti.API.info(JSON.stringify(this.data));

		var view = Ti.UI.createView
		({
			top: 0,
			left: 0,
			height: config.MODAL_HEIGHT,
			width: config.MODAL_WIDTH,
			backgroundColor: config.MODAL_BACKGROUND_COLOR
		});

		var close = Ti.UI.createImageView({
			image:config.HS_ASSETS+'/images/close-icon.jpg',
			top:5,
			left: config.DISP_WIDTH-((config.DISP_HEIGHT/10)/2),
			height:(config.DISP_HEIGHT/10)/2,
			width: (config.DISP_HEIGHT/10)/2
		});

		close.addEventListener('touchend', function(e){
			Ti.UI.currentWindow.close();
		});

		var currentStop = this.getSchedule();

		var color = "";
		if(currentStop[0] == -1)
		{
			color = config.TIME_COLOR_4;
		}
		if(currentStop[0] > 6)
		{
			currColor = config.TIME_COLOR_1;
		}
		else
		{
			if(currentStop[0] < 3)
			{
				currColor = config.TIME_COLOR_3;
			}
			else
			{
				currColor = config.TIME_COLOR_2;
			}
		}

		if(currentStop[0] != -1)
		{
			var label0 = Ti.UI.createLabel
			({
				top:5,
				text:"The shuttle arrives in:",
				color: config.TIME_COLOR_4,
				height:"auto",
				width:"auto",
				font: config.SCHEDULE_FONT,
				textAlign: Ti.UI.TEXT_ALIGNMENT_LEFT
			});

			var label1 = Ti.UI.createLabel
			({
				top:25,
				text:schedule[0]+" minutes",
				color: currColor,
				height:"auto",
				width:"auto",
				font: config.DEFAULT_FONT,
				textAlign: Ti.UI.TEXT_ALIGNMENT_LEFT
			});
			view.add(label0);
			view.add(label1);
		}
		else
		{
			var label0 = Ti.UI.createLabel
			({
				top:5,
				text:"There are no scheduled arrivals remaining.",
				color: config.TIME_COLOR_4,
				height:"auto",
				width:"auto",
				font: config.SCHEDULE_FONT,
				textAlign: Ti.UI.TEXT_ALIGNMENT_LEFT
			});
			view.add(label0);
		}

		view.add(close);
		var lView = this.getListOfStops(currentStop[1]);
		modal.add(view);
		modal.add(lView);
	},
	getSchedule: function()
	{
		var ETA = -1;
		var arrival;
		var index = 0;
		var timeNow = new Date();
		var schedule = this.data;

		for( time in schedule)
		{
			var stopTime = this.parseTime(schedule[time][0]);
			if(timeNow>stopTime)
			{
				continue;
			}
			else
			{
				arrival = stopTime;
				ETA = Math.round(((stopTime.getTime() - timeNow.getTime())/1000)/60);
				index = time;
				break;
			}
		}

		return [ETA, index, arrival];

	},
	getListOfStops: function(index){
		var schedule = this.data;
		var listView = Ti.UI.createListView({
				backgroundColor: config.MODAL_BACKGROUND_COLOR,
				separatorColor: config.DEFAULT_FONT_COLOR
			});
		var sections = [];
		var dataset = [];
		var stopSection = Ti.UI.createListSection({
				headerTitle: 'Remaining Schedule'
			});

		for(time in schedule)
		{
			dataset.push({properties:
					{
					title: this.parseTime(schedule[time][0].split(" ")[0]),
					font: config.ALTERNATIVE_FONT,
					color: config.DEFAULT_FONT_COLOR,
					height: "20px"
					}
			});
		}

		stopSection.setItems(dataset);
		sections.push(stopSection);
		listView.sections = sections;
		return listView;
	},
	parseTime: function(timeStr, dt)
	{
	    if (!dt) {
	        dt = new Date();
	    }

	    var time = timeStr.match(/(\d+)(?::(\d\d))?\s*(p?)/i);
	    if (!time) {
	        return NaN;
	    }
	    var hours = parseInt(time[1], 10);
	    if (hours == 12 && !time[3]) {
	        hours = 0;
	    }
	    else {
	        hours += (hours < 12 && time[3]) ? 12 : 0;
	    }

	    dt.setHours(hours);
	    dt.setMinutes(parseInt(time[2], 10) || 0);
	    dt.setSeconds(0, 0);
	    return dt;
	}
};
schedule.init();
<commit_msg>updated schedule js<commit_after>/* File: schedule.js
 * Organization: Technical Entrepreneurs <Tech-Es>
 * Contributors:
 * Date: 10/28/2014
 * Desc: schedule popup, it handles the popup for upcoming stop times.
 * requires: lib.config, lib.data;
 */

var modal = Ti.UI.currentWindow;
var stopid = modal.stopid;
var regionid = modal.region;

var data = require('/lib/data');
var AppData = new data();

var config = require('/lib/config');

var schedule = {
	stopId: stopid,
	regionId: regionid,
	data: {},
	init: function(){
		this.data = AppData.getSchedule(this.regionId,this.stopId);
		Ti.API.info(JSON.stringify(this.data));

		var view = Ti.UI.createView
		({
			top: 0,
			left: 0,
			height: config.MODAL_HEIGHT,
			width: config.MODAL_WIDTH,
			backgroundColor: config.MODAL_BACKGROUND_COLOR
		});

		var close = Ti.UI.createImageView({
			image:config.HS_ASSETS+'/images/close-icon.jpg',
			top:5,
			left: config.DISP_WIDTH-((config.DISP_HEIGHT/10)/2),
			height:(config.DISP_HEIGHT/10)/2,
			width: (config.DISP_HEIGHT/10)/2
		});

		close.addEventListener('touchend', function(e){
			Ti.UI.currentWindow.close();
		});

		var currentStop = this.getSchedule();

		var color = "";
		if(currentStop[0] == -1)
		{
			color = config.TIME_COLOR_4;
		}
		if(currentStop[0] > 6)
		{
			currColor = config.TIME_COLOR_1;
		}
		else
		{
			if(currentStop[0] < 3)
			{
				currColor = config.TIME_COLOR_3;
			}
			else
			{
				currColor = config.TIME_COLOR_2;
			}
		}

		if(currentStop[0] != -1)
		{
			var label0 = Ti.UI.createLabel
			({
				top:5,
				text:"The shuttle arrives in:",
				color: config.TIME_COLOR_4,
				height:"auto",
				width:"auto",
				font: config.SCHEDULE_FONT,
				textAlign: Ti.UI.TEXT_ALIGNMENT_LEFT
			});

			var label1 = Ti.UI.createLabel
			({
				top:25,
				text:currentStop[0]+" minutes",
				color: currColor,
				height:"auto",
				width:"auto",
				font: config.DEFAULT_FONT,
				textAlign: Ti.UI.TEXT_ALIGNMENT_LEFT
			});
			view.add(label0);
			view.add(label1);
		}
		else
		{
			var label0 = Ti.UI.createLabel
			({
				top:5,
				text:"There are no scheduled arrivals remaining.",
				color: config.TIME_COLOR_4,
				height:"auto",
				width:"auto",
				font: config.SCHEDULE_FONT,
				textAlign: Ti.UI.TEXT_ALIGNMENT_LEFT
			});
			view.add(label0);
		}

		view.add(close);
		var lView = this.getListOfStops(currentStop[1]);
		modal.add(view);
		modal.add(lView);
	},
	getSchedule: function()
	{
		var ETA = -1;
		var arrival;
		var index = 0;
		var timeNow = new Date();
		var schedule = this.data;

		for( time in schedule)
		{
			var stopTime = this.parseTime(schedule[time][0]);
			if(timeNow>stopTime)
			{
				continue;
			}
			else
			{
				arrival = stopTime;
				ETA = Math.round(((stopTime.getTime() - timeNow.getTime())/1000)/60);
				index = time;
				break;
			}
		}

		Ti.API.info("Arrival: "+arrival);
		Ti.API.info("ETA: "+ETA+" Minutes");
		Ti.API.info("Time Now: "+timeNow);

		return [ETA, index, arrival];

	},
	getListOfStops: function(index){
		var schedule = this.data;
		var listView = Ti.UI.createListView({
				backgroundColor: config.MODAL_BACKGROUND_COLOR,
				separatorColor: config.DEFAULT_FONT_COLOR
			});
		var sections = [];
		var dataset = [];
		var stopSection = Ti.UI.createListSection({
				headerTitle: 'Remaining Schedule'
			});

		for(time in schedule)
		{
			var timeString =
			dataset.push({properties:
					{
						title: this.formatAMPM(this.parseTime(schedule[time][0])),
						font: config.ALTERNATIVE_FONT,
						color: config.DEFAULT_FONT_COLOR,
						height: "80px"
					}
			});
		}

		stopSection.setItems(dataset);
		sections.push(stopSection);
		listView.sections = sections;
		return listView;
	},
	parseTime: function(timeStr, dt)
	{
	    if (!dt) {
	        dt = new Date();
	    }

	    var time = timeStr.match(/(\d+)(?::(\d\d))?\s*(p?)/i);
	    if (!time) {
	        return NaN;
	    }
	    var hours = parseInt(time[1], 10);
	    if (hours == 12 && !time[3]) {
	        hours = 0;
	    }
	    else {
	        hours += (hours < 12 && time[3]) ? 12 : 0;
	    }

	    dt.setHours(hours);
	    dt.setMinutes(parseInt(time[2], 10) || 0);
	    dt.setSeconds(0, 0);
	    return dt;
	},
	formatAMPM:	function (date) {
	  var hours = date.getHours();
	  var minutes = date.getMinutes();
	  var ampm = hours >= 12 ? 'pm' : 'am';
	  hours = hours % 12;
	  hours = hours ? hours : 12; // the hour '0' should be '12'
	  minutes = minutes < 10 ? '0'+minutes : minutes;
	  var strTime = hours + ':' + minutes + ' ' + ampm;
	  return strTime;
	}
};
schedule.init();
<|endoftext|>


<commit_before>// ==UserScript==
// @name         SFcomic Viewer
// @namespace
// @version      0.2.1
// @description  Auto load comic picture.
// @author       AntonioTsai
// @match        http://comic.sfacg.com/*
// @include      http://comic.sfacg.com/*
// @grant        none
// @downloadURL  https://github.com/AntonioTsai/SFcomic-Viewer/raw/master/SFcomicViewer.user.js
// ==/UserScript==

/**
 * picAy: The array store all image's url of the volume
 * hosts: The array store possible host
 * getHost(): Get the current host index
 */
(() => {
	const imgTable = document.querySelector("table tbody");
	const tempTr = document.createElement("tr");
	tempTr.appendChild(document.createElement("td"));

	// Remove original image & social media icon
	imgTable.querySelector('tr').remove();

	// Render all images
	picAy.map((pic) => {
		const imgTr = tempTr.cloneNode(true);
		const img = document.createElement("img");

		img.src = hosts[getHost()] + pic;
		img.className = "scalable";
		imgTr.querySelector('td').appendChild(img);
		imgTable.appendChild(imgTr);
	});

	// Add CSS style
	const customStyle = document.createElement('style');
	let styleSheet;

	document.head.appendChild(customStyle);
	styleSheet = customStyle.sheet;
	// Set max-width to a huge number to ensure img show as its original size
	styleSheet.insertRule("img.scalable { max-width: 100vw; width: auto; -webkit-transition: max-width .5s 2s; transition: transition: max-width .5s 2s; }");
	styleSheet.insertRule("img.scalable:hover { max-width: 5000px; -webkit-transition: max-width .25s 0s; transition: max-width .25s 0s; }");
})();
<commit_msg>Edit the delay time<commit_after>// ==UserScript==
// @name         SFcomic Viewer
// @namespace
// @version      0.2.1
// @description  Auto load comic picture.
// @author       AntonioTsai
// @match        http://comic.sfacg.com/*
// @include      http://comic.sfacg.com/*
// @grant        none
// @downloadURL  https://github.com/AntonioTsai/SFcomic-Viewer/raw/master/SFcomicViewer.user.js
// ==/UserScript==

/**
 * picAy: The array store all image's url of the volume
 * hosts: The array store possible host
 * getHost(): Get the current host index
 */
(() => {
	const imgTable = document.querySelector("table tbody");
	const tempTr = document.createElement("tr");
	tempTr.appendChild(document.createElement("td"));

	// Remove original image & social media icon
	imgTable.querySelector('tr').remove();

	// Render all images
	picAy.map((pic) => {
		const imgTr = tempTr.cloneNode(true);
		const img = document.createElement("img");

		img.src = hosts[getHost()] + pic;
		img.className = "scalable";
		imgTr.querySelector('td').appendChild(img);
		imgTable.appendChild(imgTr);
	});

	// Add CSS style
	const customStyle = document.createElement('style');
	let styleSheet;

	document.head.appendChild(customStyle);
	styleSheet = customStyle.sheet;
	// Set max-width to a huge number to ensure img show as its original size
	styleSheet.insertRule(`img.scalable {
		max-width: 100vw;
		width: auto;
		-webkit-transition: max-width .5s 1s;
		transition: transition: max-width .5s 1s;
	}`);
	styleSheet.insertRule(`img.scalable:hover {
		max-width: 5000px;
		-webkit-transition: max-width .25s 0.5s;
		transition: max-width .25s 0.5s;
	}`);
})();
<|endoftext|>


<commit_before>
<commit_msg>Update page-specific.js<commit_after>//only works for positive for now!
var getNumberOne = function(){
	return new number( true, $("#number-one").text() );
}

var getNumberTwo = function(){
	return new number( true, $("#number-two").text() );
}

var doAdd = function(){
	var result = getNumberOne().plus(getNumberTwo());
 	$("#result").text() = result;
}

var doSubtract = function(){
	var result = getNumberOne().minus(getNumberTwo());
 	$("#result").text() = result;
}
<|endoftext|>


<commit_before>module.exports = function(config) {
  config.set({
    basePath: '../',

    frameworks: ['jasmine'],

    files: [
      APPLICATION_SPEC,
      'app/assets/javascripts/shared/jquery-1.8.0.js', // TODO: Can we link to Rails' jquery?
      'app/assets/javascripts/shared/jquery.timeago.js',
      'app/assets/javascripts/shared/angular-local-storage.js',
      'app/assets/javascripts/shared/bindonce.min.js',
      'app/assets/javascripts/shared/ng-infinite-scroll.min.js',

      'app/assets/javascripts/admin/*.js*',
      'app/assets/javascripts/admin/*/*.js*', // Pull in top level files in each folder first (often these are module declarations)
      'app/assets/javascripts/admin/**/*.js*',
      'app/assets/javascripts/darkswarm/*.js*',
      'app/assets/javascripts/darkswarm/**/*.js*',
      'spec/javascripts/unit/**/*.js*'
    ],

    exclude: [
      '**/.#*',
      'app/assets/javascripts/darkswarm/all.js.coffee',
      'app/assets/javascripts/darkswarm/overrides.js.coffee',
      'app/assets/javascripts/admin/util.js.erb'
    ],

    preprocessors: {
      '**/*.coffee': ['coffee']
    },

    coffeePreprocessor: {
      options: {
        sourceMap: true
      },
      transformPath: function(path) {
        return path.replace(/\.coffee$/, '.js');
      }
    },

    autoWatch: true,

    browsers: ['Chrome'],

    junitReporter: {
      outputFile: 'log/testacular-unit.xml',
      suite: 'unit'
    }
  });
};
<commit_msg>Use phantomjs for js specs, so we can run them in CI<commit_after>module.exports = function(config) {
  config.set({
    basePath: '../',

    frameworks: ['jasmine'],

    files: [
      APPLICATION_SPEC,
      'app/assets/javascripts/shared/jquery-1.8.0.js', // TODO: Can we link to Rails' jquery?
      'app/assets/javascripts/shared/jquery.timeago.js',
      'app/assets/javascripts/shared/angular-local-storage.js',
      'app/assets/javascripts/shared/bindonce.min.js',
      'app/assets/javascripts/shared/ng-infinite-scroll.min.js',

      'app/assets/javascripts/admin/*.js*',
      'app/assets/javascripts/admin/*/*.js*', // Pull in top level files in each folder first (often these are module declarations)
      'app/assets/javascripts/admin/**/*.js*',
      'app/assets/javascripts/darkswarm/*.js*',
      'app/assets/javascripts/darkswarm/**/*.js*',
      'spec/javascripts/unit/**/*.js*'
    ],

    exclude: [
      '**/.#*',
      'app/assets/javascripts/darkswarm/all.js.coffee',
      'app/assets/javascripts/darkswarm/overrides.js.coffee',
      'app/assets/javascripts/admin/util.js.erb'
    ],

    preprocessors: {
      '**/*.coffee': ['coffee']
    },

    coffeePreprocessor: {
      options: {
        sourceMap: true
      },
      transformPath: function(path) {
        return path.replace(/\.coffee$/, '.js');
      }
    },

    autoWatch: true,

    browsers: ['PhantomJS'],

    junitReporter: {
      outputFile: 'log/testacular-unit.xml',
      suite: 'unit'
    }
  });
};
<|endoftext|>


<commit_before>import * as L from "leaflet/src/Leaflet";
import {CooperativeScrollWheelZoom} from "./LeafletCooperativeScrollWheelZoom";

let coordsToPolygon = function (coords) {
    return coords[0].map((lnglat) => L.latLng(lnglat[1], lnglat[0]));
};

let getPolygonCoveringExterior = function (limitRect, outline) {
    let polygons = [];
    if (typeof(outline) === 'string') {
        outline = JSON.parse(outline);
    }

    // The limit of the view is the outer ring of the polygon
    polygons.push([
        L.latLng(limitRect['min']['lat'], limitRect['min']['lng']),
        L.latLng(limitRect['min']['lat'], limitRect['max']['lng']),
        L.latLng(limitRect['max']['lat'], limitRect['max']['lng']),
        L.latLng(limitRect['max']['lat'], limitRect['min']['lng']),
    ]);

    // The shape of the city itself is the "hole" in the polygon
    if (outline['type'] && outline['type'] === 'Polygon') {
        polygons.push(coordsToPolygon(outline['coordinates']));
    }
    if (outline['features']) {
        outline['features'].forEach((feature) => {
            if (feature['geometry']['type'] === 'MultiPolygon') {
                feature['geometry']['coordinates'].forEach((coords) => polygons.push(coordsToPolygon(coords)));
            }
            if (feature['geometry']['type'] === 'Polygon') {
                polygons.push(coordsToPolygon(feature['geometry']['coordinates']));
            }
        });
    }

    return polygons;
};

let setTiles = function (leaflet, initData) {
    let tiles = initData['tiles'];
    switch (tiles['provider']) {
        case 'OSM':
            L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                attribution: 'Map data © OpenStreetMap',
            }).addTo(leaflet);
            break;
        case 'Mapbox':
            let tileUrl = (tiles['tileUrl'] ? tiles['tileUrl'] : 'https://api.tiles.mapbox.com/v4/mapbox.streets/{z}/{x}/{y}{highres}.png?access_token={accessToken}');
            L.tileLayer(tileUrl, {
                attribution: 'Map data © OpenStreetMap, CC-BY-SA, Imagery © Mapbox',
                accessToken: tiles['token'],
                highres: (window.devicePixelRatio > 1.5 ? '@2x' : '')
            }).addTo(leaflet);
            break;
    }
};

let setInitView = function (leaflet, initData) {
    let initCenter = L.latLng(35.658611, 139.745556); // If nothing else is said explicitly, we're probably talking about the Tokyo Tower
    let initZoom = 15;
    if (initData['center']) {
        initCenter = L.latLng(initData['center']['lat'], initData['center']['lng']);
    }
    if (initData['zoom']) {
        initZoom = initData['zoom'];
    }
    leaflet.setView(initCenter, initZoom);
};

let setOutline = function (leaflet, initData) {
    if (initData['outline'] && initData['limit']) {
        let polygon = getPolygonCoveringExterior(initData['limit'], initData['outline']);
        let polygonLayer = L.polygon(polygon, {
            weight: 1,
            fillColor: "#ffffff",
            fillOpacity: 0.75,
            stroke: true,
            color: '#0000ff',
            opacity: 0.5,
            dashArray: [2, 4]
        });
        leaflet.addLayer(polygonLayer);
    }
};

let setBounds = function (leaflet, initData) {
    if (initData['limit']) {
        let bounds = L.latLngBounds(
            L.latLng(initData['limit']['min']['lat'], initData['limit']['min']['lng']),
            L.latLng(initData['limit']['max']['lat'], initData['limit']['max']['lng'])
        );
        leaflet.setMaxBounds(bounds);
    }
};

let setZoomBehavior = function (leaflet) {
    leaflet.addHandler('cooperativezoom', CooperativeScrollWheelZoom);
    leaflet.cooperativezoom.enable();
};

export default function ($map_element, initData, noTouchDrag) {
    // noTouchDrag: Dragging is disabled by default.
    // For mouse users, it is enabled once the first mouse-typical event occurs
    // For touch users, it is enabled as long as the map has the focus
    // This behavior is enabled for maps that are shown by default. Drop-down-maps keep leaflet's default behavior.

    let leaflet = L.map($map_element.attr("id"), {
        maxBoundsViscosity: 1,
        minZoom: (initData['zoom'] < 12 ? initData['zoom'] : 12),
        maxZoom: 19,
        scrollWheelZoom: !(noTouchDrag === true),
        dragging: !(noTouchDrag === true),
    });

    setTiles(leaflet, initData);
    setInitView(leaflet, initData);
    setBounds(leaflet, initData);
    setOutline(leaflet, initData);

    if (noTouchDrag === true) {
        setZoomBehavior(leaflet);

        let mouseMode = false;

        $map_element.one("mousedown mousemove", () => {
            leaflet.dragging.enable();
            mouseMode = true;
        });
        $map_element.on("focus", () => {
            if (!mouseMode) {
                leaflet.dragging.enable();
            }
        });
        $map_element.on("focus", () => {
            if (!mouseMode) {
                leaflet.dragging.disable();
            }
        });
    }

    return leaflet;
}
<commit_msg>Bugfix: ghost events / disabling dragging again<commit_after>import * as L from "leaflet/src/Leaflet";
import {CooperativeScrollWheelZoom} from "./LeafletCooperativeScrollWheelZoom";

let coordsToPolygon = function (coords) {
    return coords[0].map((lnglat) => L.latLng(lnglat[1], lnglat[0]));
};

let getPolygonCoveringExterior = function (limitRect, outline) {
    let polygons = [];
    if (typeof(outline) === 'string') {
        outline = JSON.parse(outline);
    }

    // The limit of the view is the outer ring of the polygon
    polygons.push([
        L.latLng(limitRect['min']['lat'], limitRect['min']['lng']),
        L.latLng(limitRect['min']['lat'], limitRect['max']['lng']),
        L.latLng(limitRect['max']['lat'], limitRect['max']['lng']),
        L.latLng(limitRect['max']['lat'], limitRect['min']['lng']),
    ]);

    // The shape of the city itself is the "hole" in the polygon
    if (outline['type'] && outline['type'] === 'Polygon') {
        polygons.push(coordsToPolygon(outline['coordinates']));
    }
    if (outline['features']) {
        outline['features'].forEach((feature) => {
            if (feature['geometry']['type'] === 'MultiPolygon') {
                feature['geometry']['coordinates'].forEach((coords) => polygons.push(coordsToPolygon(coords)));
            }
            if (feature['geometry']['type'] === 'Polygon') {
                polygons.push(coordsToPolygon(feature['geometry']['coordinates']));
            }
        });
    }

    return polygons;
};

let setTiles = function (leaflet, initData) {
    let tiles = initData['tiles'];
    switch (tiles['provider']) {
        case 'OSM':
            L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                attribution: 'Map data © OpenStreetMap',
            }).addTo(leaflet);
            break;
        case 'Mapbox':
            let tileUrl = (tiles['tileUrl'] ? tiles['tileUrl'] : 'https://api.tiles.mapbox.com/v4/mapbox.streets/{z}/{x}/{y}{highres}.png?access_token={accessToken}');
            L.tileLayer(tileUrl, {
                attribution: 'Map data © OpenStreetMap, CC-BY-SA, Imagery © Mapbox',
                accessToken: tiles['token'],
                highres: (window.devicePixelRatio > 1.5 ? '@2x' : '')
            }).addTo(leaflet);
            break;
    }
};

let setInitView = function (leaflet, initData) {
    let initCenter = L.latLng(35.658611, 139.745556); // If nothing else is said explicitly, we're probably talking about the Tokyo Tower
    let initZoom = 15;
    if (initData['center']) {
        initCenter = L.latLng(initData['center']['lat'], initData['center']['lng']);
    }
    if (initData['zoom']) {
        initZoom = initData['zoom'];
    }
    leaflet.setView(initCenter, initZoom);
};

let setOutline = function (leaflet, initData) {
    if (initData['outline'] && initData['limit']) {
        let polygon = getPolygonCoveringExterior(initData['limit'], initData['outline']);
        let polygonLayer = L.polygon(polygon, {
            weight: 1,
            fillColor: "#ffffff",
            fillOpacity: 0.75,
            stroke: true,
            color: '#0000ff',
            opacity: 0.5,
            dashArray: [2, 4]
        });
        leaflet.addLayer(polygonLayer);
    }
};

let setBounds = function (leaflet, initData) {
    if (initData['limit']) {
        let bounds = L.latLngBounds(
            L.latLng(initData['limit']['min']['lat'], initData['limit']['min']['lng']),
            L.latLng(initData['limit']['max']['lat'], initData['limit']['max']['lng'])
        );
        leaflet.setMaxBounds(bounds);
    }
};

let setZoomBehavior = function (leaflet) {
    leaflet.addHandler('cooperativezoom', CooperativeScrollWheelZoom);
    leaflet.cooperativezoom.enable();
};

export default function ($map_element, initData, noTouchDrag) {
    // noTouchDrag: Dragging is disabled by default.
    // For mouse users, it is enabled once the first mouse-typical event occurs
    // For touch users, it is enabled as long as the map has the focus
    // This behavior is enabled for maps that are shown by default. Drop-down-maps keep leaflet's default behavior.

    let leaflet = L.map($map_element.attr("id"), {
        maxBoundsViscosity: 1,
        minZoom: (initData['zoom'] < 12 ? initData['zoom'] : 12),
        maxZoom: 19,
        scrollWheelZoom: !(noTouchDrag === true),
        dragging: !(noTouchDrag === true),
    });

    setTiles(leaflet, initData);
    setInitView(leaflet, initData);
    setBounds(leaflet, initData);
    setOutline(leaflet, initData);

    if (noTouchDrag === true) {
        setZoomBehavior(leaflet);

        let mouseMode = false;

        $map_element.one("mousedown mousemove", (ev) => {
            if (ev.originalEvent.sourceCapabilities && ev.originalEvent.sourceCapabilities.firesTouchEvents === true) {
                // Ghost event triggered by touching the map
                return;
            }
            leaflet.dragging.enable();
            mouseMode = true;
        });
        $map_element.on("focus", () => {
            if (!mouseMode) {
                leaflet.dragging.enable();
            }
        });
        $map_element.on("blur", () => {
            if (!mouseMode) {
                leaflet.dragging.disable();
            }
        });
    }

    return leaflet;
}
<|endoftext|>


<commit_before>if(typeof sne == "undefined") sne = { steps: {} };
sne.steps.login = {};

(function ()
{
  var $loginFields = $('#loginFields');
  var $registerFields = $('#registerFields');
  var loginCallback = null;

  sne.steps.login.show = function (cb)
  {
    var reg = shownoteseditor.connectors[sne.connectorName].registration;
    loginCallback = cb;

    if(!reg.needsRegistration)
      return loginCallback();

    fillForms(reg);
  };

  sne.steps.login.hide = function ()
  {
    $('#remove').removeClass('active');
    loginCallback();
  };

  function fillForms(reg)
  {
    shownoteseditor.connectors[sne.connectorName].login(
      sne.connectorOptions,
      function (err)
      {
        if (!err)
          return loginCallback();

        for (var i = 0; i < reg.loginFields.length; i++)
        {
          addField($loginFields, reg.loginFields[i]);
        }

        for (var i = 0; i < reg.registerFields.length; i++)
        {
          addField($registerFields, reg.registerFields[i])
        }

        $('#login').addClass('active');
      }
    );

    function addField($parent, field)
    {
      var $field = $('
<input>
');
      $field.find('span').text(field + ": ");
      $field.find('input').attr('data-name', field);
      $parent.append($field);
    }
  }

  $('#loginSubmit').click(
    function ()
    {
      var reg = shownoteseditor.connectors[sne.connectorName].registration;
      sne.connectorOptions.auth = {};

      for (var i = 0; i < reg.loginFields.length; i++) {
        var field = reg.loginFields[i];
        var val = $loginFields.find('input[data-name=' + field + ']').val();
        sne.connectorOptions.auth[field] = val;
      }

      shownoteseditor.connectors[sne.connectorName].login(
        sne.connectorOptions,
        function (err)
        {
          if(err)
          {
            $('#loginError').text(err);
          }
          else
          {
            sne.steps.login.hide();
          }
        }
      );
    }
  );

  $('#registerSubmit').click(
    function ()
    {
      alert("todo");
    }
  )
})();
<commit_msg>Hide login when it was successfull<commit_after>if(typeof sne == "undefined") sne = { steps: {} };
sne.steps.login = {};

(function ()
{
  var $loginFields = $('#loginFields');
  var $registerFields = $('#registerFields');
  var loginCallback = null;

  sne.steps.login.show = function (cb)
  {
    var reg = shownoteseditor.connectors[sne.connectorName].registration;
    loginCallback = cb;

    if(!reg.needsRegistration)
      return loginCallback();

    fillForms(reg);
  };

  sne.steps.login.hide = function ()
  {
    $('#login').removeClass('active');
    loginCallback();
  };

  function fillForms(reg)
  {
    shownoteseditor.connectors[sne.connectorName].login(
      sne.connectorOptions,
      function (err)
      {
        if (!err)
          return loginCallback();

        for (var i = 0; i < reg.loginFields.length; i++)
        {
          addField($loginFields, reg.loginFields[i]);
        }

        for (var i = 0; i < reg.registerFields.length; i++)
        {
          addField($registerFields, reg.registerFields[i])
        }

        $('#login').addClass('active');
      }
    );

    function addField($parent, field)
    {
      var $field = $('
<input>
');
      $field.find('span').text(field + ": ");
      $field.find('input').attr('data-name', field);
      $parent.append($field);
    }
  }

  $('#loginSubmit').click(
    function ()
    {
      var reg = shownoteseditor.connectors[sne.connectorName].registration;
      sne.connectorOptions.auth = {};

      for (var i = 0; i < reg.loginFields.length; i++) {
        var field = reg.loginFields[i];
        var val = $loginFields.find('input[data-name=' + field + ']').val();
        sne.connectorOptions.auth[field] = val;
      }

      shownoteseditor.connectors[sne.connectorName].login(
        sne.connectorOptions,
        function (err)
        {
          if(err)
          {
            $('#loginError').text(err);
          }
          else
          {
            sne.steps.login.hide();
          }
        }
      );
    }
  );

  $('#registerSubmit').click(
    function ()
    {
      alert("todo");
    }
  )
})();
<|endoftext|>


<commit_before>import { inject as service } from '@ember/service';
import Route from 'consul-ui/routing/route';

// TODO: We should potentially move all these nspace related things
// up a level to application.js

export default class DcRoute extends Route {
  @service('repository/permission') permissionsRepo;

  async model(params) {
    // When disabled nspaces is [], so nspace is undefined
    const permissions = await this.permissionsRepo.findAll({
      dc: params.dc,
      ns: this.optionalParams().nspace,
    });
    // the model here is actually required for the entire application
    // but we need to wait until we are in this route so we know what the dc
    // and or nspace is if the below changes please revisit the comments
    // in routes/application:model
    // We do this here instead of in setupController to prevent timing issues
    // in lower routes
    this.controllerFor('application').setProperties({
      permissions,
    });
    return {
      permissions,
    };
  }
}
<commit_msg>ui: Ensure we request permission wiht the correct partition (#11060)<commit_after>import { inject as service } from '@ember/service';
import Route from 'consul-ui/routing/route';

// TODO: We should potentially move all these nspace related things
// up a level to application.js

export default class DcRoute extends Route {
  @service('repository/permission') permissionsRepo;

  async model(params) {
    // When disabled nspaces is [], so nspace is undefined
    const permissions = await this.permissionsRepo.findAll({
      dc: params.dc,
      ns: this.optionalParams().nspace,
      partition: this.optionalParams().partition,
    });
    // the model here is actually required for the entire application
    // but we need to wait until we are in this route so we know what the dc
    // and or nspace is if the below changes please revisit the comments
    // in routes/application:model
    // We do this here instead of in setupController to prevent timing issues
    // in lower routes
    this.controllerFor('application').setProperties({
      permissions,
    });
    return {
      permissions,
    };
  }
}
<|endoftext|>


<commit_before>'use strict';

// TODO: is there a different ability if the shield duty action isn't used properly?
// TODO: is there an ability from Raiden (the bird) if you get eaten?
// TODO: maybe chain lightning warning if you get hit while you have system shock (8B8)

let noOrb = (str) => {
  return {
    en: str + ' (no orb)',
    fr: str + ' (pas d\'orbe)',
  };
};

[{
  zoneRegex: {
    en: /^Eden's Verse: Fulmination \(Savage\)$/,
    ko: /^희망의 낙원 에덴: 공명편\(영웅\) \(1\)$/,
  },
  damageWarn: {
    'Impact': '4E3B', // Stratospear landing AoE
    'Gallop': '4BB4', // Sideways add charge
    'Shock Strike': '4BC1', // Small AoE circles during Thunderstorm
    'Stepped Leader Twister': '4BC7', // Twister stepped leader
    'Stepped Leader Donut': '4BC8', // Donut stepped leader
    'Shock': '4E3D', // Hated of Levin Stormcloud-cleansable exploding debuff
  },
  damageFail: {
    'Judgment Jolt': '4BA7', // Stratospear explosions
  },
  triggers: [
    {
      // Helper for orb pickup failures
      id: 'E5S Orb Tracking',
      gainsEffectRegex: gLang.kEffect.SurgeProtection,
      losesEffectRegex: gLang.kEffect.SurgeProtection,
      run: function(e, data) {
        data.hasOrb = data.hasOrb || {};
        data.hasOrb[e.targetName] = e.gains;
      },
    },
    {
      id: 'E5S Divine Judgement Volts',
      damageRegex: '4BB7',
      condition: function(e, data) {
        return !data.hasOrb[e.targetName];
      },
      mistake: function(e) {
        return { type: 'fail', blame: e.targetName, text: noOrb(e.abilityName) };
      },
    },
    {
      // Large AoE circles during Thunderstorm
      id: 'E5S Volt Strike Double',
      damageRegex: '4BC3',
      condition: function(e, data) {
        // Double taps only.
        return e.type != '15';
      },
      mistake: function(e, data) {
        return { type: 'warn', blame: e.targetName, text: e.abilityName };
      },
    },
    {
      id: 'E5S Volt Strike Orb',
      damageRegex: '4BC3',
      condition: function(e, data) {
        return !data.hasOrb[e.targetName];
      },
      mistake: function(e) {
        return { type: 'fail', blame: e.targetName, text: noOrb(e.abilityName) };
      },
    },
    {
      id: 'E5S Deadly Discharge Big Knockback',
      damageRegex: '4BB2',
      condition: function(e, data) {
        return !data.hasOrb[e.targetName];
      },
      mistake: function(e) {
        return { type: 'fail', blame: e.targetName, text: noOrb(e.abilityName) };
      },
    },
    {
      id: 'E5S Crippling Blow',
      damageRegex: '4BCA',
      condition: function(e, data) {
        // Double taps only.
        return e.type != '15';
      },
      mistake: function(e, data) {
        return { type: 'warn', blame: e.targetName, text: e.abilityName };
      },
    },
    {
      id: 'E5S Lightning Bolt',
      damageRegex: '4BB9',
      condition: function(e, data) {
        // Having a non-idempotent condition function is a bit <_<
        // Only consider lightning bolt damage if you have a debuff to clear.
        if (!data.hated || !data.hated[e.targetName])
          return true;

        delete data.hated[e.targetName];
        return false;
      },
      mistake: function(e, data) {
        return { type: 'warn', blame: e.targetName, text: e.abilityName };
      },
    },
    {
      id: 'E5S Hated of Levin',
      regex: Regexes.headMarker({ id: '00D2' }),
      run: function(e, data) {
        data.hated = data.hated || {};
        data.hated[e.targetName] = true;
      },
    },
    {
      id: 'E5S Stormcloud Target Tracking',
      regex: Regexes.headMarker({ id: '006E' }),
      run: function(e, data) {
        data.cloudMarkers = data.cloudMarkers || [];
        data.cloudMarkers.push(e.targetName);
      },
    },
    {
      // This ability is seen only if players stacked the clouds instead of spreading them.
      id: 'E5S The Parting Clouds',
      damageRegex: '4BBA',
      suppressSeconds: 30,
      mistake: function(e, data) {
        for (let m of data.cloudMarkers) {
          return {
            type: 'fail',
            blame: data.cloudMarkers[m],
            text: e.abilityName + '(clouds too close)',
          };
        }
      },
    },
    {
      id: 'E5S Stormcloud cleanup',
      regex: Regexes.headMarker({ id: '006E' }),
      // Stormclouds resolve well before this.
      delaySeconds: 30,
      run: function(e, data) {
        delete data.cloudMarkers;
        delete data.hated;
      },
    },
    {
      id: 'E5S Chain Lightning Double',
      damageRegex: '4BC5',
      condition: function(e, data) {
        // Double taps only.
        return e.type != '15';
      },
      mistake: function(e, data) {
        return { type: 'warn', blame: e.targetName, text: e.abilityName };
      },
    },
  ],
}];
<commit_msg>oopsy: be more robust to orb state in e5s<commit_after>'use strict';

// TODO: is there a different ability if the shield duty action isn't used properly?
// TODO: is there an ability from Raiden (the bird) if you get eaten?
// TODO: maybe chain lightning warning if you get hit while you have system shock (8B8)

let noOrb = (str) => {
  return {
    en: str + ' (no orb)',
    fr: str + ' (pas d\'orbe)',
  };
};

[{
  zoneRegex: {
    en: /^Eden's Verse: Fulmination \(Savage\)$/,
    ko: /^희망의 낙원 에덴: 공명편\(영웅\) \(1\)$/,
  },
  damageWarn: {
    'Impact': '4E3B', // Stratospear landing AoE
    'Gallop': '4BB4', // Sideways add charge
    'Shock Strike': '4BC1', // Small AoE circles during Thunderstorm
    'Stepped Leader Twister': '4BC7', // Twister stepped leader
    'Stepped Leader Donut': '4BC8', // Donut stepped leader
    'Shock': '4E3D', // Hated of Levin Stormcloud-cleansable exploding debuff
  },
  damageFail: {
    'Judgment Jolt': '4BA7', // Stratospear explosions
  },
  triggers: [
    {
      // Helper for orb pickup failures
      id: 'E5S Orb Tracking',
      gainsEffectRegex: gLang.kEffect.SurgeProtection,
      losesEffectRegex: gLang.kEffect.SurgeProtection,
      run: function(e, data) {
        data.hasOrb = data.hasOrb || {};
        data.hasOrb[e.targetName] = e.gains;
      },
    },
    {
      id: 'E5S Divine Judgement Volts',
      damageRegex: '4BB7',
      condition: function(e, data) {
        return !data.hasOrb || !data.hasOrb[e.targetName];
      },
      mistake: function(e) {
        return { type: 'fail', blame: e.targetName, text: noOrb(e.abilityName) };
      },
    },
    {
      // Large AoE circles during Thunderstorm
      id: 'E5S Volt Strike Double',
      damageRegex: '4BC3',
      condition: function(e, data) {
        // Double taps only.
        return e.type != '15';
      },
      mistake: function(e, data) {
        return { type: 'warn', blame: e.targetName, text: e.abilityName };
      },
    },
    {
      id: 'E5S Volt Strike Orb',
      damageRegex: '4BC3',
      condition: function(e, data) {
        return !data.hasOrb || !data.hasOrb[e.targetName];
      },
      mistake: function(e) {
        return { type: 'fail', blame: e.targetName, text: noOrb(e.abilityName) };
      },
    },
    {
      id: 'E5S Deadly Discharge Big Knockback',
      damageRegex: '4BB2',
      condition: function(e, data) {
        return !data.hasOrb || !data.hasOrb[e.targetName];
      },
      mistake: function(e) {
        return { type: 'fail', blame: e.targetName, text: noOrb(e.abilityName) };
      },
    },
    {
      id: 'E5S Crippling Blow',
      damageRegex: '4BCA',
      condition: function(e, data) {
        // Double taps only.
        return e.type != '15';
      },
      mistake: function(e, data) {
        return { type: 'warn', blame: e.targetName, text: e.abilityName };
      },
    },
    {
      id: 'E5S Lightning Bolt',
      damageRegex: '4BB9',
      condition: function(e, data) {
        // Having a non-idempotent condition function is a bit <_<
        // Only consider lightning bolt damage if you have a debuff to clear.
        if (!data.hated || !data.hated[e.targetName])
          return true;

        delete data.hated[e.targetName];
        return false;
      },
      mistake: function(e, data) {
        return { type: 'warn', blame: e.targetName, text: e.abilityName };
      },
    },
    {
      id: 'E5S Hated of Levin',
      regex: Regexes.headMarker({ id: '00D2' }),
      run: function(e, data) {
        data.hated = data.hated || {};
        data.hated[e.targetName] = true;
      },
    },
    {
      id: 'E5S Stormcloud Target Tracking',
      regex: Regexes.headMarker({ id: '006E' }),
      run: function(e, data) {
        data.cloudMarkers = data.cloudMarkers || [];
        data.cloudMarkers.push(e.targetName);
      },
    },
    {
      // This ability is seen only if players stacked the clouds instead of spreading them.
      id: 'E5S The Parting Clouds',
      damageRegex: '4BBA',
      suppressSeconds: 30,
      mistake: function(e, data) {
        for (let m of data.cloudMarkers) {
          return {
            type: 'fail',
            blame: data.cloudMarkers[m],
            text: e.abilityName + '(clouds too close)',
          };
        }
      },
    },
    {
      id: 'E5S Stormcloud cleanup',
      regex: Regexes.headMarker({ id: '006E' }),
      // Stormclouds resolve well before this.
      delaySeconds: 30,
      run: function(e, data) {
        delete data.cloudMarkers;
        delete data.hated;
      },
    },
    {
      id: 'E5S Chain Lightning Double',
      damageRegex: '4BC5',
      condition: function(e, data) {
        // Double taps only.
        return e.type != '15';
      },
      mistake: function(e, data) {
        return { type: 'warn', blame: e.targetName, text: e.abilityName };
      },
    },
  ],
}];
<|endoftext|>


<commit_before>import React, {Component} from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import classnames from 'classnames'
import isEmpty from 'lodash/isEmpty'

import {MultiGrid, ColumnSizer} from 'react-virtualized'
import moment from 'moment'

import {timeSeriesToTableGraph} from 'src/utils/timeSeriesToDygraph'
import {
  NULL_COLUMN_INDEX,
  NULL_ROW_INDEX,
  NULL_HOVER_TIME,
  TIME_FORMAT_DEFAULT,
  TIME_COLUMN_DEFAULT,
  FIX_FIRST_COLUMN_DEFAULT,
} from 'src/shared/constants/tableGraph'
import {generateThresholdsListHexs} from 'shared/constants/colorOperations'

class TableGraph extends Component {
  constructor(props) {
    super(props)
    this.state = {
      data: [[]],
      unzippedData: [[]],
      hoveredColumnIndex: NULL_COLUMN_INDEX,
      hoveredRowIndex: NULL_ROW_INDEX,
      sortByColumnIndex: -1,
    }
  }

  componentWillReceiveProps(nextProps) {
    const {data, unzippedData} = timeSeriesToTableGraph(nextProps.data)

    const {tableOptions: {sortBy: {internalName}}} = nextProps
    const sortByColumnIndex = _.indexOf(data[0], internalName)

    const sortedData = _.sortBy(_.drop(data, 1), sortByColumnIndex)
    this.setState({
      data: [data[0], ...sortedData],
      unzippedData,
      sortByColumnIndex,
    })
  }

  calcHoverTimeIndex = (data, hoverTime, verticalTimeAxis) => {
    if (isEmpty(data) || hoverTime === NULL_HOVER_TIME) {
      return undefined
    }
    if (verticalTimeAxis) {
      return data.findIndex(
        row => row[0] && _.isNumber(row[0]) && row[0] >= hoverTime
      )
    }
    return data[0].findIndex(d => _.isNumber(d) && d >= hoverTime)
  }

  handleHover = (columnIndex, rowIndex) => () => {
    const {onSetHoverTime, tableOptions: {verticalTimeAxis}} = this.props
    const data = verticalTimeAxis ? this.state.data : this.state.unzippedData
    if (onSetHoverTime) {
      const hoverTime = verticalTimeAxis
        ? data[rowIndex][0]
        : data[0][columnIndex]
      onSetHoverTime(hoverTime.toString())
    }
    this.setState({
      hoveredColumnIndex: columnIndex,
      hoveredRowIndex: rowIndex,
    })
  }

  handleMouseOut = () => {
    if (this.props.onSetHoverTime) {
      this.props.onSetHoverTime(NULL_HOVER_TIME)
      this.setState({
        hoveredColumnIndex: NULL_COLUMN_INDEX,
        hoveredRowIndex: NULL_ROW_INDEX,
      })
    }
  }

  cellRenderer = ({columnIndex, rowIndex, key, style, parent}) => {
    const {colors, tableOptions} = this.props
    const {hoveredColumnIndex, hoveredRowIndex} = this.state

    const data = _.get(tableOptions, 'verticalTimeAxis', true)
      ? this.state.data
      : this.state.unzippedData

    const columnCount = _.get(data, ['0', 'length'], 0)
    const rowCount = data.length
    const timeFormat = _.get(tableOptions, 'timeFormat', TIME_FORMAT_DEFAULT)
    const columnNames = _.get(tableOptions, 'columnNames', [
      TIME_COLUMN_DEFAULT,
    ])
    const fixFirstColumn = _.get(
      tableOptions,
      'fixFirstColumn',
      FIX_FIRST_COLUMN_DEFAULT
    )

    const isFixedRow = rowIndex === 0 && columnIndex > 0
    const isFixedColumn = fixFirstColumn && rowIndex > 0 && columnIndex === 0
    const isTimeData = tableOptions.verticalTimeAxis
      ? rowIndex > 0 && columnIndex === 0
      : isFixedRow
    const isFixedCorner = rowIndex === 0 && columnIndex === 0
    const isLastRow = rowIndex === rowCount - 1
    const isLastColumn = columnIndex === columnCount - 1
    const isHighlighted =
      rowIndex === parent.props.scrollToRow ||
      columnIndex === parent.props.scrollToColumn ||
      (rowIndex === hoveredRowIndex && hoveredRowIndex !== 0) ||
      (columnIndex === hoveredColumnIndex && hoveredColumnIndex !== 0)
    const dataIsNumerical = _.isNumber(data[rowIndex][columnIndex])

    let cellStyle = style

    if (!isFixedRow && !isFixedColumn && !isFixedCorner) {
      const {bgColor, textColor} = generateThresholdsListHexs(
        colors,
        data[rowIndex][columnIndex]
      )

      cellStyle = {
        ...style,
        backgroundColor: bgColor,
        color: textColor,
      }
    }

    const cellClass = classnames('table-graph-cell', {
      'table-graph-cell__fixed-row': isFixedRow,
      'table-graph-cell__fixed-column': isFixedColumn,
      'table-graph-cell__fixed-corner': isFixedCorner,
      'table-graph-cell__last-row': isLastRow,
      'table-graph-cell__last-column': isLastColumn,
      'table-graph-cell__highlight': isHighlighted,
      'table-graph-cell__numerical': dataIsNumerical,
    })

    const cellData = data[rowIndex][columnIndex]
    const foundColumn = columnNames.find(
      column => column.internalName === cellData
    )
    const columnName =
      foundColumn && (foundColumn.displayName || foundColumn.internalName)

    return (
      <div
        key={key}
        style={cellStyle}
        className={cellClass}
        onMouseOver={this.handleHover(columnIndex, rowIndex)}
      >
        {isTimeData
          ? `${moment(cellData).format(timeFormat)}`
          : columnName || `${cellData}`}
      </div>
    )
  }

  render() {
    const {sortByColumnIndex, hoveredColumnIndex, hoveredRowIndex} = this.state
    const {hoverTime, tableOptions, colors} = this.props

    const verticalTimeAxis = _.get(tableOptions, 'verticalTimeAxis', true)

    const data = verticalTimeAxis ? this.state.data : this.state.unzippedData

    const columnCount = _.get(data, ['0', 'length'], 0)
    const rowCount = data.length
    const COLUMN_MIN_WIDTH = 98
    const COLUMN_MAX_WIDTH = 500
    const ROW_HEIGHT = 30
    const tableWidth = this.gridContainer ? this.gridContainer.clientWidth : 0
    const tableHeight = this.gridContainer ? this.gridContainer.clientHeight : 0
    const hoverTimeIndex =
      hoveredRowIndex === NULL_ROW_INDEX
        ? this.calcHoverTimeIndex(data, hoverTime, verticalTimeAxis)
        : hoveredRowIndex
    const fixedColumnCount = tableOptions.fixFirstColumn ? 1 : undefined
    const hoveringThisTable = hoveredColumnIndex !== NULL_COLUMN_INDEX
    const scrollToRow =
      !hoveringThisTable && verticalTimeAxis ? hoverTimeIndex : undefined
    const scrollToColumn =
      !hoveringThisTable && !verticalTimeAxis ? hoverTimeIndex : undefined

    return (
      <div
        className="table-graph-container"
        ref={gridContainer => (this.gridContainer = gridContainer)}
        onMouseOut={this.handleMouseOut}
      >
        {!isEmpty(data) &&
          <ColumnSizer
            columnCount={columnCount}
            columnMaxWidth={COLUMN_MAX_WIDTH}
            columnMinWidth={COLUMN_MIN_WIDTH}
            width={tableWidth}
          >
            {({getColumnWidth, registerChild}) =>
              <MultiGrid
                ref={registerChild}
                columnCount={columnCount}
                columnWidth={getColumnWidth}
                rowCount={rowCount}
                rowHeight={ROW_HEIGHT}
                height={tableHeight}
                width={tableWidth}
                fixedColumnCount={fixedColumnCount}
                fixedRowCount={1}
                enableFixedColumnScroll={true}
                enableFixedRowScroll={true}
                timeFormat={
                  tableOptions ? tableOptions.timeFormat : TIME_FORMAT_DEFAULT
                }
                columnNames={
                  tableOptions
                    ? tableOptions.columnNames
                    : [TIME_COLUMN_DEFAULT]
                }
                scrollToRow={scrollToRow}
                scrollToColumn={scrollToColumn}
                verticalTimeAxis={verticalTimeAxis}
                sortByColumnIndex={sortByColumnIndex}
                cellRenderer={this.cellRenderer}
                hoveredColumnIndex={hoveredColumnIndex}
                hoveredRowIndex={hoveredRowIndex}
                hoverTime={hoverTime}
                colors={colors}
                classNameBottomRightGrid="table-graph--scroll-window"
              />}
          </ColumnSizer>}
      </div>
    )
  }
}

const {arrayOf, number, shape, string, func} = PropTypes

TableGraph.propTypes = {
  cellHeight: number,
  data: arrayOf(shape()),
  tableOptions: shape({}),
  hoverTime: string,
  onSetHoverTime: func,
  colors: arrayOf(
    shape({
      type: string.isRequired,
      hex: string.isRequired,
      id: string.isRequired,
      name: string.isRequired,
      value: string.isRequired,
    }).isRequired
  ),
}

export default TableGraph
<commit_msg>Display complete cell contents on hover<commit_after>import React, {Component} from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import classnames from 'classnames'
import isEmpty from 'lodash/isEmpty'

import {MultiGrid, ColumnSizer} from 'react-virtualized'
import moment from 'moment'

import {timeSeriesToTableGraph} from 'src/utils/timeSeriesToDygraph'
import {
  NULL_COLUMN_INDEX,
  NULL_ROW_INDEX,
  NULL_HOVER_TIME,
  TIME_FORMAT_DEFAULT,
  TIME_COLUMN_DEFAULT,
  FIX_FIRST_COLUMN_DEFAULT,
} from 'src/shared/constants/tableGraph'
import {generateThresholdsListHexs} from 'shared/constants/colorOperations'

class TableGraph extends Component {
  constructor(props) {
    super(props)
    this.state = {
      data: [[]],
      unzippedData: [[]],
      hoveredColumnIndex: NULL_COLUMN_INDEX,
      hoveredRowIndex: NULL_ROW_INDEX,
      sortByColumnIndex: -1,
    }
  }

  componentWillReceiveProps(nextProps) {
    const {data, unzippedData} = timeSeriesToTableGraph(nextProps.data)

    const {tableOptions: {sortBy: {internalName}}} = nextProps
    const sortByColumnIndex = _.indexOf(data[0], internalName)

    const sortedData = _.sortBy(_.drop(data, 1), sortByColumnIndex)
    this.setState({
      data: [data[0], ...sortedData],
      unzippedData,
      sortByColumnIndex,
    })
  }

  calcHoverTimeIndex = (data, hoverTime, verticalTimeAxis) => {
    if (isEmpty(data) || hoverTime === NULL_HOVER_TIME) {
      return undefined
    }
    if (verticalTimeAxis) {
      return data.findIndex(
        row => row[0] && _.isNumber(row[0]) && row[0] >= hoverTime
      )
    }
    return data[0].findIndex(d => _.isNumber(d) && d >= hoverTime)
  }

  handleHover = (columnIndex, rowIndex) => () => {
    const {onSetHoverTime, tableOptions: {verticalTimeAxis}} = this.props
    const data = verticalTimeAxis ? this.state.data : this.state.unzippedData
    if (onSetHoverTime) {
      const hoverTime = verticalTimeAxis
        ? data[rowIndex][0]
        : data[0][columnIndex]
      onSetHoverTime(hoverTime.toString())
    }
    this.setState({
      hoveredColumnIndex: columnIndex,
      hoveredRowIndex: rowIndex,
    })
  }

  handleMouseOut = () => {
    if (this.props.onSetHoverTime) {
      this.props.onSetHoverTime(NULL_HOVER_TIME)
      this.setState({
        hoveredColumnIndex: NULL_COLUMN_INDEX,
        hoveredRowIndex: NULL_ROW_INDEX,
      })
    }
  }

  cellRenderer = ({columnIndex, rowIndex, key, style, parent}) => {
    const {colors, tableOptions} = this.props
    const {hoveredColumnIndex, hoveredRowIndex} = this.state

    const data = _.get(tableOptions, 'verticalTimeAxis', true)
      ? this.state.data
      : this.state.unzippedData

    const columnCount = _.get(data, ['0', 'length'], 0)
    const rowCount = data.length
    const timeFormat = _.get(tableOptions, 'timeFormat', TIME_FORMAT_DEFAULT)
    const columnNames = _.get(tableOptions, 'columnNames', [
      TIME_COLUMN_DEFAULT,
    ])
    const fixFirstColumn = _.get(
      tableOptions,
      'fixFirstColumn',
      FIX_FIRST_COLUMN_DEFAULT
    )

    const isFixedRow = rowIndex === 0 && columnIndex > 0
    const isFixedColumn = fixFirstColumn && rowIndex > 0 && columnIndex === 0
    const isTimeData = tableOptions.verticalTimeAxis
      ? rowIndex > 0 && columnIndex === 0
      : isFixedRow
    const isFixedCorner = rowIndex === 0 && columnIndex === 0
    const isLastRow = rowIndex === rowCount - 1
    const isLastColumn = columnIndex === columnCount - 1
    const isHighlighted =
      rowIndex === parent.props.scrollToRow ||
      columnIndex === parent.props.scrollToColumn ||
      (rowIndex === hoveredRowIndex && hoveredRowIndex !== 0) ||
      (columnIndex === hoveredColumnIndex && hoveredColumnIndex !== 0)
    const dataIsNumerical = _.isNumber(data[rowIndex][columnIndex])

    let cellStyle = style

    if (!isFixedRow && !isFixedColumn && !isFixedCorner) {
      const {bgColor, textColor} = generateThresholdsListHexs(
        colors,
        data[rowIndex][columnIndex]
      )

      cellStyle = {
        ...style,
        backgroundColor: bgColor,
        color: textColor,
      }
    }

    const cellClass = classnames('table-graph-cell', {
      'table-graph-cell__fixed-row': isFixedRow,
      'table-graph-cell__fixed-column': isFixedColumn,
      'table-graph-cell__fixed-corner': isFixedCorner,
      'table-graph-cell__last-row': isLastRow,
      'table-graph-cell__last-column': isLastColumn,
      'table-graph-cell__highlight': isHighlighted,
      'table-graph-cell__numerical': dataIsNumerical,
    })

    const cellData = data[rowIndex][columnIndex]
    const foundColumn = columnNames.find(
      column => column.internalName === cellData
    )
    const columnName =
      foundColumn && (foundColumn.displayName || foundColumn.internalName)

    const cellContents = isTimeData
      ? `${moment(cellData).format(timeFormat)}`
      : columnName || `${cellData}`

    return (
      <div
        key={key}
        style={cellStyle}
        className={cellClass}
        onMouseOver={this.handleHover(columnIndex, rowIndex)}
        title={cellContents}
        alt={cellContents}
      >
        {cellContents}
      </div>
    )
  }

  render() {
    const {sortByColumnIndex, hoveredColumnIndex, hoveredRowIndex} = this.state
    const {hoverTime, tableOptions, colors} = this.props

    const verticalTimeAxis = _.get(tableOptions, 'verticalTimeAxis', true)

    const data = verticalTimeAxis ? this.state.data : this.state.unzippedData

    const columnCount = _.get(data, ['0', 'length'], 0)
    const rowCount = data.length
    const COLUMN_MIN_WIDTH = 98
    const COLUMN_MAX_WIDTH = 500
    const ROW_HEIGHT = 30
    const tableWidth = this.gridContainer ? this.gridContainer.clientWidth : 0
    const tableHeight = this.gridContainer ? this.gridContainer.clientHeight : 0
    const hoverTimeIndex =
      hoveredRowIndex === NULL_ROW_INDEX
        ? this.calcHoverTimeIndex(data, hoverTime, verticalTimeAxis)
        : hoveredRowIndex
    const fixedColumnCount = tableOptions.fixFirstColumn ? 1 : undefined
    const hoveringThisTable = hoveredColumnIndex !== NULL_COLUMN_INDEX
    const scrollToRow =
      !hoveringThisTable && verticalTimeAxis ? hoverTimeIndex : undefined
    const scrollToColumn =
      !hoveringThisTable && !verticalTimeAxis ? hoverTimeIndex : undefined

    return (
      <div
        className="table-graph-container"
        ref={gridContainer => (this.gridContainer = gridContainer)}
        onMouseOut={this.handleMouseOut}
      >
        {!isEmpty(data) &&
          <ColumnSizer
            columnCount={columnCount}
            columnMaxWidth={COLUMN_MAX_WIDTH}
            columnMinWidth={COLUMN_MIN_WIDTH}
            width={tableWidth}
          >
            {({getColumnWidth, registerChild}) =>
              <MultiGrid
                ref={registerChild}
                columnCount={columnCount}
                columnWidth={getColumnWidth}
                rowCount={rowCount}
                rowHeight={ROW_HEIGHT}
                height={tableHeight}
                width={tableWidth}
                fixedColumnCount={fixedColumnCount}
                fixedRowCount={1}
                enableFixedColumnScroll={true}
                enableFixedRowScroll={true}
                timeFormat={
                  tableOptions ? tableOptions.timeFormat : TIME_FORMAT_DEFAULT
                }
                columnNames={
                  tableOptions
                    ? tableOptions.columnNames
                    : [TIME_COLUMN_DEFAULT]
                }
                scrollToRow={scrollToRow}
                scrollToColumn={scrollToColumn}
                verticalTimeAxis={verticalTimeAxis}
                sortByColumnIndex={sortByColumnIndex}
                cellRenderer={this.cellRenderer}
                hoveredColumnIndex={hoveredColumnIndex}
                hoveredRowIndex={hoveredRowIndex}
                hoverTime={hoverTime}
                colors={colors}
                classNameBottomRightGrid="table-graph--scroll-window"
              />}
          </ColumnSizer>}
      </div>
    )
  }
}

const {arrayOf, number, shape, string, func} = PropTypes

TableGraph.propTypes = {
  cellHeight: number,
  data: arrayOf(shape()),
  tableOptions: shape({}),
  hoverTime: string,
  onSetHoverTime: func,
  colors: arrayOf(
    shape({
      type: string.isRequired,
      hex: string.isRequired,
      id: string.isRequired,
      name: string.isRequired,
      value: string.isRequired,
    }).isRequired
  ),
}

export default TableGraph
<|endoftext|>