<commit_before>// ==UserScript==
// @name         SlitherAI - Bot
// @namespace    SlitherAI
// @version      0.1.1
// @description  Controlling all the snakes!
// @author       Alberto Sola
// @match        http://slither.io/
// @grant        none
// ==/UserScript==

// ----------------------------------------------------------------------------------

//var ENABLE_BOT_KEY = 83;     // 'S' - ASCII
//var ENABLE_VISUAL_INFO_KEY;

// ----------------------------------------------------------------------------------

var UPDATE_INTERVAL = 60;

// ----------------------------------------------------------------------------------

var bot_name = "Slither.AI";
var bot_enabled = true;
var mouse_enabled = true;

// ----------------------------------------------------------------------------------

var direction = 0;

var objective_offset = 40;
var direction_offset = 20;

var goto_x = 0;
var goto_y = 0;
var has_objective = false;

var enemy_radius = 150;
var near_snakes = [];

var scape_mode = false;

// ----------------------------------------------------------------------------------

var game_redraw = null;
var capture_mouse = null;

// ----------------------------------------------------------------------------------

function disableDrawing(){
    window.redraw = function(){};
}

// ----------------------------------------------------------------------------------

function disableMouse(){
    window.onmousemove = function(){};
}

function enableMouse(){
    window.onmousemove = capture_mouse;
}

// ----------------------------------------------------------------------------------

function startGame(){
    /*
        Start the game.
    */
    document.getElementById("playh").children[0].click();
    bot_enabled = true;
    console.log("STARTING");

    // Wait until the conexion is stablished
    setTimeout(update,1000);
}

// ----------------------------------------------------------------------------------

function setNick(nick){
    /*
        Set the bot nick in the input field.
    */
    var nick_input = document.getElementById("nick");
    nick_input.setAttribute("value", nick);
}

// ----------------------------------------------------------------------------------

function sendData(data){
    /*
        Send data to the server.

        TODO: Search how it works.
    */
    var packet = new Uint8Array(1);
    packet[0] = data;
    ws.send(packet);
}

// ----------------------------------------------------------------------------------

function getDistance(a,b,c,d){
    /*
        Calculate the distance between (a,b) and (c,d).
    */
    var x = a - c;
    var y = b - d;

    return Math.sqrt(x*x+y*y);
}

function radToGameAngle(angle){
    var new_direction;

    if(angle > 0)
        new_direction = (1 - angle/Math.PI) * 125 + 125;
    else
        new_direction = -angle/Math.PI * 125;

    return Math.round(new_direction);
}

function getDirectionScape(){
    /*
        Go to the oposite direction if there are near snakes.

        [WIP]

        TODO: Sometimes fails.
    */
    var angles = [];
    var min_angle;
    var max_angle;

    var i;
    for(i = 0; i < near_snakes.length; i++){
        angles.push(getDirection(near_snakes[i].x,near_snakes[i].y));
    }

    min_angle = max_angle = angles[0];

    for(j = 0; i < angles.length; i++){
        if(angles[i] > max_angle)
            max_angle = angles[i];
        else if(angles[i] < min_angle)
            min_angle = angles[i];

    }

    return (125 + (max_angle+min_angle)/2)%250;
}

function getDirection(x,y){
    /*
        Calculate the direction [0,250] to go to the current objective.
    */
    var my_x = snake.xx;
    var my_y = snake.yy;

    var moved_x = x - my_x;
    var moved_y = - y + my_y;

    var angle = Math.atan2(moved_y,moved_x);

    return radToGameAngle(angle);
}

// ----------------------------------------------------------------------------------

function get_next_food(){
    /*
        Set the coordinates of the next objective.

        TODO: Check if there isn't any snake over it or if it's behind a snake.
    */
    var my_x = snake.xx;
    var my_y = snake.yy;
    var food_x = null;
    var food_y = null;
    var small_distance;
    var next_food_x;
    var next_food_y;
    var next_distance;

    // TODO: new angle -> the less the better
    // TODO: new angle -> don't go to danger zone
    if(foods.length > 0){
        food_x = foods[0].xx;
        food_y = foods[0].yy;
        small_distance = foods[0].sz*foods[0].sz / getDistance(my_x,my_y,food_x,food_y);
    }

    for(var i = 1; i < foods.length; i++){
        if( foods[i] !== null && goto_x != foods[i].xx && goto_y != foods[i].yy){
            next_food_x = foods[i].xx;
            next_food_y = foods[i].yy;
            next_distance = foods[i].sz*foods[i].sz / (getDistance(my_x,my_y,next_food_x,next_food_y) + Math.abs(direction - getDirection(my_x,my_y,next_food_x,next_food_y)));

            if(next_distance > small_distance){
                small_distance = next_distance;
                food_x = next_food_x;
                food_y = next_food_y;
            }

        }
    }

    goto_x = food_x;
    goto_y = food_y;
}

// ----------------------------------------------------------------------------------

function objectiveEaten(){
    /*
        Check if the current objective has been eaten.
    */
    var eaten = false;

    for(var i = 0; i < foods.length && !eaten; i++){
        if( foods[i] !== null && foods[i].xx == goto_x && foods[i].yy == goto_y )
            eaten = true;
    }

    return !eaten;
}

function update(){
    /*
        Main function.
    */
    if(ws === null){
        bot_enabled = false;
        console.log("GAME OVER");
    }

    if(bot_enabled){

        var my_x = snake.xx;
        var my_y = snake.yy;

        if( Math.abs(Math.abs(my_x) - Math.abs(goto_x)) < objective_offset && Math.abs(Math.abs(my_y) - Math.abs(goto_y)) < objective_offset )
            has_objective = false;

        if( Math.abs(direction - 62) < direction_offset) // 62 = 250/4 = 90º
            has_objective = false;

        if(objectiveEaten())
            has_objective = false;

        searchSnakes();

        if(scape_mode){
            direction = getDirectionScape();
        }
        else{
            if(!has_objective){
                get_next_food();
                has_objective = true;
            }
            direction = getDirection(goto_x,goto_y);

        }

        //console.log("DIRECTION:" + direction);
        sendData(direction);
        draw();

        setTimeout(update,UPDATE_INTERVAL);
    }
}

function drawLine(ctx,start_x,start_y,end_x,end_y){
    /*
        Draw a line in a canvas context.
    */
    ctx.moveTo(start_x,start_y);
    ctx.lineTo(end_x,end_y);
    ctx.stroke();
}

function searchSnakes(){
    /*
        Search for points inside an area (enemy_radius).
    */
    near_snakes = [];
    // TODO: Check null elements
    for(var i = 0; i < snakes.length - 1; i++){ // Our snake is the last one
        for(var j = 0; j < snakes[i].pts.length; j++){ // There are some hidden points -> "dying = true"
            if(!snakes[i].pts[j].dying && getDistance(snake.xx,snake.yy,snakes[i].pts[j].xx,snakes[i].pts[j].yy) <= enemy_radius)
                near_snakes.push({x:snakes[i].pts[j].xx,y:snakes[i].pts[j].yy});
        }
    }

    if(near_snakes.length > 0 )
        scape_mode = true;
    else
        scape_mode = false;

}

function draw(){
    /*
        Render the game and the aditional bot info.
    */
    game_redraw();

    var context = window.mc.getContext('2d');
    var mid_x = window.mc.width/2;
    var mid_y = window.mc.height/2;
    var my_x = snake.xx;
    var my_y = snake.yy;
    var obj_x = Math.round(goto_x - my_x + mid_x);
    var obj_y = Math.round(goto_y - my_y + mid_y);

    context.save();
    //context.globalAlpha = 1;
    context.beginPath();
    context.rect(obj_x, obj_y, 10,10);

    context.strokeStyle = "black";
    context.fillStyle = "white";
    context.stroke();
    context.fill();

    context.beginPath();
    context.lineWidth = 2;
    context.strokeStyle = "white";
    drawLine(context,mid_x,mid_y,obj_x+5,obj_y+5);

    context.beginPath();
    context.strokeStyle = "red";
    context.fillStyle = "red";

    context.arc(mid_x,mid_y,enemy_radius,0,2*Math.PI);
    context.stroke();

    for(var i = 0; i < near_snakes.length; i++){
        context.beginPath();
        context.strokeStyle = "red";
        context.fillStyle = "red";
        obj_x = Math.round(near_snakes[i].x - my_x + mid_x);
        obj_y = Math.round(near_snakes[i].y - my_y + mid_y);

        context.rect(obj_x, obj_y, 10,10);
        context.stroke();
        context.fill();

        context.lineWidth = 2;
        context.strokeStyle = "red";
        drawLine(context,mid_x,mid_y,obj_x+5,obj_y+5);
    }

    context.restore();
}

// ----------------------------------------------------------------------------------
// MAIN
// ----------------------------------------------------------------------------------

function initialize(){
    // Save game functions
    game_redraw = window.redraw;
    capture_mouse = window.onmousemove;

    // Update game function
    //disableMouse();
    disableDrawing();

    // Update data
    setNick(bot_name);

    startGame();
}

(function() {
    'use strict';

    // TODO: Detect page is loaded
    // TODO: Override play button click event.
    setTimeout(initialize,1000);

})();
<commit_msg>Better accuracy when drawing points.<commit_after>// ==UserScript==
// @name         SlitherAI - Bot
// @namespace    SlitherAI
// @version      0.1.1
// @description  Controlling all the snakes!
// @author       Alberto Sola
// @match        http://slither.io/
// @grant        none
// ==/UserScript==

// ----------------------------------------------------------------------------------

//var ENABLE_BOT_KEY = 83;     // 'S' - ASCII
//var ENABLE_VISUAL_INFO_KEY;

// ----------------------------------------------------------------------------------

var UPDATE_INTERVAL = 60;

// ----------------------------------------------------------------------------------

var bot_name = "Slither.AI";
var bot_enabled = true;
var mouse_enabled = true;
var nick_input_id = "nick_holder";

// ----------------------------------------------------------------------------------

var direction = 0;

var objective_offset = 40;
var direction_offset = 20;

var goto_x = 0;
var goto_y = 0;
var has_objective = false;

var enemy_radius = 150;
var near_snakes = [];

var scape_mode = false;

// ----------------------------------------------------------------------------------

var game_redraw = null;
var capture_mouse = null;

// ----------------------------------------------------------------------------------

function disableDrawing(){
    window.redraw = function(){};
}

// ----------------------------------------------------------------------------------

function disableMouse(){
    window.onmousemove = function(){};
}

function enableMouse(){
    window.onmousemove = capture_mouse;
}

// ----------------------------------------------------------------------------------

function startGame(){
    /*
        Start the game.
    */
    document.getElementById("playh").children[0].click();
    bot_enabled = true;
    console.log("STARTING");

    // Wait until the conexion is stablished
    setTimeout(update,1000);
}

// ----------------------------------------------------------------------------------

function setNick(nick){
    /*
        Set the bot nick in the input field.
    */
    var nick_input = document.getElementById(nick_input_id);
    nick_input.setAttribute("value", nick);
}

// ----------------------------------------------------------------------------------

function sendData(data){
    /*
        Send data to the server.

        TODO: Search how it works.
    */
    var packet = new Uint8Array(1);
    packet[0] = data;
    ws.send(packet);
}

// ----------------------------------------------------------------------------------

function getDistance(a,b,c,d){
    /*
        Calculate the distance between (a,b) and (c,d).
    */
    var x = a - c;
    var y = b - d;

    return Math.sqrt(x*x+y*y);
}

function radToGameAngle(angle){
    var new_direction;

    if(angle > 0)
        new_direction = (1 - angle/Math.PI) * 125 + 125;
    else
        new_direction = -angle/Math.PI * 125;

    return Math.round(new_direction);
}

function getDirectionScape(){
    /*
        Go to the oposite direction if there are near snakes.

        [WIP]

        TODO: Sometimes fails.
    */
    var angles = [];
    var min_angle;
    var max_angle;

    var i;
    for(i = 0; i < near_snakes.length; i++){
        angles.push(getDirection(near_snakes[i].x,near_snakes[i].y));
    }

    min_angle = max_angle = angles[0];

    for(j = 0; i < angles.length; i++){
        if(angles[i] > max_angle)
            max_angle = angles[i];
        else if(angles[i] < min_angle)
            min_angle = angles[i];

    }

    return (125 + (max_angle+min_angle)/2)%250;
}

function getDirection(x,y){
    /*
        Calculate the direction [0,250] to go to the current objective.
    */
    var my_x = snake.xx;
    var my_y = snake.yy;

    var moved_x = x - my_x;
    var moved_y = - y + my_y;

    var angle = Math.atan2(moved_y,moved_x);

    return radToGameAngle(angle);
}

// ----------------------------------------------------------------------------------

function get_next_food(){
    /*
        Set the coordinates of the next objective.

        TODO: Check if there isn't any snake over it or if it's behind a snake.
    */
    var my_x = snake.xx;
    var my_y = snake.yy;
    var food_x = null;
    var food_y = null;
    var small_distance;
    var next_food_x;
    var next_food_y;
    var next_distance;

    // TODO: new angle -> the less the better
    // TODO: new angle -> don't go to danger zone
    if(foods.length > 0){
        food_x = foods[0].xx;
        food_y = foods[0].yy;
        small_distance = foods[0].sz*foods[0].sz / getDistance(my_x,my_y,food_x,food_y);
    }

    for(var i = 1; i < foods.length; i++){
        if( foods[i] !== null && goto_x != foods[i].xx && goto_y != foods[i].yy){
            next_food_x = foods[i].xx;
            next_food_y = foods[i].yy;
            next_distance = foods[i].sz*foods[i].sz / (getDistance(my_x,my_y,next_food_x,next_food_y) + Math.abs(direction - getDirection(my_x,my_y,next_food_x,next_food_y)));

            if(next_distance > small_distance){
                small_distance = next_distance;
                food_x = next_food_x;
                food_y = next_food_y;
            }

        }
    }

    goto_x = food_x;
    goto_y = food_y;
}

// ----------------------------------------------------------------------------------

function objectiveEaten(){
    /*
        Check if the current objective has been eaten.
    */
    var eaten = false;

    for(var i = 0; i < foods.length && !eaten; i++){
        if( foods[i] !== null && foods[i].xx == goto_x && foods[i].yy == goto_y )
            eaten = true;
    }

    return !eaten;
}

function update(){
    /*
        Main function.
    */
    if(ws === null){
        bot_enabled = false;
        console.log("GAME OVER");
    }

    if(bot_enabled){

        var my_x = snake.xx;
        var my_y = snake.yy;

        if( Math.abs(Math.abs(my_x) - Math.abs(goto_x)) < objective_offset && Math.abs(Math.abs(my_y) - Math.abs(goto_y)) < objective_offset )
            has_objective = false;

        if( Math.abs(direction - 62) < direction_offset) // 62 = 250/4 = 90º
            has_objective = false;

        if(objectiveEaten())
            has_objective = false;

        searchSnakes();

        if(scape_mode){
            direction = getDirectionScape();
        }
        else{
            if(!has_objective){
                get_next_food();
                has_objective = true;
            }
            direction = getDirection(goto_x,goto_y);

        }

        //console.log("DIRECTION:" + direction);
        sendData(direction);
        draw();

        setTimeout(update,UPDATE_INTERVAL);
    }
}

function drawLine(ctx,start_x,start_y,end_x,end_y){
    /*
        Draw a line in a canvas context.
    */
    ctx.moveTo(start_x,start_y);
    ctx.lineTo(end_x,end_y);
    ctx.stroke();
}

function searchSnakes(){
    /*
        Search for points inside an area (enemy_radius).
    */
    near_snakes = [];
    // TODO: Check null elements
    for(var i = 0; i < snakes.length - 1; i++){ // Our snake is the last one
        for(var j = 0; j < snakes[i].pts.length; j++){ // There are some hidden points -> "dying = true"
            if(!snakes[i].pts[j].dying && getDistance(snake.xx,snake.yy,snakes[i].pts[j].xx,snakes[i].pts[j].yy) <= enemy_radius)
                near_snakes.push({x:snakes[i].pts[j].xx,y:snakes[i].pts[j].yy});
        }
    }

    if(near_snakes.length > 0 )
        scape_mode = true;
    else
        scape_mode = false;

}

function draw(){
    /*
        Render the game and the aditional bot info.
    */
    game_redraw();

    var context = window.mc.getContext('2d');
    var mid_x = window.mc.width/2;
    var mid_y = window.mc.height/2;
    var my_x = snake.xx;
    var my_y = snake.yy;
    var obj_x = Math.round((goto_x - my_x)*window.gsc + mid_x);
    var obj_y = Math.round((goto_y - my_y)*window.gsc + mid_y);

    context.save();
    //context.globalAlpha = 1;
    context.beginPath();
    context.rect(obj_x, obj_y, 10,10);

    context.strokeStyle = "black";
    context.fillStyle = "white";
    context.stroke();
    context.fill();

    context.beginPath();
    context.lineWidth = 2;
    context.strokeStyle = "white";
    drawLine(context,mid_x,mid_y,obj_x+5,obj_y+5);

    context.beginPath();
    context.strokeStyle = "red";
    context.fillStyle = "red";

    context.arc(mid_x,mid_y,enemy_radius,0,2*Math.PI);
    context.stroke();

    for(var i = 0; i < near_snakes.length; i++){
        context.beginPath();
        context.strokeStyle = "red";
        context.fillStyle = "red";
        obj_x = Math.round((near_snakes[i].x - my_x)*window.gsc + mid_x);
        obj_y = Math.round((near_snakes[i].y - my_y)*window.gsc + mid_y);

        context.rect(obj_x, obj_y, 10,10);
        context.stroke();
        context.fill();

        context.lineWidth = 2;
        context.strokeStyle = "red";
        drawLine(context,mid_x,mid_y,obj_x+5,obj_y+5);
    }

    context.restore();
}

// ----------------------------------------------------------------------------------
// MAIN
// ----------------------------------------------------------------------------------

function initialize(){
    // Save game functions
    game_redraw = window.redraw;
    capture_mouse = window.onmousemove;

    // Update game function
    //disableMouse();
    disableDrawing();

    // Update data
    setNick(bot_name);

    startGame();
}

(function() {
    'use strict';

    // TODO: Detect page is loaded
    // TODO: Override play button click event.
    setTimeout(initialize,1000);

})();
<|endoftext|>


<commit_before>(function (angular) {
    'use strict';
    var archivoPerfilControllerApp = angular.module('archivoPerfil', ['ngAnimate', 'ngSanitize', 'ui.bootstrap', 'ui.grid', 'ngTouch', 'ui.grid.edit', 'ui.grid.autoResize', 'ui.grid.selection', 'ui.grid.cellNav', 'xeditable'])
    .controller('archivoPerfilController', ['$scope', '$http', '$window', '$location', '$filter', 'uiGridConstants', 'i18nService', '$scope', function ($scope, $http, $window, $location, $filter, i18nService, uiGridConstants) {
        var init = function () {
            $scope.archivoID = $window.localStorage.getItem("archivoID");
            // DATOS PRINCIPALES
            $scope.cargarDatosPrincipales();
            // ANOTACIONES
            $scope.cargarNotas();
        };

        //Datos principales
        $scope.archivoDatos = {};
        $scope.datosPrincipales = {};
        $scope.datosPrincipales.archivoTitulo = "";
        $scope.datosPrincipales.archivoNombre = "";
        $scope.datosPrincipales.institucionFondo = "";
        $scope.datosPrincipales.seccion = "";
        $scope.datosPrincipales.numRefDentroFondo = "";
        $scope.datosPrincipales.archivoFecha = "";
        $scope.datosPrincipales.folioInicial = "";
        $scope.datosPrincipales.folioFinal = "";
        $scope.datosPrincipales.legajo = "";
        $scope.datosPrincipales.numOrden = "";
        $scope.datosPrincipales.numPaginas = "";
        $scope.datosPrincipales.palabrasClaves = {};
        $scope.datosPrincipales.disponibilidad = "";

        //Bandera para saber cuando guardar o no
        $scope.datosPrincipales.editado = false;

        $scope.cargarDatosPrincipales = function () {
            $http.get('http://monsalvediaz.com:5000/PIMC0.1/ConsultaArchivo?archivoID=' + $scope.archivoID).then(function (data) {
                //Obtener los datos JSON
                $scope.archivoDatos = data.data[0];
                //Log
                console.log($scope.archivoDatos);

                //Llenamos los datos del archivo
                $scope.datosPrincipales.archivoTitulo = $scope.archivoDatos.titulo;
                $scope.datosPrincipales.archivoNombre = $scope.archivoDatos.fondo;
                $scope.datosPrincipales.institucionFondo = $scope.archivoDatos.institucionFondo;
                $scope.datosPrincipales.seccion = $scope.archivoDatos.seccion;
                $scope.datosPrincipales.numRefDentroFondo = $scope.archivoDatos.numRefDentroFondo;
                $scope.datosPrincipales.archivoFecha = $filter('date')(new Date($scope.archivoDatos.fechaInicial), String($scope.archivoDatos.fechaInicialFormato).toLowerCase()) + " - " + $filter('date')(new Date($scope.archivoDatos.fechaFinal), String($scope.archivoDatos.fechaFinalFormato).toLowerCase());
                $scope.datosPrincipales.folioInicial = $scope.archivoDatos.folioInicial;
                $scope.datosPrincipales.folioFinal = $scope.archivoDatos.folioFinal;
                $scope.datosPrincipales.legajo = $scope.archivoDatos.legajo;
                $scope.datosPrincipales.numOrden = $scope.archivoDatos.numOrden;
                $scope.datosPrincipales.numPaginas = $scope.archivoDatos.numPaginas;
                $scope.datosPrincipales.palabrasClaves = $scope.archivoDatos.palabrasClaves.split(",");
                $scope.datosPrincipales.palabrasClaves = $scope.datosPrincipales.palabrasClaves.map(function (e) { return e.trim(); });

                $scope.datosPrincipales.disponibilidad = $scope.archivoDatos.disponibilidad;
                //Limpiamos la bandera de editado
                $scope.datosPrincipales.editado = false;

                //Para palabras claves
                $scope.palabraNueva = {mensaje:"+Agregar"};

            });
        };

        // Anotaciones
        $scope.notas = "";
        $scope.notasAEliminar = [];
        $scope.notasCambio = false;
        $scope.cargarNotas = function () {
            $scope.notas = "";
            $scope.notasAEliminar = [];
            $scope.notasCambio = false;
            $http.get('http://monsalvediaz.com:5000/PIMC0.1/Consulta/ArchivosNotas?archivoID=' + $scope.archivoID).then(function (data) {
                if (!String(data.data).startsWith("[WARNING]")) {
                    $scope.notas = data.data;
                    $scope.notas.forEach( function (nota) {
                        nota.modificada = false;
                    });
                    // LOG
                    console.log($scope.notas);
                }
            });

        };
        $scope.agregarNotaVacia = function () {
            // Una nota que no tiene fecha de creacion es una nota que no existe en la base de datos aun
            if ($scope.notas === "") {
                $scope.notas = [{
                    nota:"",
                    referencia:"",
                    fechaCreacion:"",
                    fechaHistorica:"",
                    fechaHistFormato:"",
                    modificada: false
                }];
            } else {
                $scope.notas.push({
                    nota:"",
                    referencia:"",
                    fechaCreacion:"",
                    fechaHistorica:"",
                    fechaHistFormato:"",
                    modificada: false
                });
            }
            $scope.notasCambios = true;
        }
        $scope.eliminarNota = function(indexNota) {
            if ($scope.notas[indexNota].fechaCreacion != "") {
                $scope.notasAEliminar.push($scope.notas[indexNota]);
            }
            $scope.notas.splice(indexNota,1);
            $scope.notasCambios = true;
        };
        $scope.modificarNota = function(indexNota,nuevaNota) {
            $scope.notas[indexNota].nota = nuevaNota;
            if ($scope.notas[indexNota].fechaCreacion != "") {
                $scope.notas[indexNota].modificada = true;
            };
            $scope.notasCambios = true;
        };
        $scope.modificarReferencia = function(indexNota,nuevaReferencia) {
            $scope.notas[indexNota].referencia = nuevaReferencia;
            if ($scope.notas[indexNota].fechaCreacion != "") {
                $scope.notas[indexNota].modificada = true;
            }
            $scope.notasCambios = true;

        };

        // Para borrar palabras claves
        $scope.borrarSiVacio = function (indexEditada, palabra) {
            if (palabra == "") {
                $scope.datosPrincipales.palabrasClaves.splice(indexEditada,1);
            }
        }
        //Para agregar palabras claves
        $scope.palabraNueva = {mensaje: '+Agregar'};

        $scope.borrarCampo = function () {
            $scope.palabraNueva.mensaje = "";
        }
        $scope.mostrarCampo = function () {
            $scope.palabraNueva.mensaje = "+Agregar";
        }
        $scope.agregarPalabraNueva = function (palabra) {
            if (!$scope.datosPrincipales.palabrasClaves.includes(palabra) && palabra.length != 0) {
                $scope.datosPrincipales.palabrasClaves.push(palabra);
            }
            $scope.palabraNueva.mensaje = "+Agregar";
        }

        //Para guardar borrar y barra
        $scope.ultimaAccion = "";
        // Button functions
        $scope.borrarCambios = function () {
            if (window.confirm("Esta Seguro que quiere borrar los cambios?") === true) {
                init();
            }
        };
        $scope.guardarCambios = function () {
            if($scope.notasCambios) {
                $scope.notasCambios = false;
                $scope.notas.forEach(function (nota) {
                    // Insertamos notas nuevas
                    if (nota.fechaCreacion.length == 0 && nota.nota.length != 0)
                        $http.get('http://monsalvediaz.com:5000/PIMC0.1/Insertar/ArchivosNotas?ArchivoID='+ $scope.archivoID +'&nota="' + nota.nota + '"&referencia="'  + nota.referencia + '"').then(function (data) {
                            console.log(data);
                        });
                    // Modificamos notas viejas
                    if (nota.modificada == true) {
                        $http.get('http://monsalvediaz.com:5000/PIMC0.1/Modificar/ArchivosNotas?idUnico2=archivoID&idUnico=notaID&notaID='+ nota.notaID +' &archivoID=' + $scope.archivoID +'&nota="' + nota.nota + '"&referencia="'  + nota.referencia + '"').then(function (data) {
                            console.log(data);
                        });
                    }
                });
                // Eliminamos notas eliminadas
                $scope.notasAEliminar.forEach(function (nota) {
                    $http.get('http://monsalvediaz.com:5000/PIMC0.1/Eliminar/ArchivosNotas?idUnico=archivoID&idUnico2=notaID&notaID='+ nota.notaID +'&archivoID=' + $scope.archivoID).then(function (data) {
                        console.log(data);
                    });
                });

            }
            //Revisamos datos principales editados
            if ($scope.datosPrincipales.editado) {

            }
            init();
        };

        // Initialization fucntion
        init();

    }]);
    archivoPerfilControllerApp.run(function (editableOptions,editableThemes) {
        editableThemes.bs3.inputClass = 'input-sm';
        editableThemes.bs3.buttonsClass = 'btn-sm';
        editableOptions.theme = 'bs3';
        editableOptions.buttons = 'no';
    });
})(window.angular);

<commit_msg>Agregar Notas<commit_after>(function (angular) {
    'use strict';
    var archivoPerfilControllerApp = angular.module('archivoPerfil', ['ngAnimate', 'ngSanitize', 'ui.bootstrap', 'ui.grid', 'ngTouch', 'ui.grid.edit', 'ui.grid.autoResize', 'ui.grid.selection', 'ui.grid.cellNav', 'xeditable'])
    .controller('archivoPerfilController', ['$scope', '$http', '$window', '$location', '$filter', 'uiGridConstants', 'i18nService', '$scope', function ($timeout, $scope, $http, $window, $location, $filter, i18nService, uiGridConstants) {
        var init = function () {
            $scope.archivoID = $window.localStorage.getItem("archivoID");
            // DATOS PRINCIPALES
            $scope.cargarDatosPrincipales();
            // ANOTACIONES
            $scope.cargarNotas();
        };

        //Datos principales
        $scope.archivoDatos = {};
        $scope.datosPrincipales = {};
        $scope.datosPrincipales.archivoTitulo = "";
        $scope.datosPrincipales.archivoNombre = "";
        $scope.datosPrincipales.institucionFondo = "";
        $scope.datosPrincipales.seccion = "";
        $scope.datosPrincipales.numRefDentroFondo = "";
        $scope.datosPrincipales.archivoFecha = "";
        $scope.datosPrincipales.folioInicial = "";
        $scope.datosPrincipales.folioFinal = "";
        $scope.datosPrincipales.legajo = "";
        $scope.datosPrincipales.numOrden = "";
        $scope.datosPrincipales.numPaginas = "";
        $scope.datosPrincipales.palabrasClaves = {};
        $scope.datosPrincipales.disponibilidad = "";

        //Bandera para saber cuando guardar o no
        $scope.datosPrincipales.editado = false;

        $scope.cargarDatosPrincipales = function () {
            $http.get('http://monsalvediaz.com:5000/PIMC0.1/ConsultaArchivo?archivoID=' + $scope.archivoID).then(function (data) {
                //Obtener los datos JSON
                $scope.archivoDatos = data.data[0];
                //Log
                console.log($scope.archivoDatos);

                //Llenamos los datos del archivo
                $scope.datosPrincipales.archivoTitulo = $scope.archivoDatos.titulo;
                $scope.datosPrincipales.archivoNombre = $scope.archivoDatos.fondo;
                $scope.datosPrincipales.institucionFondo = $scope.archivoDatos.institucionFondo;
                $scope.datosPrincipales.seccion = $scope.archivoDatos.seccion;
                $scope.datosPrincipales.numRefDentroFondo = $scope.archivoDatos.numRefDentroFondo;
                $scope.datosPrincipales.archivoFecha = $filter('date')(new Date($scope.archivoDatos.fechaInicial), String($scope.archivoDatos.fechaInicialFormato).toLowerCase()) + " - " + $filter('date')(new Date($scope.archivoDatos.fechaFinal), String($scope.archivoDatos.fechaFinalFormato).toLowerCase());
                $scope.datosPrincipales.folioInicial = $scope.archivoDatos.folioInicial;
                $scope.datosPrincipales.folioFinal = $scope.archivoDatos.folioFinal;
                $scope.datosPrincipales.legajo = $scope.archivoDatos.legajo;
                $scope.datosPrincipales.numOrden = $scope.archivoDatos.numOrden;
                $scope.datosPrincipales.numPaginas = $scope.archivoDatos.numPaginas;
                $scope.datosPrincipales.palabrasClaves = $scope.archivoDatos.palabrasClaves.split(",");
                $scope.datosPrincipales.palabrasClaves = $scope.datosPrincipales.palabrasClaves.map(function (e) { return e.trim(); });

                $scope.datosPrincipales.disponibilidad = $scope.archivoDatos.disponibilidad;
                //Limpiamos la bandera de editado
                $scope.datosPrincipales.editado = false;

                //Para palabras claves
                $scope.palabraNueva = {mensaje:"+Agregar"};

            });
        };

        // Anotaciones
        $scope.notas = "";
        $scope.notasAEliminar = [];
        $scope.notasCambio = false;
        $scope.cargarNotas = function () {
            $scope.notas = "";
            $scope.notasAEliminar = [];
            $scope.notasCambio = false;
            $http.get('http://monsalvediaz.com:5000/PIMC0.1/Consulta/ArchivosNotas?archivoID=' + $scope.archivoID).then(function (data) {
                if (!String(data.data).startsWith("[WARNING]")) {
                    $scope.notas = data.data;
                    $scope.notas.forEach( function (nota) {
                        nota.modificada = false;
                    });
                    // LOG
                    console.log($scope.notas);
                }
            });

        };
        $scope.agregarNotaVacia = function () {
            // Una nota que no tiene fecha de creacion es una nota que no existe en la base de datos aun
            if ($scope.notas === "") {
                $scope.notas = [{
                    nota:"",
                    referencia:"",
                    fechaCreacion:"",
                    fechaHistorica:"",
                    fechaHistFormato:"",
                    modificada: false
                }];
            } else {
                $scope.notas.push({
                    nota:"",
                    referencia:"",
                    fechaCreacion:"",
                    fechaHistorica:"",
                    fechaHistFormato:"",
                    modificada: false
                });
            }
            $scope.notasCambios = true;
        }
        $scope.eliminarNota = function(indexNota) {
            if ($scope.notas[indexNota].fechaCreacion != "") {
                $scope.notasAEliminar.push($scope.notas[indexNota]);
            }
            $scope.notas.splice(indexNota,1);
            $scope.notasCambios = true;
        };
        $scope.modificarNota = function(indexNota,nuevaNota) {
            $scope.notas[indexNota].nota = nuevaNota;
            if ($scope.notas[indexNota].fechaCreacion != "") {
                $scope.notas[indexNota].modificada = true;
            };
            $scope.notasCambios = true;
        };
        $scope.modificarReferencia = function(indexNota,nuevaReferencia) {
            $scope.notas[indexNota].referencia = nuevaReferencia;
            if ($scope.notas[indexNota].fechaCreacion != "") {
                $scope.notas[indexNota].modificada = true;
            }
            $scope.notasCambios = true;

        };

        // Para borrar palabras claves
        $scope.borrarSiVacio = function (indexEditada, palabra) {
            if (palabra == "") {
                $scope.datosPrincipales.palabrasClaves.splice(indexEditada,1);
            }
        }
        //Para agregar palabras claves
        $scope.palabraNueva = {mensaje: '+Agregar'};

        $scope.borrarCampo = function () {
            $scope.palabraNueva.mensaje = "";
        }
        $scope.mostrarCampo = function () {
            $scope.palabraNueva.mensaje = "+Agregar";
        }
        $scope.agregarPalabraNueva = function (palabra) {
            if (!$scope.datosPrincipales.palabrasClaves.includes(palabra) && palabra.length != 0) {
                $scope.datosPrincipales.palabrasClaves.push(palabra);
            }
            $scope.palabraNueva.mensaje = "+Agregar";
        }

        //Para guardar borrar y barra
        $scope.ultimaAccion = "";
        // Button functions
        $scope.borrarCambios = function () {
            if (window.confirm("Esta Seguro que quiere borrar los cambios?") === true) {
                init();
            }
        };
        $scope.guardarCambios = function () {
            if($scope.notasCambios) {
                $scope.notasCambios = false;
                $scope.notas.forEach(function (nota) {
                    // Insertamos notas nuevas
                    if (nota.fechaCreacion.length == 0 && nota.nota.length != 0)
                        $http.get('http://monsalvediaz.com:5000/PIMC0.1/Insertar/ArchivosNotas?ArchivoID='+ $scope.archivoID +'&nota="' + nota.nota + '"&referencia="'  + nota.referencia + '"').then(function (data) {
                            console.log(data);
                        });
                    // Modificamos notas viejas
                    if (nota.modificada == true) {
                        $http.get('http://monsalvediaz.com:5000/PIMC0.1/Modificar/ArchivosNotas?idUnico2=archivoID&idUnico=notaID&notaID='+ nota.notaID +' &archivoID=' + $scope.archivoID +'&nota="' + nota.nota + '"&referencia="'  + nota.referencia + '"').then(function (data) {
                            console.log(data);
                        });
                    }
                });
                // Eliminamos notas eliminadas
                $scope.notasAEliminar.forEach(function (nota) {
                    $http.get('http://monsalvediaz.com:5000/PIMC0.1/Eliminar/ArchivosNotas?idUnico=archivoID&idUnico2=notaID&notaID='+ nota.notaID +'&archivoID=' + $scope.archivoID).then(function (data) {
                        console.log(data);
                    });
                });

            }
            //Revisamos datos principales editados
            if ($scope.datosPrincipales.editado) {

            }
            init();
        };

        // Initialization fucntion
        init();

    }]);
    archivoPerfilControllerApp.run(function (editableOptions,editableThemes) {
        editableThemes.bs3.inputClass = 'input-sm';
        editableThemes.bs3.buttonsClass = 'btn-sm';
        editableOptions.theme = 'bs3';
        editableOptions.buttons = 'no';
    });
})(window.angular);

<|endoftext|>


<commit_before>import React, { useState } from 'react'
import { StaticQuery, graphql } from 'gatsby'
import highlightCode from 'gatsby-remark-prismjs/highlight-code.js'

import { Quickstart } from '../components/quickstart'
import generator, { DATA as GENERATOR_DATA } from './quickstart-training-generator'
import { htmlToReact } from '../components/util'

const DEFAULT_LANG = 'en'
const DEFAULT_HARDWARE = 'cpu'
const DEFAULT_OPT = 'efficiency'
const DEFAULT_TEXTCAT_EXCLUSIVE = true
const COMPONENTS = ['tagger', 'parser', 'ner', 'textcat']
const COMMENT = `# This is an auto-generated partial config. To use it with 'spacy train'
# you can run spacy init fill-config to auto-fill all default settings:
# python -m spacy init fill-config ./base_config.cfg ./config.cfg`

const DATA = [
    {
        id: 'lang',
        title: 'Language',
        defaultValue: DEFAULT_LANG,
    },
    {
        id: 'components',
        title: 'Components',
        help: 'Pipeline components to train. Requires training data for those annotations.',
        options: COMPONENTS.map(id => ({ id, title: id })),
        multiple: true,
    },
    {
        id: 'textcat',
        title: 'Text Classification',
        multiple: true,
        options: [
            {
                id: 'exclusive',
                title: 'exclusive categories',
                checked: DEFAULT_TEXTCAT_EXCLUSIVE,
                help: 'only one label can apply',
            },
        ],
    },
    {
        id: 'hardware',
        title: 'Hardware',
        options: [
            { id: 'cpu', title: 'CPU', checked: DEFAULT_HARDWARE === 'cpu' },
            { id: 'gpu', title: 'GPU (transformer)', checked: DEFAULT_HARDWARE === 'gpu' },
        ],
    },
    {
        id: 'optimize',
        title: 'Optimize for',
        help:
            'Optimize for efficiency (faster inference, smaller model, lower memory consumption) or higher accuracy (potentially larger & slower model). Will impact the choice of architecture, pretrained weights and hyperparameters.',
        options: [
            { id: 'efficiency', title: 'efficiency', checked: DEFAULT_OPT === 'efficiency' },
            { id: 'accuracy', title: 'accuracy', checked: DEFAULT_OPT === 'accuracy' },
        ],
    },
]

export default function QuickstartTraining({ id, title, download = 'base_config.cfg' }) {
    const [lang, setLang] = useState(DEFAULT_LANG)
    const [_components, _setComponents] = useState([])
    const [components, setComponents] = useState([])
    const [[hardware], setHardware] = useState([DEFAULT_HARDWARE])
    const [[optimize], setOptimize] = useState([DEFAULT_OPT])
    const [textcatExclusive, setTextcatExclusive] = useState(DEFAULT_TEXTCAT_EXCLUSIVE)

    function updateComponents(value, isExclusive) {
        _setComponents(value)
        const updated = value.map(c => (c === 'textcat' && !isExclusive ? 'textcat_multilabel' : c))
        setComponents(updated)
    }

    const setters = {
        lang: setLang,
        components: v => updateComponents(v, textcatExclusive),
        hardware: setHardware,
        optimize: setOptimize,
        textcat: v => {
            const isExclusive = v.includes('exclusive')
            setTextcatExclusive(isExclusive)
            updateComponents(_components, isExclusive)
        },
    }
    const reco = GENERATOR_DATA[lang] || GENERATOR_DATA.__default__
    const content = generator({
        lang,
        components,
        optimize,
        hardware,
        transformer_data: reco.transformer,
        word_vectors: reco.word_vectors,
        has_letters: reco.has_letters,
    })
    const rawStr = content.trim().replace(/\n\n\n+/g, '\n\n')
    const rawContent = `${COMMENT}\n${rawStr}`
    const displayContent = highlightCode('ini', rawContent)
        .split('\n')
        .map(line => (line.startsWith('#') ? `<span class="token comment">${line}</span>` : line))
        .join('\n')
    return (
        <StaticQuery
            query={query}
            render={({ site }) => {
                let data = DATA
                const langs = site.siteMetadata.languages
                data[0].dropdown = langs
                    .map(({ name, code }) => ({
                        id: code,
                        title: name,
                    }))
                    .sort((a, b) => a.title.localeCompare(b.title))
                if (!_components.includes('textcat')) {
                    data = data.map(field =>
                        field.id === 'textcat' ? { ...field, hidden: true } : field
                    )
                }
                return (
                    <Quickstart
                        id="quickstart-widget"
                        Container="div"
                        download={download}
                        rawContent={rawContent}
                        data={data}
                        title={title}
                        id={id}
                        setters={setters}
                        hidePrompts
                        small
                        codeLang="ini"
                    >
                        {htmlToReact(displayContent)}
                    </Quickstart>
                )
            }}
        />
    )
}

const query = graphql`
    query QuickstartTrainingQuery {
        site {
            siteMetadata {
                languages {
                    code
                    name
                }
            }
        }
    }
`
<commit_msg>Fix default transformer in quickstart generator (resolves #8018) [ci skip]<commit_after>import React, { useState } from 'react'
import { StaticQuery, graphql } from 'gatsby'
import highlightCode from 'gatsby-remark-prismjs/highlight-code.js'

import { Quickstart } from '../components/quickstart'
import generator, { DATA as GENERATOR_DATA } from './quickstart-training-generator'
import { htmlToReact } from '../components/util'

const DEFAULT_LANG = 'en'
const DEFAULT_HARDWARE = 'cpu'
const DEFAULT_OPT = 'efficiency'
const DEFAULT_TEXTCAT_EXCLUSIVE = true
const COMPONENTS = ['tagger', 'parser', 'ner', 'textcat']
const COMMENT = `# This is an auto-generated partial config. To use it with 'spacy train'
# you can run spacy init fill-config to auto-fill all default settings:
# python -m spacy init fill-config ./base_config.cfg ./config.cfg`

const DATA = [
    {
        id: 'lang',
        title: 'Language',
        defaultValue: DEFAULT_LANG,
    },
    {
        id: 'components',
        title: 'Components',
        help: 'Pipeline components to train. Requires training data for those annotations.',
        options: COMPONENTS.map(id => ({ id, title: id })),
        multiple: true,
    },
    {
        id: 'textcat',
        title: 'Text Classification',
        multiple: true,
        options: [
            {
                id: 'exclusive',
                title: 'exclusive categories',
                checked: DEFAULT_TEXTCAT_EXCLUSIVE,
                help: 'only one label can apply',
            },
        ],
    },
    {
        id: 'hardware',
        title: 'Hardware',
        options: [
            { id: 'cpu', title: 'CPU', checked: DEFAULT_HARDWARE === 'cpu' },
            { id: 'gpu', title: 'GPU (transformer)', checked: DEFAULT_HARDWARE === 'gpu' },
        ],
    },
    {
        id: 'optimize',
        title: 'Optimize for',
        help:
            'Optimize for efficiency (faster inference, smaller model, lower memory consumption) or higher accuracy (potentially larger & slower model). Will impact the choice of architecture, pretrained weights and hyperparameters.',
        options: [
            { id: 'efficiency', title: 'efficiency', checked: DEFAULT_OPT === 'efficiency' },
            { id: 'accuracy', title: 'accuracy', checked: DEFAULT_OPT === 'accuracy' },
        ],
    },
]

export default function QuickstartTraining({ id, title, download = 'base_config.cfg' }) {
    const [lang, setLang] = useState(DEFAULT_LANG)
    const [_components, _setComponents] = useState([])
    const [components, setComponents] = useState([])
    const [[hardware], setHardware] = useState([DEFAULT_HARDWARE])
    const [[optimize], setOptimize] = useState([DEFAULT_OPT])
    const [textcatExclusive, setTextcatExclusive] = useState(DEFAULT_TEXTCAT_EXCLUSIVE)

    function updateComponents(value, isExclusive) {
        _setComponents(value)
        const updated = value.map(c => (c === 'textcat' && !isExclusive ? 'textcat_multilabel' : c))
        setComponents(updated)
    }

    const setters = {
        lang: setLang,
        components: v => updateComponents(v, textcatExclusive),
        hardware: setHardware,
        optimize: setOptimize,
        textcat: v => {
            const isExclusive = v.includes('exclusive')
            setTextcatExclusive(isExclusive)
            updateComponents(_components, isExclusive)
        },
    }
    const defaultData = GENERATOR_DATA.__default__
    const reco = GENERATOR_DATA[lang] || defaultData
    const content = generator({
        lang,
        components,
        optimize,
        hardware,
        transformer_data: reco.transformer || defaultData.transformer,
        word_vectors: reco.word_vectors,
        has_letters: reco.has_letters,
    })
    const rawStr = content.trim().replace(/\n\n\n+/g, '\n\n')
    const rawContent = `${COMMENT}\n${rawStr}`
    const displayContent = highlightCode('ini', rawContent)
        .split('\n')
        .map(line => (line.startsWith('#') ? `<span class="token comment">${line}</span>` : line))
        .join('\n')
    return (
        <StaticQuery
            query={query}
            render={({ site }) => {
                let data = DATA
                const langs = site.siteMetadata.languages
                data[0].dropdown = langs
                    .map(({ name, code }) => ({
                        id: code,
                        title: name,
                    }))
                    .sort((a, b) => a.title.localeCompare(b.title))
                if (!_components.includes('textcat')) {
                    data = data.map(field =>
                        field.id === 'textcat' ? { ...field, hidden: true } : field
                    )
                }
                return (
                    <Quickstart
                        id="quickstart-widget"
                        Container="div"
                        download={download}
                        rawContent={rawContent}
                        data={data}
                        title={title}
                        id={id}
                        setters={setters}
                        hidePrompts
                        small
                        codeLang="ini"
                    >
                        {htmlToReact(displayContent)}
                    </Quickstart>
                )
            }}
        />
    )
}

const query = graphql`
    query QuickstartTrainingQuery {
        site {
            siteMetadata {
                languages {
                    code
                    name
                }
            }
        }
    }
`
<|endoftext|>


<commit_before>'use strict';

var expect = require('expect.js');

describe('nodeInstances:', function () {
    var instances;

    it('should list deployment nodes instances', function (done) {
        testClient.deployments.list(null,  function( err, response, body ){
            var depName = JSON.parse(body)[0].id;

            console.log('depName is', depName);

            testClient.nodeInstances.list(depName, null, function (err, response, body) {
                expect(body).to.be.ok();
                expect(response.statusCode).to.be(200);

                instances = JSON.parse(body);
                expect(instances).to.not.be.empty();
                done();

            });
        });
    });

    it('should get node instance', function(done) {
        testClient.nodeInstances.get(instances[0].id, null, function (err, response, body) {
            expect(body).to.be.ok();
            expect(response.statusCode).to.be(200);

            var instance = JSON.parse(body);
            expect(instance.id).to.be(instances[0].id);

            done();
        });
    });

    it('should update node instance', function(done) {
        var newState = 'starting';
        var oldState = instances[0].state;
        var newProperties = {
            'kuku': 'muku'
        };
        var oldProperties = instances[0].runtime_properties;

        testClient.nodeInstances.update(instances[0].id, newState, newProperties, 0, function (err, response, instance) {
            expect(instance).to.be.ok();
            expect(response.statusCode).to.be(200 || 415);

            if ( response.statusCode === 415 ){
                expect(instance.error_code).to.be('hello_world'); // todo fill in here
                done();
            }

            if ( response.statusCode === 200 ) {
                expect(instance.id).to.be(instances[0].id);
                expect(instance.state).to.be(newState);
                expect(instance.runtime_properties).to.eql(newProperties);

                // reset node to previous state
                testClient.nodeInstances.update(instances[0].id, oldState, oldProperties, 0, function (err, response, body) {
                    expect(body).to.be.ok();
                    expect(response.statusCode).to.be(200);

                    done();
                });
            }
        });

    });

});<commit_msg>make tests more stable<commit_after>'use strict';

var expect = require('expect.js');

describe('nodeInstances:', function () {
    var instances;

    it('should list deployment nodes instances', function (done) {
        testClient.deployments.list(null,  function( err, response, body ){
            var depName = JSON.parse(body)[0].id;

            console.log('depName is', depName);

            testClient.nodeInstances.list(depName, null, function (err, response, body) {
                expect(body).to.be.ok();
                expect(response.statusCode).to.be(200);

                instances = JSON.parse(body);
                expect(instances).to.not.be.empty();
                done();

            });
        });
    });

    it('should get node instance', function(done) {
        testClient.nodeInstances.get(instances[0].id, null, function (err, response, body) {
            expect(body).to.be.ok();
            expect(response.statusCode).to.be(200);

            var instance = JSON.parse(body);
            expect(instance.id).to.be(instances[0].id);

            done();
        });
    });

    it('should update node instance', function(done) {
        var newState = 'starting';
        var oldState = instances[0].state;
        var newProperties = {
            'kuku': 'muku'
        };
        var oldProperties = instances[0].runtime_properties;

        testClient.nodeInstances.update(instances[0].id, newState, newProperties, 0, function (err, response, instance) {
            expect(instance).to.be.ok();
            expect(response.statusCode).to.be(200 || 415);

            if ( response.statusCode === 415 ){
                expect(instance.error_code).to.be('hello_world'); // todo fill in here
                done();
            }

            if ( response.statusCode === 200 ) {
                expect(instance.id).to.be(instances[0].id);
                expect(instance.state).to.be(newState);
                expect(instance.runtime_properties).to.eql(newProperties);

                // reset node to previous state
                testClient.nodeInstances.update(instances[0].id, oldState, oldProperties, 0, function (err, response, body) {
                    expect(body).to.be.ok();
                    expect(response.statusCode).to.be(200 || 415);

                    done();
                });
            }
        });

    });

});<|endoftext|>


<commit_before>'use strict';

/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

import $C from '../deps/collection';
import Snakeskin from '../core';
import { IS_NODE } from '../consts/hacks';
import { NULL } from '../consts/links';
import { $globalCache, $globalFnCache } from '../consts/cache';
import { escapeEOLs } from './escape';

/**
 * Returns data from the cache by the specified key
 *
 * @param {?string} cacheKey - cache key
 * @param {string} code - source SS code
 * @param {!Object} params - compile parameters
 * @param {!Object} ctx - context
 * @return {(string|undefined)}
 */
export function getFromCache(cacheKey, code, params, ctx) {
	if (IS_NODE && ctx !== NULL && $globalFnCache[cacheKey]) {
		$C($globalFnCache[cacheKey][code]).forEach((el, key) => {
			ctx[key] = el;
		});
	}

	const
		cache = $globalCache[cacheKey] && $globalCache[cacheKey][code];

	if (cache) {
		let
			skip = false;

		if (params.words) {
			if (!cache.words) {
				skip = true;

			} else {
				$C(cache.words).forEach((el, key) => {
					params.words[key] = el;
				});
			}
		}

		if (params.debug) {
			if (!cache.debug) {
				skip = true;

			} else {
				$C(cache.debug).forEach((el, key) => {
					params.debug[key] = el;
				});
			}
		}

		if (!skip) {
			return cache.text;
		}
	}
}

/**
 * Returns a cache key for the current SS process
 *
 * @param {!Object} params - compile parameters
 * @param {!Object} ctx - context
 * @return {?string}
 */
export function getCacheKey(params, ctx) {
	return params.language || params.macros ?
		null : [
			params.exports,
			ctx !== NULL,
			escapeEOLs(params.eol),
			params.doctype,
			params.tolerateWhitespace,
			params.inlineIterators,
			params.renderAs,
			params.renderMode,
			params.replaceUndef,
			params.escapeOutput,
			params.prettyPrint,
			params.ignore,
			params.autoReplace,
			params.localization,
			params.i18nFn,
			params.bemFilter,
			params.useStrict
		].join();
}

/**
 * Saves compiled template functions in the cache by the specified key
 *
 * @param {?string} cacheKey - cache key
 * @param {string} code - source SS code
 * @param {!Object} params - compile parameters
 * @param {!Object} ctx - context
 */
export function saveIntoFnCache(cacheKey, code, params, ctx) {
	if (ctx !== NULL) {
		ctx['init'](Snakeskin);

		if (cacheKey && (params.cache || $globalFnCache[cacheKey])) {
			$globalFnCache[cacheKey] = $globalFnCache[cacheKey] || {};
			$globalFnCache[cacheKey][code] = ctx;
		}
	}
}

/**
 * Saves templates in the cache by the specified key
 *
 * @param {?string} cacheKey - cache key
 * @param {string} code - source SS code
 * @param {!Object} params - compile parameters
 * @param {!Parser} parser - instance of Parser class
 */
export function saveIntoCache(cacheKey, code, params, parser) {
	if (cacheKey && (params.cache || $globalCache[cacheKey])) {
		$globalCache[cacheKey] = $globalCache[cacheKey] || {};
		$globalCache[cacheKey][code] = {
			debug: params.debug,
			text: parser.res,
			words: params.words
		};
	}
}
<commit_msg>:art: Refactoring<commit_after>'use strict';

/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

import $C from '../deps/collection';
import Snakeskin from '../core';
import { IS_NODE } from '../consts/hacks';
import { NULL } from '../consts/links';
import { $globalCache, $globalFnCache } from '../consts/cache';
import { escapeEOLs } from './escape';

/**
 * Returns data from the cache by the specified key
 *
 * @param {?string} key - cache key
 * @param {string} code - source SS code
 * @param {!Object} params - compile parameters
 * @param {!Object} ctx - context
 * @return {(string|undefined)}
 */
export function getFromCache(key, code, params, ctx) {
	if (IS_NODE && ctx !== NULL && $globalFnCache[key]) {
		$C($globalFnCache[key][code]).forEach((el, key) => {
			ctx[key] = el;
		});
	}

	const
		cache = $globalCache[key] && $globalCache[key][code];

	if (!cache) {
		return;
	}

	if (params.words) {
		if (!cache.words) {
			return;
		}

		$C(cache.words).forEach((el, key) => {
			params.words[key] = el;
		});
	}

	if (params.debug) {
		if (!cache.debug) {
			return;
		}

		$C(cache.debug).forEach((el, key) => {
			params.debug[key] = el;
		});
	}

	return cache.text;
}

/**
 * Returns a cache key for the current SS process
 *
 * @param {!Object} params - compile parameters
 * @param {!Object} ctx - context
 * @return {?string}
 */
export function getCacheKey(params, ctx) {
	return params.language || params.macros ?
		null : [
			params.exports,
			ctx !== NULL,
			escapeEOLs(params.eol),
			params.doctype,
			params.tolerateWhitespace,
			params.inlineIterators,
			params.renderAs,
			params.renderMode,
			params.replaceUndef,
			params.escapeOutput,
			params.prettyPrint,
			params.ignore,
			params.autoReplace,
			params.localization,
			params.i18nFn,
			params.bemFilter,
			params.useStrict
		].join();
}

/**
 * Saves compiled template functions in the cache by the specified key
 *
 * @param {?string} key - cache key
 * @param {string} code - source SS code
 * @param {!Object} params - compile parameters
 * @param {!Object} ctx - context
 */
export function saveIntoFnCache(key, code, params, ctx) {
	if (ctx === NULL) {
		return;
	}

	ctx['init'](Snakeskin);
	if (!key || !(params.cache || $globalCache[key])) {
		return;
	}

	$globalFnCache[key] = $C.extend(false, $globalFnCache[key], {[code]: ctx});
}

/**
 * Saves templates in the cache by the specified key
 *
 * @param {?string} key - cache key
 * @param {string} code - source SS code
 * @param {!Object} params - compile parameters
 * @param {!Parser} parser - instance of a Parser class
 */
export function saveIntoCache(key, code, params, parser) {
	if (!key || !(params.cache || $globalCache[key])) {
		return;
	}

	$globalCache[key] = $C.extend(false, $globalCache[key], {
		debug: params.debug,
		text: params.res,
		words: params.words
	});
}
<|endoftext|>


<commit_before>(function($, $TD, TD){
  //
  // Variable: Current highlighted column jQuery object.
  //
  var highlightedColumnEle;

  //
  // Variable: Currently highlighted tweet jQuery object.
  //
  var highlightedTweetEle;

  //
  // Function: Initializes TweetD*ck events. Called after the website app is loaded.
  //
  var initializeTweetDck = function(){
    // Settings button hook
    $("[data-action='settings-menu']").click(function(){
      setTimeout(function(){
        var menu = $(".js-dropdown-content").children("ul").first();
        if (menu.length === 0)return;

        menu.children(".drp-h-divider").last().after([
          '
'+$TD.brandName+' settings
',
          '
'+$TD.brandName+' plugins
',
          '

'
        ].join(""));

        var buttons = menu.children("[data-std]");

        buttons.on("click", "a", function(){
          var action = $(this).attr("data-action");

          if (action === "td-settings"){
            $TD.openSettingsMenu();
          }
          else if (action === "td-plugins"){
            $TD.openPluginsMenu();
          }
        });

        buttons.hover(function(){
          $(this).addClass("is-selected");
        }, function(){
          $(this).removeClass("is-selected");
        });
      }, 0);
    });

    // Notification handling
    $.subscribe("/notifications/new", function(obj){
      for(let index = obj.items.length-1; index >= 0; index--){
        onNewTweet(obj.column, obj.items[index]);
      }
    });

    // Setup video element replacement and fix missing target in user links
    new MutationObserver(function(){
      $("video").each(function(){
        $(this).parent().replaceWith("► Open video in browser");
      });

      $("a[rel='user']").attr("target", "_blank");
    }).observe($(".js-app-columns")[0], {
      childList: true,
      subtree: true
    });

    // Finish init and load plugins
    $TD.loadFontSizeClass(TD.settings.getFontSize());
    $TD.loadNotificationHeadContents(getNotificationHeadContents());

    window.TD_APP_READY = true;

    if (window.TD_PLUGINS){
      window.TD_PLUGINS.onReady();
    }
  };

  //
  // Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled.
  //
  var prependToFunction = function(func, extension){
    return function(){
      return extension.apply(this, arguments) === true ? undefined : func.apply(this, arguments);
    };
  };

  //
  // Function: Appends code at the end of a function.
  //
  var appendToFunction = function(func, extension){
    return function(){
      var res = func.apply(this, arguments);
      extension.apply(this, arguments);
      return res;
    };
  };

  //
  // Function: Event callback for a new tweet.
  //
  var onNewTweet = function(column, tweet){
    if (column.model.getHasNotification()){
      var html = $(tweet.render({
        withFooter: false,
        withTweetActions: false,
        withMediaPreview: false
      }));

      html.css("border", "0");
      html.find(".tweet-body").first().children("footer").remove();

      var url = html.find("time").first().children("a").first().attr("href") || "";

      $TD.onTweetPopup(html.html(), url, tweet.text.length); // TODO column
    }
    else if (column.model.getHasSound()){
      $TD.onTweetSound(); // TODO disable original
    }
  };

  //
  // Function: Retrieves the tags to be put into  for notification HTML code.
  //
  var getNotificationHeadContents = function(){
    var tags = [];

    $(document.head).children("link[rel='stylesheet'],meta[charset],meta[http-equiv]").each(function(){
      tags.push($(this)[0].outerHTML);
    });

    return tags.join("");
  };

  //
  // Block: Observe the app
 element and initialize TweetD*ck whenever possible.
  //
  var app = $("body").children(".js-app");

  new MutationObserver(function(){
    if (window.TD_APP_READY && app.hasClass("is-hidden")){
      window.TD_APP_READY = false;
    }
    else if (!window.TD_APP_READY && !app.hasClass("is-hidden")){
      initializeTweetDck();
    }
  }).observe(app[0], {
    attributes: true,
    attributeFilter: [ "class" ]
  });

  //
  // Block: Hook into settings object to detect when the settings change.
  //
  TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){
    $TD.loadFontSizeClass(name);
  });

  TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(){
    setTimeout(function(){
      $TD.loadNotificationHeadContents(getNotificationHeadContents());
    }, 0);
  });

  //
  // Block: Force popup notification settings.
  //
  TD.controller.notifications.hasNotifications = function(){
    return true;
  };

  TD.controller.notifications.isPermissionGranted = function(){
    return true;
  };

  //
  // Block: Expand shortened links on hover or display tooltip.
  //
  (function(){
    var cutStart = function(str, search){
      return str.startsWith(search) ? str.substr(search.length) : str;
    };

    var prevMouseX = -1, prevMouseY = -1;
    var tooltipTimer, tooltipDisplayed;

    $(document.body).delegate("a[data-full-url]", "mouseenter mouseleave mousemove", function(e){
      var me = $(this);

      if (e.type === "mouseenter"){
        var text = me.text();

        if (text.charCodeAt(text.length-1) !== 8230){ // horizontal ellipsis
          return;
        }

        if ($TD.expandLinksOnHover){
          tooltipTimer = window.setTimeout(function(){
            var expanded = me.attr("data-full-url");
            expanded = cutStart(expanded, "https://");
            expanded = cutStart(expanded, "http://");
            expanded = cutStart(expanded, "www.");

            me.attr("td-prev-text", text);
            me.text(expanded);
          }, 200);
        }
        else{
          tooltipTimer = window.setTimeout(function(){
            $TD.displayTooltip(me.attr("data-full-url"), false);
            tooltipDisplayed = true;
          }, 400);
        }
      }
      else if (e.type === "mouseleave"){
        if ($TD.expandLinksOnHover){
          var prevText = me.attr("td-prev-text");

          if (prevText){
            me.text(prevText);
          }
        }

        window.clearTimeout(tooltipTimer);

        if (tooltipDisplayed){
          tooltipDisplayed = false;
          $TD.displayTooltip(null, false);
        }
      }
      else if (e.type === "mousemove"){
        if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
          $TD.displayTooltip(me.attr("data-full-url"), false);
          prevMouseX = e.clientX;
          prevMouseY = e.clientY;
        }
      }
    });
  })();

  //
  // Block: Allow bypassing of t.co in context menus.
  //
  $(document.body).delegate("a", "contextmenu", function(){
    $TD.setLastRightClickedLink($(this).attr("data-full-url") || "");
  });

  //
  // Block: Hook into the notification sound effect.
  //
  (function(){
    var soundEle = document.getElementById("update-sound");

    soundEle.play = prependToFunction(soundEle.play, function(){
      return $TD.muteNotifications;
    });
  })();

  /* TODO document.getElementById("update-sound").play = function(){
    $TD.onTweetSound();
  };*/

  //
  // Block: Update highlighted column.
  //
  app.delegate("section", "mouseenter mouseleave", function(e){
    if (e.type === "mouseenter"){
      highlightedColumnEle = $(this);
    }
    else if (e.type === "mouseleave"){
      highlightedColumnEle = null;
    }
  });

  //
  // Block: Copy tweet address and update highlighted tweet.
  //
  (function(){
    var lastTweet = "";

    var updateHighlightedTweet = function(link, embeddedLink){
      if (lastTweet !== link){
        $TD.setLastHighlightedTweet(link, embeddedLink);
        lastTweet = link;
      }
    };

    app.delegate("article.js-stream-item", "mouseenter mouseleave", function(e){
      if (e.type === "mouseenter"){
        highlightedTweetEle = $(this);

        var link = $(this).find("time").first().children("a").first();
        var embedded = $(this).find(".quoted-tweet[data-tweet-id]").first();

        updateHighlightedTweet(link.length > 0 ? link.attr("href") : "", embedded.length > 0 ? embedded.find(".account-link").first().attr("href")+"/status/"+embedded.attr("data-tweet-id") : "");
      }
      else if (e.type === "mouseleave"){
        highlightedTweetEle = null;
        updateHighlightedTweet("", "");
      }
    });
  })();

  //
  // Block: Paste images when tweeting.
  //
  (function(){
    var lastPasteElement;
    var prevScrollTop;

    var getScroller = function(){
      return $(".js-drawer").find(".js-compose-scroller").first().children().first();
    };

    var clickUpload = function(){
      var button = $(".js-add-image-button").first();

      var scroller = getScroller();
      prevScrollTop = scroller.scrollTop();

      scroller.scrollTop(0);
      scroller.scrollTop(button.offset().top); // scrolls the button into view

      var buttonPos = button.children().first().offset(); // finds the camera icon offset
      $TD.clickUploadImage(Math.floor(buttonPos.left), Math.floor(buttonPos.top));
    };

    $(".js-app").delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(){
      lastPasteElement = $(this);
      $TD.tryPasteImage();
    });

    window.TDGF_tryPasteImage = function(){
      if (lastPasteElement){
        var parent = lastPasteElement.parent();

        if (parent.siblings(".js-add-image-button").length === 0){
          var pop = parent.closest(".js-inline-reply,.rpl").find(".js-inline-compose-pop,.js-reply-popout");

          if (pop.length === 0){
            lastPasteElement = null;
            return;
          }

          pop.click();

          var drawer = $(".js-drawer");
          var counter = 0;

          var interval = setInterval(function(){
            if (drawer.offset().left >= 195){
              clickUpload();
              clearInterval(interval);
            }
            else if (++counter >= 10){
              clearInterval(interval);
            }
          }, 51);
        }
        else{
          clickUpload();
        }

        lastPasteElement = null;
      }
    };

    window.TDGF_tryPasteImageFinish = function(){
      setTimeout(function(){
        getScroller().scrollTop(prevScrollTop);
        $(".js-drawer").find(".js-compose-text").first()[0].focus();
      }, 10);
    };
  })();

  //
  // Block: Support for extra mouse buttons.
  //
  window.TDGF_onMouseClickExtra = function(button){
    if (button === 1){ // back button
      var modal = $("#open-modal");

      if (highlightedColumnEle && highlightedColumnEle.closest(".js-column").is(".is-shifted-1")){
        highlightedColumnEle.find(".js-column-back").first().click();
      }
      else if (modal.is(":visible")){
        modal.find("a[rel=dismiss]").click();
      }
      else{
        $(".js-column-back").click();
      }
    }
    else if (button === 2){ // forward button
      if (highlightedTweetEle){
        highlightedTweetEle.children().first().click();
      }
    }
  };

  //
  // Block: Inject custom CSS and layout into the page.
  //
  (function(){
    var styleOfficial = document.createElement("style");
    document.head.appendChild(styleOfficial);

    styleOfficial.sheet.insertRule("a[data-full-url] { word-break: break-all; }", 0); // break long urls
    styleOfficial.sheet.insertRule(".column-nav-link .attribution { position: absolute; }", 0); // fix cut off account names
    styleOfficial.sheet.insertRule(".txt-base-smallest .badge-verified:before { height: 13px !important; }", 0); // fix cut off badge icon
    styleOfficial.sheet.insertRule(".keyboard-shortcut-list { vertical-align: top; }", 0); // fix keyboard navigation alignment

    if ($TD.hasCustomBrowserCSS){
      var styleCustom = document.createElement("style");
      styleCustom.innerHTML = $TD.customBrowserCSS;
      document.head.appendChild(styleCustom);
    }
  })();
})($, $TD, TD);
<commit_msg>Work around Scheduled column update delay bug in TweetDeck<commit_after>(function($, $TD, TD){
  //
  // Variable: Current highlighted column jQuery object.
  //
  var highlightedColumnEle;

  //
  // Variable: Currently highlighted tweet jQuery object.
  //
  var highlightedTweetEle;

  //
  // Function: Initializes TweetD*ck events. Called after the website app is loaded.
  //
  var initializeTweetDck = function(){
    // Settings button hook
    $("[data-action='settings-menu']").click(function(){
      setTimeout(function(){
        var menu = $(".js-dropdown-content").children("ul").first();
        if (menu.length === 0)return;

        menu.children(".drp-h-divider").last().after([
          '
'+$TD.brandName+' settings
',
          '
'+$TD.brandName+' plugins
',
          '

'
        ].join(""));

        var buttons = menu.children("[data-std]");

        buttons.on("click", "a", function(){
          var action = $(this).attr("data-action");

          if (action === "td-settings"){
            $TD.openSettingsMenu();
          }
          else if (action === "td-plugins"){
            $TD.openPluginsMenu();
          }
        });

        buttons.hover(function(){
          $(this).addClass("is-selected");
        }, function(){
          $(this).removeClass("is-selected");
        });
      }, 0);
    });

    // Notification handling
    $.subscribe("/notifications/new", function(obj){
      for(let index = obj.items.length-1; index >= 0; index--){
        onNewTweet(obj.column, obj.items[index]);
      }
    });

    // Setup video element replacement and fix missing target in user links
    new MutationObserver(function(){
      $("video").each(function(){
        $(this).parent().replaceWith("► Open video in browser");
      });

      $("a[rel='user']").attr("target", "_blank");
    }).observe($(".js-app-columns")[0], {
      childList: true,
      subtree: true
    });

    // Finish init and load plugins
    $TD.loadFontSizeClass(TD.settings.getFontSize());
    $TD.loadNotificationHeadContents(getNotificationHeadContents());

    window.TD_APP_READY = true;

    if (window.TD_PLUGINS){
      window.TD_PLUGINS.onReady();
    }
  };

  //
  // Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled.
  //
  var prependToFunction = function(func, extension){
    return function(){
      return extension.apply(this, arguments) === true ? undefined : func.apply(this, arguments);
    };
  };

  //
  // Function: Appends code at the end of a function.
  //
  var appendToFunction = function(func, extension){
    return function(){
      var res = func.apply(this, arguments);
      extension.apply(this, arguments);
      return res;
    };
  };

  //
  // Function: Event callback for a new tweet.
  //
  var onNewTweet = function(column, tweet){
    if (column.model.getHasNotification()){
      var html = $(tweet.render({
        withFooter: false,
        withTweetActions: false,
        withMediaPreview: false
      }));

      html.css("border", "0");
      html.find(".tweet-body").first().children("footer").remove();

      var url = html.find("time").first().children("a").first().attr("href") || "";

      $TD.onTweetPopup(html.html(), url, tweet.text.length); // TODO column
    }
    else if (column.model.getHasSound()){
      $TD.onTweetSound(); // TODO disable original
    }
  };

  //
  // Function: Retrieves the tags to be put into  for notification HTML code.
  //
  var getNotificationHeadContents = function(){
    var tags = [];

    $(document.head).children("link[rel='stylesheet'],meta[charset],meta[http-equiv]").each(function(){
      tags.push($(this)[0].outerHTML);
    });

    return tags.join("");
  };

  //
  // Block: Observe the app
 element and initialize TweetD*ck whenever possible.
  //
  var app = $("body").children(".js-app");

  new MutationObserver(function(){
    if (window.TD_APP_READY && app.hasClass("is-hidden")){
      window.TD_APP_READY = false;
    }
    else if (!window.TD_APP_READY && !app.hasClass("is-hidden")){
      initializeTweetDck();
    }
  }).observe(app[0], {
    attributes: true,
    attributeFilter: [ "class" ]
  });

  //
  // Block: Hook into settings object to detect when the settings change.
  //
  TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){
    $TD.loadFontSizeClass(name);
  });

  TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(){
    setTimeout(function(){
      $TD.loadNotificationHeadContents(getNotificationHeadContents());
    }, 0);
  });

  //
  // Block: Force popup notification settings.
  //
  TD.controller.notifications.hasNotifications = function(){
    return true;
  };

  TD.controller.notifications.isPermissionGranted = function(){
    return true;
  };

  //
  // Block: Expand shortened links on hover or display tooltip.
  //
  (function(){
    var cutStart = function(str, search){
      return str.startsWith(search) ? str.substr(search.length) : str;
    };

    var prevMouseX = -1, prevMouseY = -1;
    var tooltipTimer, tooltipDisplayed;

    $(document.body).delegate("a[data-full-url]", "mouseenter mouseleave mousemove", function(e){
      var me = $(this);

      if (e.type === "mouseenter"){
        var text = me.text();

        if (text.charCodeAt(text.length-1) !== 8230){ // horizontal ellipsis
          return;
        }

        if ($TD.expandLinksOnHover){
          tooltipTimer = window.setTimeout(function(){
            var expanded = me.attr("data-full-url");
            expanded = cutStart(expanded, "https://");
            expanded = cutStart(expanded, "http://");
            expanded = cutStart(expanded, "www.");

            me.attr("td-prev-text", text);
            me.text(expanded);
          }, 200);
        }
        else{
          tooltipTimer = window.setTimeout(function(){
            $TD.displayTooltip(me.attr("data-full-url"), false);
            tooltipDisplayed = true;
          }, 400);
        }
      }
      else if (e.type === "mouseleave"){
        if ($TD.expandLinksOnHover){
          var prevText = me.attr("td-prev-text");

          if (prevText){
            me.text(prevText);
          }
        }

        window.clearTimeout(tooltipTimer);

        if (tooltipDisplayed){
          tooltipDisplayed = false;
          $TD.displayTooltip(null, false);
        }
      }
      else if (e.type === "mousemove"){
        if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
          $TD.displayTooltip(me.attr("data-full-url"), false);
          prevMouseX = e.clientX;
          prevMouseY = e.clientY;
        }
      }
    });
  })();

  //
  // Block: Allow bypassing of t.co in context menus.
  //
  $(document.body).delegate("a", "contextmenu", function(){
    $TD.setLastRightClickedLink($(this).attr("data-full-url") || "");
  });

  //
  // Block: Hook into the notification sound effect.
  //
  (function(){
    var soundEle = document.getElementById("update-sound");

    soundEle.play = prependToFunction(soundEle.play, function(){
      return $TD.muteNotifications;
    });
  })();

  /* TODO document.getElementById("update-sound").play = function(){
    $TD.onTweetSound();
  };*/

  //
  // Block: Update highlighted column.
  //
  app.delegate("section", "mouseenter mouseleave", function(e){
    if (e.type === "mouseenter"){
      highlightedColumnEle = $(this);
    }
    else if (e.type === "mouseleave"){
      highlightedColumnEle = null;
    }
  });

  //
  // Block: Copy tweet address and update highlighted tweet.
  //
  (function(){
    var lastTweet = "";

    var updateHighlightedTweet = function(link, embeddedLink){
      if (lastTweet !== link){
        $TD.setLastHighlightedTweet(link, embeddedLink);
        lastTweet = link;
      }
    };

    app.delegate("article.js-stream-item", "mouseenter mouseleave", function(e){
      if (e.type === "mouseenter"){
        highlightedTweetEle = $(this);

        var link = $(this).find("time").first().children("a").first();
        var embedded = $(this).find(".quoted-tweet[data-tweet-id]").first();

        updateHighlightedTweet(link.length > 0 ? link.attr("href") : "", embedded.length > 0 ? embedded.find(".account-link").first().attr("href")+"/status/"+embedded.attr("data-tweet-id") : "");
      }
      else if (e.type === "mouseleave"){
        highlightedTweetEle = null;
        updateHighlightedTweet("", "");
      }
    });
  })();

  //
  // Block: Paste images when tweeting.
  //
  (function(){
    var lastPasteElement;
    var prevScrollTop;

    var getScroller = function(){
      return $(".js-drawer").find(".js-compose-scroller").first().children().first();
    };

    var clickUpload = function(){
      var button = $(".js-add-image-button").first();

      var scroller = getScroller();
      prevScrollTop = scroller.scrollTop();

      scroller.scrollTop(0);
      scroller.scrollTop(button.offset().top); // scrolls the button into view

      var buttonPos = button.children().first().offset(); // finds the camera icon offset
      $TD.clickUploadImage(Math.floor(buttonPos.left), Math.floor(buttonPos.top));
    };

    $(".js-app").delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(){
      lastPasteElement = $(this);
      $TD.tryPasteImage();
    });

    window.TDGF_tryPasteImage = function(){
      if (lastPasteElement){
        var parent = lastPasteElement.parent();

        if (parent.siblings(".js-add-image-button").length === 0){
          var pop = parent.closest(".js-inline-reply,.rpl").find(".js-inline-compose-pop,.js-reply-popout");

          if (pop.length === 0){
            lastPasteElement = null;
            return;
          }

          pop.click();

          var drawer = $(".js-drawer");
          var counter = 0;

          var interval = setInterval(function(){
            if (drawer.offset().left >= 195){
              clickUpload();
              clearInterval(interval);
            }
            else if (++counter >= 10){
              clearInterval(interval);
            }
          }, 51);
        }
        else{
          clickUpload();
        }

        lastPasteElement = null;
      }
    };

    window.TDGF_tryPasteImageFinish = function(){
      setTimeout(function(){
        getScroller().scrollTop(prevScrollTop);
        $(".js-drawer").find(".js-compose-text").first()[0].focus();
      }, 10);
    };
  })();

  //
  // Block: Support for extra mouse buttons.
  //
  window.TDGF_onMouseClickExtra = function(button){
    if (button === 1){ // back button
      var modal = $("#open-modal");

      if (highlightedColumnEle && highlightedColumnEle.closest(".js-column").is(".is-shifted-1")){
        highlightedColumnEle.find(".js-column-back").first().click();
      }
      else if (modal.is(":visible")){
        modal.find("a[rel=dismiss]").click();
      }
      else{
        $(".js-column-back").click();
      }
    }
    else if (button === 2){ // forward button
      if (highlightedTweetEle){
        highlightedTweetEle.children().first().click();
      }
    }
  };

  //
  // Block: Fix scheduled tweets not showing up sometimes.
  //
  $(document).on("dataTweetSent", function(e, data){
    if (data.response.state && data.response.state === "scheduled"){
      var column = Object.values(TD.controller.columnManager.getAll()).find(column => column.model.state.type === "scheduled");

      if (column){
        setTimeout(function(){
          column.reloadTweets();
        }, 1000);
      }
    }
  });

  //
  // Block: Inject custom CSS and layout into the page.
  //
  (function(){
    var styleOfficial = document.createElement("style");
    document.head.appendChild(styleOfficial);

    styleOfficial.sheet.insertRule("a[data-full-url] { word-break: break-all; }", 0); // break long urls
    styleOfficial.sheet.insertRule(".column-nav-link .attribution { position: absolute; }", 0); // fix cut off account names
    styleOfficial.sheet.insertRule(".txt-base-smallest .badge-verified:before { height: 13px !important; }", 0); // fix cut off badge icon
    styleOfficial.sheet.insertRule(".keyboard-shortcut-list { vertical-align: top; }", 0); // fix keyboard navigation alignment

    if ($TD.hasCustomBrowserCSS){
      var styleCustom = document.createElement("style");
      styleCustom.innerHTML = $TD.customBrowserCSS;
      document.head.appendChild(styleCustom);
    }
  })();
})($, $TD, TD);
<|endoftext|>