<commit_before>//This is the baby of Wei Lu
<<<<<<< HEAD

=======
//Tue 6:35pm
>>>>>>> FETCH_HEAD
DECK_NUM = 2;
SUIT_NUM = 4;
VALUE_NUM = 13;
JOKER_NUM =2;
ALL_SUIT =['jokers','spades','hearts','diamonds','clubs'];
function Card(my_suit,my_value){
    this.suit = my_suit;
    this.value = my_value;

}

function playerProperty(players){
    for(var i = 0 ;i < players.length; i++){
        players[i].cards = [];
        players[i].suit = new Array(4);
        for(var j =0 ;j<ALL_SUIT.length ; j++){
            players[i].suit[j] = [];
        }
        players[i].points = 0;  //point is for this game
        players[i].score = 2;   //score is for the whole game
        players[i].declarer = -1;  // 0 is false, 1 is true, -1 is undefined.
        //players[i].mynum = -1;
    }
}
function shuffle(array) {
    var m = array.length, t, i;

    // While there remain elements to shuffle…
    while (m) {

        // Pick a remaining element…
        i = Math.floor(Math.random() * m--);

        // And swap it with the current element.
        t = array[m];
        array[m] = array[i];
        array[i] = t;
    }
    return array;
}

function Deck(){
    var deck = [];

    for(var i = 0; i <DECK_NUM; i++ ){
        for ( var j = 1 ; j<= SUIT_NUM; j++){
            for( var k =0 ; k < VALUE_NUM ; k++){
                deck.push(new Card(ALL_SUIT[j],k+1))
            }
        }
        for( var j =0; j < JOKER_NUM ; j ++){
            deck.push(new Card(ALL_SUIT[0],j+1))
        }
    }
    shuffle(deck);
    return deck;
}

function GameInfo(){
    this.dominantSuit = 'unknown';
    this.dominantRank = 2;
    //this.starter = -1; //should be one of player 0 to 3
    this.tempPos = -1;
    this.count = -1;
    this.cardsLeft = -1;
    this.firstgame = false;  //should initial as true
    this.dealer = -1;
    this.leader = -1;
    this.currentPlayer = -1;
    this.kitty = [];

}

function Dealing(players,gameInfo){
    debug('Start Dealing');
    var deck = Deck();
    debug('deck length is ' + deck.length);
    var i = gameInfo.dealer;
    for(var j = 0;j < 4; j++){
        players[j].on('DominantSuitIn',function(card){
            //TODO: some checking
            gameInfo.dominantSuit = card.suit;
            debug('dominantSuit now is ' + gameInfo.dominantSuit);
        })
    }
    next(deck,i,players,gameInfo);

}

function next(deck,i,players,gameInfo){
    //debug(deck.length);
    if(deck.length>8){
        var card = deck.shift();
        sendCard(card,players[i], gameInfo.dominantRank,function(result){
            i++;
            i = i%4;
            next(deck,i,players,gameInfo);
        })
    }
    else{
        //all give to the dealer
        var j = gameInfo.dealer;
        //check
        if(j != i){
            debug('1bad');
            debug('j is ' +j + ' i is ' + i);
        }

        for(var i =0;i<deck.length;i++){
            addCard(players[j],deck[i]);
        }

        for(var i =0 ;i<4 ;i++){
            updateHand(players[i]);
        }
        //updateHand(players[j]);
        //sortCards(players);
        debug('Dealing Done');

        playing(players,gameInfo)

    }
}

function addCard(player,card){
    var num = 0;
    var done = false;
    for(var i =0 ;i< ALL_SUIT.length;i++){
        if(ALL_SUIT[i] === card.suit){
            player.suit[i].push(card);
            player.suit[i].sort(function(a,b){
                if(a.suit === 'jokers'){
                    return b.value - a.value
                }
                if (a.value === 1){
                    return false;
                }
                else if (b.value === 1){
                    return true;
                }
                else{
                    return b.value - a.value
                }
            });
            var temp = find(player,card);
            num += temp[1];
            done = true;

        }
        else{
            if(done === false){
                num += player.suit[i].length;
            }
        }
    }
    player.emit('newcard',card,num);

}

function sendCard(card,player,dominantRank,callback){
    addCard(player,card);

    var time = 0.01*1000;  // 0.01s
    //var time = 0.25*1000;
    var IsDominantSuit = false;
    if(card.value === dominantRank && card.suit != 'jokers'){
        player.emit('declaration');
        //player.broadcast.to(player.game).emit('declarationOff');
        //time = 0.5*1000 //10s;
        IsDominantSuit = true;
    }
    else{
        //player.emit('declarationOff');
    }

    setTimeout(function() {
        callback(IsDominantSuit); }, time);
}

function find(player,target){
    for(var i = 0; i< 5; i++){
        if(ALL_SUIT[i] === target.suit){
            for(var j =0 ;j< player.suit[i].length ; j++){
                if(target.value === player.suit[i][j].value){
                    return [i,j];
                }
            }
        }
    }
    return [-1,-1];
}

function debug(message){
    console.log(message +'---------------------------------');
}

function nextPlayer(i){
    return (i+1)%4;
}

function playing(players,gameInfo){
    console.log('OK. please start your trick');
    var done = false;

    gameInfo.count = 0;

    var pos = gameInfo.dealer;
    gameInfo.currentPlayer = gameInfo.dealer;
    var player = players[pos];
    player.emit('go');
    player.broadcast.to(player.game).emit('stop');
    var n = 0;
    while( n < 4){
        n++;
        var player = players[pos];

        pos = nextPlayer(pos);
        player.on('usecard', function(result) {
            var player = players[gameInfo.currentPlayer];
            debug('gamecore:: ' + player.userid );
            var cardsCombination = [];
            for(var i = 0; i< result.length ; i++){
                //result[i].value = parseInt(result[i].value);
                var temp = new Card(result[i].suit, result[i].value);
                cardsCombination.push(temp);
                console.log( ' used card ' + temp.suit + ' ' + temp.value);
            }
            //cardsCombination.push(oneCard);
            var isLegal = deleteHand(player , cardsCombination);  // -1 means not legal
            // if want he want to play is not legal. Tell him.
            //deleteHand(player , cardsCombination);
            if (isLegal === -1){
                players[i].emit('DoAgain');
            }
            else{
                //debug(5);
                updateHand(player);
                player.broadcast.to(player.game).emit('otherTricks',result,gameInfo.currentPlayer);
                player.emit('otherTricks',result,gameInfo.currentPlayer);

                player.emit('stop');
                //next
                gameInfo.count++;
                if(gameInfo.count === 4){
                    // new
                    gameInfo.count = 0;
                    gameInfo.currentPlayer = gameInfo.leader;
                }
                else if(gameInfo.count < 4){
                    gameInfo.currentPlayer =nextPlayer(gameInfo.currentPlayer);
                }
                var myPos = gameInfo.currentPlayer;
                players[myPos].emit('go');

            }
        })
    }
}

function deleteHand(player,cardsCombination){
    console.log(cardsCombination);
    //console.log(player.cards);

    // first make sure you have the cards you want to play
    var cardsPosition = [];  // store the position of the cards in my hands
    for(var i = 0; i<cardsCombination.length; i++){
        var index = find(player,cardsCombination[i]);
        if(index[0] === -1){
            debug('not found');
            return -1;  // card not exists
        }
        cardsPosition.push(index);
    }

    //check all the rules, make sure it's legal

    //it's legal, so delete the cards in my hands.
    for(var i =0;i<cardsCombination.length;i++){
        var index = find(player,cardsCombination[i]);
        player.suit[index[0]].splice(index[1],1);
        //console.log(player.cards);
        //console.log(index);
        //console.log('length '+ player.cards.length);
        debug('deletedone');

    }
    return 1;  //means good
}

function updateHand(player){
    player.cards = [];
    for(var i = 0; i< ALL_SUIT.length; i++){
        for(var j =0 ; j< player.suit[i].length; j++){
            player.cards.push(player.suit[i][j])
        }
    }
    //console.log(player.cards);
    //console.log(player.suit[0]);
    player.emit('updateHand', player.cards);
}

function updateScore(players){

}

function kitty(player,gameInfo){
    player.emit('kitty');
    //TODO: kitty
}

function One_game(players,gameInfo){
    if(gameInfo.firstgame === false){
        debug('three');
        var i = gameInfo.dealer;
        gameInfo.dominantRank = players[i].score;
        players[i].emit('dealer');
        players[(i+2)%4].emit('defender');
        players[(i+1)%4].emit('attacker');
        players[(i+3)%4].emit('attacker');
        players[i].declarer = 1;
        players[(i+2)%4].declarer = 0;
        players[(i+1)%4].declarer = 1;
        players[(i+3)%4].declarer = 0;
    }

    Dealing(players,gameInfo);
    kitty(players[gameInfo.dealer],gameInfo);
    // updateScore(players);
    //playing(players,gameInfo);
}

var game_core= function (game_instance) {
    //Store the instance, if any
    this.instance = game_instance;
    //console.log(game_instance.id)
    //console.log(game_instance);
    //Store a flag if we are the server
    this.server = this.instance !== undefined;
    var players = [];
    for(var i = 0 ; i< 3; i++){
        players[i] = game_instance.player_client[i];
        players[i].emit('initial');
    }
    players[3]=game_instance.player_host;
    players[3].emit('initial');
    playerProperty(players);
    //console.log(players)
    var gameInfo = new GameInfo();
    gameInfo.dealer = 0;  // 0 ,1, 2, 3
    gameInfo.leader = gameInfo.dealer;
    One_game(players,gameInfo);

};

//server side we set the 'game_core' class to a global type, so that it can use it anywhere.
if( 'undefined' != typeof global ) {
    module.exports = global.game_core = game_core;
}

<commit_msg>Revert "jj"<commit_after>//This is the baby of Wei Lu

DECK_NUM = 2;
SUIT_NUM = 4;
VALUE_NUM = 13;
JOKER_NUM =2;
ALL_SUIT =['jokers','spades','hearts','diamonds','clubs'];
function Card(my_suit,my_value){
    this.suit = my_suit;
    this.value = my_value;

}

function playerProperty(players){
    for(var i = 0 ;i < players.length; i++){
        players[i].cards = [];
        players[i].suit = new Array(4);
        for(var j =0 ;j<ALL_SUIT.length ; j++){
            players[i].suit[j] = [];
        }
        players[i].points = 0;  //point is for this game
        players[i].score = 2;   //score is for the whole game
        players[i].declarer = -1;  // 0 is false, 1 is true, -1 is undefined.
        //players[i].mynum = -1;
    }
}
function shuffle(array) {
    var m = array.length, t, i;

    // While there remain elements to shuffle…
    while (m) {

        // Pick a remaining element…
        i = Math.floor(Math.random() * m--);

        // And swap it with the current element.
        t = array[m];
        array[m] = array[i];
        array[i] = t;
    }
    return array;
}

function Deck(){
    var deck = [];

    for(var i = 0; i <DECK_NUM; i++ ){
        for ( var j = 1 ; j<= SUIT_NUM; j++){
            for( var k =0 ; k < VALUE_NUM ; k++){
                deck.push(new Card(ALL_SUIT[j],k+1))
            }
        }
        for( var j =0; j < JOKER_NUM ; j ++){
            deck.push(new Card(ALL_SUIT[0],j+1))
        }
    }
    shuffle(deck);
    return deck;
}

function GameInfo(){
    this.dominantSuit = 'unknown';
    this.dominantRank = 2;
    //this.starter = -1; //should be one of player 0 to 3
    this.tempPos = -1;
    this.count = -1;
    this.cardsLeft = -1;
    this.firstgame = false;  //should initial as true
    this.dealer = -1;
    this.leader = -1;
    this.currentPlayer = -1;
    this.kitty = [];

}

function Dealing(players,gameInfo){
    debug('Start Dealing');
    var deck = Deck();
    debug('deck length is ' + deck.length);
    var i = gameInfo.dealer;
    for(var j = 0;j < 4; j++){
        players[j].on('DominantSuitIn',function(card){
            //TODO: some checking
            gameInfo.dominantSuit = card.suit;
            debug('dominantSuit now is ' + gameInfo.dominantSuit);
        })
    }
    next(deck,i,players,gameInfo);

}

function next(deck,i,players,gameInfo){
    //debug(deck.length);
    if(deck.length>8){
        var card = deck.shift();
        sendCard(card,players[i], gameInfo.dominantRank,function(result){
            i++;
            i = i%4;
            next(deck,i,players,gameInfo);
        })
    }
    else{
        //all give to the dealer
        var j = gameInfo.dealer;
        //check
        if(j != i){
            debug('1bad');
            debug('j is ' +j + ' i is ' + i);
        }

        for(var i =0;i<deck.length;i++){
            addCard(players[j],deck[i]);
        }

        for(var i =0 ;i<4 ;i++){
            updateHand(players[i]);
        }
        //updateHand(players[j]);
        //sortCards(players);
        debug('Dealing Done');

        playing(players,gameInfo)

    }
}

function addCard(player,card){
    var num = 0;
    var done = false;
    for(var i =0 ;i< ALL_SUIT.length;i++){
        if(ALL_SUIT[i] === card.suit){
            player.suit[i].push(card);
            player.suit[i].sort(function(a,b){
                if(a.suit === 'jokers'){
                    return b.value - a.value
                }
                if (a.value === 1){
                    return false;
                }
                else if (b.value === 1){
                    return true;
                }
                else{
                    return b.value - a.value
                }
            });
            var temp = find(player,card);
            num += temp[1];
            done = true;

        }
        else{
            if(done === false){
                num += player.suit[i].length;
            }
        }
    }
    player.emit('newcard',card,num);

}

function sendCard(card,player,dominantRank,callback){
    addCard(player,card);

    var time = 0.01*1000;  // 0.01s
    //var time = 0.25*1000;
    var IsDominantSuit = false;
    if(card.value === dominantRank && card.suit != 'jokers'){
        player.emit('declaration');
        //player.broadcast.to(player.game).emit('declarationOff');
        //time = 0.5*1000 //10s;
        IsDominantSuit = true;
    }
    else{
        //player.emit('declarationOff');
    }

    setTimeout(function() {
        callback(IsDominantSuit); }, time);
}

function find(player,target){
    for(var i = 0; i< 5; i++){
        if(ALL_SUIT[i] === target.suit){
            for(var j =0 ;j< player.suit[i].length ; j++){
                if(target.value === player.suit[i][j].value){
                    return [i,j];
                }
            }
        }
    }
    return [-1,-1];
}

function debug(message){
    console.log(message +'---------------------------------');
}

function nextPlayer(i){
    return (i+1)%4;
}

function playing(players,gameInfo){
    console.log('OK. please start your trick');
    var done = false;

    gameInfo.count = 0;

    var pos = gameInfo.dealer;
    gameInfo.currentPlayer = gameInfo.dealer;
    var player = players[pos];
    player.emit('go');
    player.broadcast.to(player.game).emit('stop');
    var n = 0;
    while( n < 4){
        n++;
        var player = players[pos];

        pos = nextPlayer(pos);
        player.on('usecard', function(result) {
            var player = players[gameInfo.currentPlayer];
            debug('gamecore:: ' + player.userid );
            var cardsCombination = [];
            for(var i = 0; i< result.length ; i++){
                //result[i].value = parseInt(result[i].value);
                var temp = new Card(result[i].suit, result[i].value);
                cardsCombination.push(temp);
                console.log( ' used card ' + temp.suit + ' ' + temp.value);
            }
            //cardsCombination.push(oneCard);
            var isLegal = deleteHand(player , cardsCombination);  // -1 means not legal
            // if want he want to play is not legal. Tell him.
            //deleteHand(player , cardsCombination);
            if (isLegal === -1){
                players[i].emit('DoAgain');
            }
            else{
                //debug(5);
                updateHand(player);
                player.broadcast.to(player.game).emit('otherTricks',result,gameInfo.currentPlayer);
                player.emit('otherTricks',result,gameInfo.currentPlayer);

                player.emit('stop');
                //next
                gameInfo.count++;
                if(gameInfo.count === 4){
                    // new
                    gameInfo.count = 0;
                    gameInfo.currentPlayer = gameInfo.leader;
                }
                else if(gameInfo.count < 4){
                    gameInfo.currentPlayer =nextPlayer(gameInfo.currentPlayer);
                }
                var myPos = gameInfo.currentPlayer;
                players[myPos].emit('go');

            }
        })
    }
}

function deleteHand(player,cardsCombination){
    console.log(cardsCombination);
    //console.log(player.cards);

    // first make sure you have the cards you want to play
    var cardsPosition = [];  // store the position of the cards in my hands
    for(var i = 0; i<cardsCombination.length; i++){
        var index = find(player,cardsCombination[i]);
        if(index[0] === -1){
            debug('not found');
            return -1;  // card not exists
        }
        cardsPosition.push(index);
    }

    //check all the rules, make sure it's legal

    //it's legal, so delete the cards in my hands.
    for(var i =0;i<cardsCombination.length;i++){
        var index = find(player,cardsCombination[i]);
        player.suit[index[0]].splice(index[1],1);
        //console.log(player.cards);
        //console.log(index);
        //console.log('length '+ player.cards.length);
        debug('deletedone');

    }
    return 1;  //means good
}

function updateHand(player){
    player.cards = [];
    for(var i = 0; i< ALL_SUIT.length; i++){
        for(var j =0 ; j< player.suit[i].length; j++){
            player.cards.push(player.suit[i][j])
        }
    }
    //console.log(player.cards);
    //console.log(player.suit[0]);
    player.emit('updateHand', player.cards);
}

function updateScore(players){

}

function kitty(player,gameInfo){
    player.emit('kitty');
    //TODO: kitty
}

function One_game(players,gameInfo){
    if(gameInfo.firstgame === false){
        debug('three');
        var i = gameInfo.dealer;
        gameInfo.dominantRank = players[i].score;
        players[i].emit('dealer');
        players[(i+2)%4].emit('defender');
        players[(i+1)%4].emit('attacker');
        players[(i+3)%4].emit('attacker');
        players[i].declarer = 1;
        players[(i+2)%4].declarer = 0;
        players[(i+1)%4].declarer = 1;
        players[(i+3)%4].declarer = 0;
    }

    Dealing(players,gameInfo);
    kitty(players[gameInfo.dealer],gameInfo);
    // updateScore(players);
    //playing(players,gameInfo);
}

var game_core= function (game_instance) {
    //Store the instance, if any
    this.instance = game_instance;
    //console.log(game_instance.id)
    //console.log(game_instance);
    //Store a flag if we are the server
    this.server = this.instance !== undefined;
    var players = [];
    for(var i = 0 ; i< 3; i++){
        players[i] = game_instance.player_client[i];
        players[i].emit('initial');
    }
    players[3]=game_instance.player_host;
    players[3].emit('initial');
    playerProperty(players);
    //console.log(players)
    var gameInfo = new GameInfo();
    gameInfo.dealer = 0;  // 0 ,1, 2, 3
    gameInfo.leader = gameInfo.dealer;
    One_game(players,gameInfo);

};

//server side we set the 'game_core' class to a global type, so that it can use it anywhere.
if( 'undefined' != typeof global ) {
    module.exports = global.game_core = game_core;
}

<|endoftext|>


<commit_before>// register the directive with your app module
var app = angular.module('ddApp', ['ngAnimate', 'ui.bootstrap', 'rzModule']);

// function referenced by the drop target
app.controller('ddController', ['$scope', '$http', '$log', function($scope, $http, $log){

    $scope.sliderOptions = {
        floor: 0,
        ceil: 0,
        showSelectionBar: true,
        showTicksValues: true
    };

    $scope.visType = "slider";
    $scope.z = 0;
    $scope.tiles = {};
    $http.get("/api/runs/570eb85c682468cf3e194637?populate=true").then(function(response){
        $scope.height = response.data.height;
        $scope.sliderOptions.ceil = $scope.height - 1;
        $scope.width = response.data.width;
        $scope.length = response.data.length;
        $scope.team = response.data.team;
        $scope.field = response.data.field;

        $scope.numberOfDropTiles = response.data.numberOfDropTiles;;
        $scope.rescuedVictims = response.data.rescuedVictims;

        for(var i = 0; i < response.data.tiles.length; i++){
            $scope.tiles[response.data.tiles[i].x + ',' +
                         response.data.tiles[i].y + ',' +
                         response.data.tiles[i].z] = response.data.tiles[i];
        }

        $scope.score = response.data.score;
        $scope.showedUp = response.data.showedUp;
        $scope.LoPs = response.data.LoPs;
        // Verified time by timekeeper
        $scope.minutes = response.data.time.minutes;;
        $scope.seconds = response.data.time.seconds;

        console.log($scope.tiles);
    }, function(response){
        console.log("Error: " + response.statusText);
    });

    (function launchSocketIo() {
        // launch socket.io
        var socket = io.connect(window.location.origin);
        socket.emit('subscribe', 'runs/' + runId);
        socket.on('data', function(data) {
            $scope.rescuedVictims = data.rescuedVictims;

            for(var i = 0; i < data.tiles.length; i++){
                $scope.tiles[data.tiles[i].x + ',' +
                             data.tiles[i].y + ',' +
                             data.tiles[i].z].scoredItems = data.tiles[i].scoredItems;
         }
            $scope.score = data.score;
            $scope.showedUp = data.showedUp;
            $scope.LoPs = data.LoPs;
            $scope.minutes = data.time.minutes;;
            $scope.seconds = data.time.seconds;
            $scope.$apply();
            console.log("Updated view from socket.io");
        });
    })();

    $scope.range = function(n){
        arr = [];
        for (var i=0; i < n; i++) {
            arr.push(i);
        }
        return arr;
    }

    $scope.getOpacity = function(x,y){
        var stackedTiles = 0;
        for(var z = 0; z < $scope.height; z++){
            if($scope.tiles[x+','+y+','+z])
                stackedTiles++;
        }
        return 1.0/stackedTiles;
    }

}]);

app.directive('tile', function() {
    return {
        scope: {
            tile: '='
        },
        restrict: 'E',
        templateUrl: '/templates/tile.html',
        link : function($scope, element, attrs){

            $scope.tileStatus = function(tile){
                // If this is a non-existent tile
                if(!tile)
                    return;
                var successfully = 0;
                var possible = 0;

                var count = function(list){
                    for(var i = 0; i < list.length; i++){
                        if(list[i])
                            successfully++;
                        possible++;
                    }
                }
                count(tile.scoredItems.gaps);
                count(tile.scoredItems.speedbumps);
                count(tile.scoredItems.intersections);
                count(tile.scoredItems.obstacles);
                if(tile.scoredItems.dropTiles.length > 0)
                    count(tile.scoredItems.dropTiles);

                if(possible > 0 && successfully == possible)
                    return "done";
                else if(successfully > 0)
                    return "halfdone";
                else if(possible > 0)
                    return "undone";
                else
                    return "";
            }

            $scope.rotateRamp = function(direction){
                switch(direction){
                case "bottom":
                    return "rot0";
                case "top":
                    return "rot180";
                case "left":
                    return "rot90";
                case "right":
                    return "rot270";
                }
            }
        }
    };
});
<commit_msg>Removed static runID<commit_after>// register the directive with your app module
var app = angular.module('ddApp', ['ngAnimate', 'ui.bootstrap', 'rzModule']);

// function referenced by the drop target
app.controller('ddController', ['$scope', '$http', '$log', function($scope, $http, $log){

    $scope.sliderOptions = {
        floor: 0,
        ceil: 0,
        showSelectionBar: true,
        showTicksValues: true
    };

    $scope.visType = "slider";
    $scope.z = 0;
    $scope.tiles = {};
    $http.get("/api/runs/"+runId+"?populate=true").then(function(response){
        $scope.height = response.data.height;
        $scope.sliderOptions.ceil = $scope.height - 1;
        $scope.width = response.data.width;
        $scope.length = response.data.length;
        $scope.team = response.data.team;
        $scope.field = response.data.field;

        $scope.numberOfDropTiles = response.data.numberOfDropTiles;;
        $scope.rescuedVictims = response.data.rescuedVictims;

        for(var i = 0; i < response.data.tiles.length; i++){
            $scope.tiles[response.data.tiles[i].x + ',' +
                         response.data.tiles[i].y + ',' +
                         response.data.tiles[i].z] = response.data.tiles[i];
        }

        $scope.score = response.data.score;
        $scope.showedUp = response.data.showedUp;
        $scope.LoPs = response.data.LoPs;
        // Verified time by timekeeper
        $scope.minutes = response.data.time.minutes;;
        $scope.seconds = response.data.time.seconds;

        console.log($scope.tiles);
    }, function(response){
        console.log("Error: " + response.statusText);
    });

    (function launchSocketIo() {
        // launch socket.io
        var socket = io.connect(window.location.origin);
        socket.emit('subscribe', 'runs/' + runId);
        socket.on('data', function(data) {
            $scope.rescuedVictims = data.rescuedVictims;

            for(var i = 0; i < data.tiles.length; i++){
                $scope.tiles[data.tiles[i].x + ',' +
                             data.tiles[i].y + ',' +
                             data.tiles[i].z].scoredItems = data.tiles[i].scoredItems;
         }
            $scope.score = data.score;
            $scope.showedUp = data.showedUp;
            $scope.LoPs = data.LoPs;
            $scope.minutes = data.time.minutes;;
            $scope.seconds = data.time.seconds;
            $scope.$apply();
            console.log("Updated view from socket.io");
        });
    })();

    $scope.range = function(n){
        arr = [];
        for (var i=0; i < n; i++) {
            arr.push(i);
        }
        return arr;
    }

    $scope.getOpacity = function(x,y){
        var stackedTiles = 0;
        for(var z = 0; z < $scope.height; z++){
            if($scope.tiles[x+','+y+','+z])
                stackedTiles++;
        }
        return 1.0/stackedTiles;
    }

}]);

app.directive('tile', function() {
    return {
        scope: {
            tile: '='
        },
        restrict: 'E',
        templateUrl: '/templates/tile.html',
        link : function($scope, element, attrs){

            $scope.tileStatus = function(tile){
                // If this is a non-existent tile
                if(!tile)
                    return;
                var successfully = 0;
                var possible = 0;

                var count = function(list){
                    for(var i = 0; i < list.length; i++){
                        if(list[i])
                            successfully++;
                        possible++;
                    }
                }
                count(tile.scoredItems.gaps);
                count(tile.scoredItems.speedbumps);
                count(tile.scoredItems.intersections);
                count(tile.scoredItems.obstacles);
                if(tile.scoredItems.dropTiles.length > 0)
                    count(tile.scoredItems.dropTiles);

                if(possible > 0 && successfully == possible)
                    return "done";
                else if(successfully > 0)
                    return "halfdone";
                else if(possible > 0)
                    return "undone";
                else
                    return "";
            }

            $scope.rotateRamp = function(direction){
                switch(direction){
                case "bottom":
                    return "rot0";
                case "top":
                    return "rot180";
                case "left":
                    return "rot90";
                case "right":
                    return "rot270";
                }
            }
        }
    };
});
<|endoftext|>


<commit_before>//= require "autocomplete"
//= require "../chrome/esc"

var keyboardHelpVisible = false;

$body.keydown(keycontrol);

var panelShortcuts = {}
//   49: 'javascript', // 1
//   50: 'css', // 2
//   51: 'html', // 3
//   52: 'console', // 4
//   53: 'live' // 5
// };
panelShortcuts.start = 48;

// work out the browser platform
var ua = navigator.userAgent;
if (/macintosh|mac os x/.test(ua)) {
  $.browser.platform = 'mac';
} else if (/windows|win32/.test(ua)) {
  $.browser.platform = 'win';
} else if (/linux/.test(ua)) {
  $.browser.platform = 'linux';
} else {
  $.browser.platform = '';
}

// var closekey = $.browser.platform == 'mac' ? 167 : 192;

$document.keydown(function (event) {
  if (event.ctrlKey) event.metaKey = true;

  var customKeys = objectValue('jsbin.settings.keys') || {},
      includeAltKey = customKeys.useAlt ? event.altKey : true,
      closekey = customKeys.closePanel ? customKeys.closePanel : 192;

  if (event.metaKey && event.which == 83) {
    if (event.shiftKey == false) {
      $('#save').click();
      event.preventDefault();
    } else if (event.shiftKey == true) {
      $('.clone').click();
      event.preventDefault();
    }
  } else if (event.which === closekey && event.metaKey && includeAltKey && jsbin.panels.focused) {
    if (jsbin.panels.focused.visible) jsbin.panels.focused.hide();
    var visible = jsbin.panels.getVisible();
    if (visible.length) {
      jsbin.panels.focused = visible[0];
      if (jsbin.panels.focused.editor) {
        jsbin.panels.focused.editor.focus();
      } else {
        jsbin.panels.focused.$el.focus();
      }
      jsbin.panels.focused.focus();
    } else if ($('#history').length && !$body.hasClass('panelsVisible')) {
      $('#history').toggle(100);
    }
  } else if (event.which === 220 && (event.metaKey || event.ctrlKey)) {
    jsbin.settings.hideheader = !jsbin.settings.hideheader;
    $body[jsbin.settings.hideheader ? 'addClass' : 'removeClass']('hideheader');
  } else if (event.which === 76 && event.ctrlKey && jsbin.panels.panels.console.visible) {
    if (event.shiftKey) {
      console.log('reset');
      // reset
      jsconsole.reset();
    } else {
      console.log('clear');
      // clear
      jsconsole.clear();
    }
  }
});

function keycontrol(event) {
  event = normalise(event);

  var panel = {};

  if (jsbin.panels.focused && jsbin.panels.focused.editor) {
    panel = jsbin.panels.focused.editor;
  } else if (jsbin.panels.focused) {
    panel = jsbin.panels.focused;
  }

  var codePanel = { css: 1, javascript: 1, html: 1}[panel.id],
      hasRun = false;

  var includeAltKey = objectValue('jsbin.settings.keys.usealt') ? event.altKey : true;

  // these should fire when the key goes down
  if (event.type == 'keydown') {
    if (codePanel) {
      if (event.metaKey && event.which == 13) {
        if (editors.console.visible && !editors.live.visible) {
          hasRun = true;
          editors.console.render();
        }
        if (editors.live.visible) {
          editors.live.render(true);
          hasRun = true;
        }

        if (hasRun) {
          event.stop();
        }
      }
    }

    // shortcut for showing a panel
    if (panelShortcuts[event.which] !== undefined && event.metaKey && includeAltKey) {
      jsbin.panels.show(panelShortcuts[event.which]);
      event.stop();
    }

    if (event.which == 191 && event.metaKey && event.shiftKey) {
      // show help
      $body.toggleClass('keyboardHelp');
      keyboardHelpVisible = $body.is('.keyboardHelp');
      if (keyboardHelpVisible) {
        analytics.help();
      }
      event.stop();
    } else if (event.which == 27 && keyboardHelpVisible) {
      $body.removeClass('keyboardHelp');
      keyboardHelpVisible = false;
      event.stop();
    } else if (event.which == 27 && jsbin.panels.focused && codePanel) {
      event.stop();
      return startComplete(panel);
    } else if (event.which == 190 && includeAltKey && event.metaKey && panel.id == 'html') {
      // auto close the element
      if (panel.somethingSelected()) return;
      // Find the token at the cursor
      var cur = panel.getCursor(false), token = panel.getTokenAt(cur), tprop = token;
      // If it's not a 'word-style' token, ignore the token.
      if (!/^[\w$_]*$/.test(token.string)) {
        token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
                         className: token.string == "." ? "js-property" : null};
      }

      panel.replaceRange('</' + token.state.htmlState.context.tagName + '>', {line: cur.line, ch: token.end}, {line: cur.line, ch: token.end});
      event.stop();
    } else if (event.which == 188 && event.ctrlKey && event.shiftKey && codePanel) {
      // start a new tag
      event.stop();
      return startTagComplete(panel);
    } else if (event.which == 191 && event.metaKey && codePanel) {
      // auto close the element
      if (panel.somethingSelected()) return;

      var cur = panel.getCursor(false),
          token = panel.getTokenAt(cur),
          type = token && token.state && token.state.htmlState && token.state.htmlState.context && token.state.htmlState.context.tagName ? token.state.htmlState.context.tagName : 'script',
          line = panel.getLine(cur.line);

      if (token && token.state && token.state.htmlState && token.state.htmlState.context == null) {
        type = 'html';
      }

      if (type == 'style') {
        if (line.match(/\s*\/\*/) !== null) {
          // already contains comment - remove
          panel.setLine(cur.line, line.replace(/\/\*\s?/, '').replace(/\s?\*\//, ''));
        } else {
          panel.setLine(cur.line, '/* ' + line + ' */');
        }
      } else if (type == 'script') {
        // FIXME - could put a JS comment next to a <script> tag
        if (line.match(/\s*\/\//) !== null) {
          // already contains comment - remove
          panel.setLine(cur.line, line.replace(/(\s*)\/\/\s?/, '$1'));
        } else {
          panel.setLine(cur.line, '// ' + line);
        }
      } else {
        if (line.match(/\s*<!--/) !== null) {
          // already contains comment - remove
          panel.setLine(cur.line, line.replace(/<!--\s?/, '').replace(/\s?-->/, ''));
        } else {
          panel.setLine(cur.line, '<!-- ' + line + ' -->');
        }
      }

      event.stop();
    }
  }
  // return true;

  if (event.stopping) {
    return false;
  }
}

function normalise(event) {
  var myEvent = {
    type: event.type,
    which: event.which,
    metaKey: event.metaKey,
    shiftKey: event.shiftKey,
    ctrlKey: event.ctrlKey,
    altKey: event.altKey,
    orig: event
  };

  if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
    myEvent.which = event.charCode != null ? event.charCode : event.keyCode;
  }

  // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
  if ( !event.metaKey && event.ctrlKey ) {
    myEvent.metaKey = event.ctrlKey;
  }

  // this is retarded - I'm having to mess with the event just to get Firefox
  // to send through the right value. i.e. when you include a shift key modifier
  // in Firefox, if it's punctuation - event.which is zero :(
  // Note that I'm only doing this for the ? symbol + ctrl + shift
  if (event.which === 0 && event.ctrlKey === true && event.shiftKey === true && event.type == 'keydown') {
    myEvent.which = 191;
  }

  var oldStop = event.stop;
  myEvent.stop = function () {
    myEvent.stopping = true;
    oldStop && oldStop.call(event);
  };

  return myEvent;
}<commit_msg>Open shortcut<commit_after>//= require "autocomplete"
//= require "../chrome/esc"

var keyboardHelpVisible = false;

$body.keydown(keycontrol);

var panelShortcuts = {}
//   49: 'javascript', // 1
//   50: 'css', // 2
//   51: 'html', // 3
//   52: 'console', // 4
//   53: 'live' // 5
// };
panelShortcuts.start = 48;

// work out the browser platform
var ua = navigator.userAgent;
if (/macintosh|mac os x/.test(ua)) {
  $.browser.platform = 'mac';
} else if (/windows|win32/.test(ua)) {
  $.browser.platform = 'win';
} else if (/linux/.test(ua)) {
  $.browser.platform = 'linux';
} else {
  $.browser.platform = '';
}

// var closekey = $.browser.platform == 'mac' ? 167 : 192;

$document.keydown(function (event) {
  if (event.ctrlKey) event.metaKey = true;

  var customKeys = objectValue('jsbin.settings.keys') || {},
      includeAltKey = customKeys.useAlt ? event.altKey : true,
      closekey = customKeys.closePanel ? customKeys.closePanel : 192;

  if (event.metaKey && event.which == 79) {
    $('.homebtn').click();
    event.preventDefault();
  } else if (event.metaKey && event.which == 83) {
    if (event.shiftKey == false) {
      $('#save').click();
      event.preventDefault();
    } else if (event.shiftKey == true) {
      $('.clone').click();
      event.preventDefault();
    }
  } else if (event.which === closekey && event.metaKey && includeAltKey && jsbin.panels.focused) {
    if (jsbin.panels.focused.visible) jsbin.panels.focused.hide();
    var visible = jsbin.panels.getVisible();
    if (visible.length) {
      jsbin.panels.focused = visible[0];
      if (jsbin.panels.focused.editor) {
        jsbin.panels.focused.editor.focus();
      } else {
        jsbin.panels.focused.$el.focus();
      }
      jsbin.panels.focused.focus();
    } else if ($('#history').length && !$body.hasClass('panelsVisible')) {
      $('#history').toggle(100);
    }
  } else if (event.which === 220 && (event.metaKey || event.ctrlKey)) {
    jsbin.settings.hideheader = !jsbin.settings.hideheader;
    $body[jsbin.settings.hideheader ? 'addClass' : 'removeClass']('hideheader');
  } else if (event.which === 76 && event.ctrlKey && jsbin.panels.panels.console.visible) {
    if (event.shiftKey) {
      console.log('reset');
      // reset
      jsconsole.reset();
    } else {
      console.log('clear');
      // clear
      jsconsole.clear();
    }
  }
});

function keycontrol(event) {
  event = normalise(event);

  var panel = {};

  if (jsbin.panels.focused && jsbin.panels.focused.editor) {
    panel = jsbin.panels.focused.editor;
  } else if (jsbin.panels.focused) {
    panel = jsbin.panels.focused;
  }

  var codePanel = { css: 1, javascript: 1, html: 1}[panel.id],
      hasRun = false;

  var includeAltKey = objectValue('jsbin.settings.keys.usealt') ? event.altKey : true;

  // these should fire when the key goes down
  if (event.type == 'keydown') {
    if (codePanel) {
      if (event.metaKey && event.which == 13) {
        if (editors.console.visible && !editors.live.visible) {
          hasRun = true;
          editors.console.render();
        }
        if (editors.live.visible) {
          editors.live.render(true);
          hasRun = true;
        }

        if (hasRun) {
          event.stop();
        }
      }
    }

    // shortcut for showing a panel
    if (panelShortcuts[event.which] !== undefined && event.metaKey && includeAltKey) {
      jsbin.panels.show(panelShortcuts[event.which]);
      event.stop();
    }

    if (event.which == 191 && event.metaKey && event.shiftKey) {
      // show help
      $body.toggleClass('keyboardHelp');
      keyboardHelpVisible = $body.is('.keyboardHelp');
      if (keyboardHelpVisible) {
        analytics.help();
      }
      event.stop();
    } else if (event.which == 27 && keyboardHelpVisible) {
      $body.removeClass('keyboardHelp');
      keyboardHelpVisible = false;
      event.stop();
    } else if (event.which == 27 && jsbin.panels.focused && codePanel) {
      event.stop();
      return startComplete(panel);
    } else if (event.which == 190 && includeAltKey && event.metaKey && panel.id == 'html') {
      // auto close the element
      if (panel.somethingSelected()) return;
      // Find the token at the cursor
      var cur = panel.getCursor(false), token = panel.getTokenAt(cur), tprop = token;
      // If it's not a 'word-style' token, ignore the token.
      if (!/^[\w$_]*$/.test(token.string)) {
        token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
                         className: token.string == "." ? "js-property" : null};
      }

      panel.replaceRange('</' + token.state.htmlState.context.tagName + '>', {line: cur.line, ch: token.end}, {line: cur.line, ch: token.end});
      event.stop();
    } else if (event.which == 188 && event.ctrlKey && event.shiftKey && codePanel) {
      // start a new tag
      event.stop();
      return startTagComplete(panel);
    } else if (event.which == 191 && event.metaKey && codePanel) {
      // auto close the element
      if (panel.somethingSelected()) return;

      var cur = panel.getCursor(false),
          token = panel.getTokenAt(cur),
          type = token && token.state && token.state.htmlState && token.state.htmlState.context && token.state.htmlState.context.tagName ? token.state.htmlState.context.tagName : 'script',
          line = panel.getLine(cur.line);

      if (token && token.state && token.state.htmlState && token.state.htmlState.context == null) {
        type = 'html';
      }

      if (type == 'style') {
        if (line.match(/\s*\/\*/) !== null) {
          // already contains comment - remove
          panel.setLine(cur.line, line.replace(/\/\*\s?/, '').replace(/\s?\*\//, ''));
        } else {
          panel.setLine(cur.line, '/* ' + line + ' */');
        }
      } else if (type == 'script') {
        // FIXME - could put a JS comment next to a <script> tag
        if (line.match(/\s*\/\//) !== null) {
          // already contains comment - remove
          panel.setLine(cur.line, line.replace(/(\s*)\/\/\s?/, '$1'));
        } else {
          panel.setLine(cur.line, '// ' + line);
        }
      } else {
        if (line.match(/\s*<!--/) !== null) {
          // already contains comment - remove
          panel.setLine(cur.line, line.replace(/<!--\s?/, '').replace(/\s?-->/, ''));
        } else {
          panel.setLine(cur.line, '<!-- ' + line + ' -->');
        }
      }

      event.stop();
    }
  }
  // return true;

  if (event.stopping) {
    return false;
  }
}

function normalise(event) {
  var myEvent = {
    type: event.type,
    which: event.which,
    metaKey: event.metaKey,
    shiftKey: event.shiftKey,
    ctrlKey: event.ctrlKey,
    altKey: event.altKey,
    orig: event
  };

  if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
    myEvent.which = event.charCode != null ? event.charCode : event.keyCode;
  }

  // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
  if ( !event.metaKey && event.ctrlKey ) {
    myEvent.metaKey = event.ctrlKey;
  }

  // this is retarded - I'm having to mess with the event just to get Firefox
  // to send through the right value. i.e. when you include a shift key modifier
  // in Firefox, if it's punctuation - event.which is zero :(
  // Note that I'm only doing this for the ? symbol + ctrl + shift
  if (event.which === 0 && event.ctrlKey === true && event.shiftKey === true && event.type == 'keydown') {
    myEvent.which = 191;
  }

  var oldStop = event.stop;
  myEvent.stop = function () {
    myEvent.stopping = true;
    oldStop && oldStop.call(event);
  };

  return myEvent;
}<|endoftext|>


<commit_before>import {
  updateUrlParams,
  removeUrlParams,
  getValueForParam,
} from "./utils/utils.js";
import searchFilterAutocomplete from "./search-filter-autocomplete.js";
import searchFiltersList from "../../api/helpers/search-filters-list.js";

const toArray = nodeList => Array.prototype.slice.call(nodeList);

const searchFilterList = {
	init() {
    searchFilterAutocomplete.init();
		this.searchFiltersFormEl = document.querySelector(".js-search-filter-list");
    this.selectedCountryListEl = document.querySelector(".js-search-filter-autocomplete-list[data-name=country]");
    this.totalFilters = 0;
    const submitBtnEl = document.querySelector(".js-search-filter-modal-show-result-btn");
    const clearFilterBtnEl = document.querySelector(".js-search-filter-modal-clear-btn");

		if (!this.searchFiltersFormEl) return;

		const type = getValueForParam("selectedCategory");
    this.SEARCH_FILTER_KEYS = [];
    searchFiltersList[type].forEach(item => {
      item.fieldNameKeys.forEach(key => this.SEARCH_FILTER_KEYS.push(key));
    });

		this.checkboxEls = toArray(
      this.searchFiltersFormEl.querySelectorAll(".js-keys-list-item input[type=checkbox]")
    );

    submitBtnEl.addEventListener("click", e => this.handleFormSubmit(e));
    clearFilterBtnEl.addEventListener("click", e => this.handleClearAllFilter(e));

    this.updateUIFromUrlParams();
	},

  // handleRemoveSelectedFilter(e) {
  //   e.preventDefault();
  //   // Get DOM of clicked item
  //   const filterEl = document.querySelector(
  //     `.item-${e.target.dataset.sectionKey}`
  //   );

  //   // Remove DOM
  //   filterEl.remove();

  //   // Get the remaining selected filters
  //   removeUrlParams(this.SEARCH_FILTER_KEYS);
  //   const selectedFilters = this.getSelectedFilterState();

  //   // add new filters as params
  //   Object.keys(selectedFilters).forEach(key => {
  //     updateUrlParams(key, selectedFilters[key]);
  //   });

  //   // load new url
  //   location.href = location.href;
  // },

  getState() {
    let selectedFilters = {};
    this.checkboxEls.forEach(el => {
      if (el.checked) {
        let fieldName = el.getAttribute("data-field-name");
        let filterName = el.getAttribute("name");
        selectedFilters = this.collectSelectedFilter(selectedFilters, fieldName, filterName);
      }
    });

    // Manually collect all country fields from autocomplete
    const countryInputEls = toArray(this.selectedCountryListEl.querySelectorAll("input"));
    countryInputEls.forEach(el => {
      let fieldName = "country";
      let filterName = el.getAttribute("value");
      selectedFilters = this.collectSelectedFilter(selectedFilters, fieldName, filterName);
    });

    return selectedFilters;
  },

  collectSelectedFilter(selectedFilters, fieldName, filterName) {
    if (Array.isArray(selectedFilters[fieldName])) {
      selectedFilters[fieldName].push(filterName);
    } else {
      selectedFilters[fieldName] = [filterName];
    }

    return selectedFilters;
  },

  // getSelectedFilterState() {
  //   this.selectedFilterBtnEl = toArray(
  //     this.selectedFilterEl.querySelectorAll(".js-remove-selected-filter-btn")
  //   );

  //   const selectedFilters = {};
  //   this.selectedFilterBtnEl.forEach(el => {
  //     const fieldName = el.getAttribute("data-field-name");
  //     const filterName = el.getAttribute("data-section-key");
  //     if (Array.isArray(selectedFilters[fieldName])) {
  //       selectedFilters[fieldName].push(filterName);
  //     } else {
  //       selectedFilters[fieldName] = [filterName];
  //     }
  //   });
  //   return selectedFilters;
  // },

  updateUIFromUrlParams() {
    const paramsFromUrl = {};
    window.location.search
      .split("?")[1]
      .split("&")
      .map(p => p.split("="))
      .forEach(param => (paramsFromUrl[param[0]] = param[1]));

    Object.keys(paramsFromUrl).forEach(key => {
      const values = paramsFromUrl[key].split(",");
      values.forEach(value => {
        const input = document.getElementById(`${key}[${value}]`);
        if (input) {
          input.checked = true;
          this.totalFilters++;
        }

        // Manually count country
        if (key == "country") {
          searchFilterAutocomplete.addSelectedItem("country", {label: decodeURI(value), value: decodeURI(value)});
          this.totalFilters++;
        }
      });
    });

    this.updateBadge();
    // this.updateChipButtonsState();
  },

  updateBadge() {
    const badgeParentEl = document.querySelector(".js-tab-buttons-button-filter");
    const badgeEl = document.querySelector(".js-total-filter-badge");

    if (this.totalFilters < 1) {
      badgeParentEl.style["justify-content"] = "center";
    } else {
      badgeEl.textContent = this.totalFilters;
      badgeEl.style["display"] = "block";
    }
  },

  // updateChipButtonsState() {
  //   this.chipButtonEls.forEach(el => {
  //     const hasCheckedItems =
  //       el
  //         .closest(".js-search-filters-chip-list-item")
  //         .querySelectorAll("input:checked").length > 0;
  //     if (hasCheckedItems) {
  //       el.classList.add("search-filters-chip-selected");
  //     } else {
  //       el.classList.remove("search-filters-chip-selected");
  //     }
  //   });
  // },

  handleClearAllFilter(e) {
    e.preventDefault();
    const allCheckboxes = toArray(
      this.searchFiltersFormEl.querySelectorAll("input:checked")
    );

    allCheckboxes.forEach(el => (el.checked = false));
    // this.updateChipButtonsState();
    removeUrlParams(this.SEARCH_FILTER_KEYS);
    // load new url without filter
    location.href = location.href;
  },

  // handleClearAllForSection(e) {
  //   e.preventDefault();
  //   const category = e.target.getAttribute("data-field-name");
  //   const allCheckboxesForSection = toArray(
  //     this.searchFiltersFormEl
  //       .querySelector(`.js-keys-list[data-field-name=${category}`)
  //       .querySelectorAll("input")
  //   );
  //   allCheckboxesForSection.forEach(el => (el.checked = false));
  //   removeUrlParams(this.SEARCH_FILTER_KEYS);
  // },

  handleFormSubmit(e) {
    e.preventDefault();
    // remove old filters
    removeUrlParams(this.SEARCH_FILTER_KEYS);
    const selectedFilters = this.getState();
    // add new filters as params
    Object.keys(selectedFilters).forEach(key => {
      updateUrlParams(key, selectedFilters[key]);
    });
    // load new url
    location.href = location.href;
  },

  // handleChipButtonClick(e) {
  //   e.preventDefault();

  //   const allPopOvers = toArray(
  //     document.querySelectorAll(".js-filter-list-pop-over")
  //   );
  //   const currentPopOverEl = e.target
  //     .closest("li")
  //     .querySelector(".js-filter-list-pop-over");
  //   const currentButton = e.target;

  //   // close all other open pop overs before opening the current clicked one
  //   allPopOvers.forEach(el => {
  //     if (el !== currentPopOverEl) {
  //       el.classList.remove("show-filter-list-popover");
  //     }
  //   });

  //   // remove open class from chip button before opening a new one
  //   this.chipButtonEls.forEach(el =>
  //     el.classList.remove("search-filters-chip-open")
  //   );

  //   this.updateChipButtonsState();

  //   const xPosOfCurrentButton =
  //     currentButton.getBoundingClientRect().x +
  //     currentButton.getBoundingClientRect().width;
  //   const shouldRenderOnRight = window.innerWidth - xPosOfCurrentButton < 300;
  //   const isMobile = window.innerWidth < 801;

  //   // toggle current popover if you click the same chip button again
  //   if (currentPopOverEl.classList.contains("show-filter-list-popover")) {
  //     // hide popover
  //     currentButton.classList.remove("search-filters-chip-open");
  //     currentPopOverEl.classList.remove("show-filter-list-popover");
  //     currentPopOverEl.style.marginLeft = 0;
  //     this.updateChipButtonsState();
  //     document.activeElement.blur();
  //   } else {
  //     // show popover
  //     currentButton.classList.add("search-filters-chip-open");
  //     currentPopOverEl.classList.add("show-filter-list-popover");
  //     if (shouldRenderOnRight && !isMobile) {
  //       const popOverOffset =
  //         currentPopOverEl.clientWidth - currentButton.clientWidth;
  //       currentPopOverEl.style.marginLeft = `-${popOverOffset}px`;
  //     }
  //   }
  // }
};

export default searchFilterList;<commit_msg>Handle filter script if category is "all"<commit_after>import {
  updateUrlParams,
  removeUrlParams,
  getValueForParam,
} from "./utils/utils.js";
import searchFilterAutocomplete from "./search-filter-autocomplete.js";
import searchFiltersList from "../../api/helpers/search-filters-list.js";

const toArray = nodeList => Array.prototype.slice.call(nodeList);

const searchFilterList = {
	init() {
    searchFilterAutocomplete.init();
		this.searchFiltersFormEl = document.querySelector(".js-search-filter-list");
    this.selectedCountryListEl = document.querySelector(".js-search-filter-autocomplete-list[data-name=country]");
    this.totalFilters = 0;
    const submitBtnEl = document.querySelector(".js-search-filter-modal-show-result-btn");
    const clearFilterBtnEl = document.querySelector(".js-search-filter-modal-clear-btn");

		if (!this.searchFiltersFormEl) return;

    this.getFiltersList();

		this.checkboxEls = toArray(
      this.searchFiltersFormEl.querySelectorAll(".js-keys-list-item input[type=checkbox]")
    );

    submitBtnEl.addEventListener("click", e => this.handleFormSubmit(e));
    clearFilterBtnEl.addEventListener("click", e => this.handleClearAllFilter(e));

    this.updateUIFromUrlParams();
	},

  getFiltersList() {
    const type = getValueForParam("selectedCategory");
    this.SEARCH_FILTER_KEYS = [];

    if (type == "all") {
      Object.keys(searchFiltersList).forEach(key => {
        searchFiltersList[key].forEach(item => {
          item.fieldNameKeys.forEach(key => this.SEARCH_FILTER_KEYS.push(key));
        });
      });
    } else {
      searchFiltersList[type].forEach(item => {
        item.fieldNameKeys.forEach(key => this.SEARCH_FILTER_KEYS.push(key));
      });
    }
  },

  // handleRemoveSelectedFilter(e) {
  //   e.preventDefault();
  //   // Get DOM of clicked item
  //   const filterEl = document.querySelector(
  //     `.item-${e.target.dataset.sectionKey}`
  //   );

  //   // Remove DOM
  //   filterEl.remove();

  //   // Get the remaining selected filters
  //   removeUrlParams(this.SEARCH_FILTER_KEYS);
  //   const selectedFilters = this.getSelectedFilterState();

  //   // add new filters as params
  //   Object.keys(selectedFilters).forEach(key => {
  //     updateUrlParams(key, selectedFilters[key]);
  //   });

  //   // load new url
  //   location.href = location.href;
  // },

  getState() {
    let selectedFilters = {};
    this.checkboxEls.forEach(el => {
      if (el.checked) {
        let fieldName = el.getAttribute("data-field-name");
        let filterName = el.getAttribute("name");
        selectedFilters = this.collectSelectedFilter(selectedFilters, fieldName, filterName);
      }
    });

    // Manually collect all country fields from autocomplete
    const countryInputEls = toArray(this.selectedCountryListEl.querySelectorAll("input"));
    countryInputEls.forEach(el => {
      let fieldName = "country";
      let filterName = el.getAttribute("value");
      selectedFilters = this.collectSelectedFilter(selectedFilters, fieldName, filterName);
    });

    return selectedFilters;
  },

  collectSelectedFilter(selectedFilters, fieldName, filterName) {
    if (Array.isArray(selectedFilters[fieldName])) {
      selectedFilters[fieldName].push(filterName);
    } else {
      selectedFilters[fieldName] = [filterName];
    }

    return selectedFilters;
  },

  // getSelectedFilterState() {
  //   this.selectedFilterBtnEl = toArray(
  //     this.selectedFilterEl.querySelectorAll(".js-remove-selected-filter-btn")
  //   );

  //   const selectedFilters = {};
  //   this.selectedFilterBtnEl.forEach(el => {
  //     const fieldName = el.getAttribute("data-field-name");
  //     const filterName = el.getAttribute("data-section-key");
  //     if (Array.isArray(selectedFilters[fieldName])) {
  //       selectedFilters[fieldName].push(filterName);
  //     } else {
  //       selectedFilters[fieldName] = [filterName];
  //     }
  //   });
  //   return selectedFilters;
  // },

  updateUIFromUrlParams() {
    const paramsFromUrl = {};
    window.location.search
      .split("?")[1]
      .split("&")
      .map(p => p.split("="))
      .forEach(param => (paramsFromUrl[param[0]] = param[1]));

    Object.keys(paramsFromUrl).forEach(key => {
      const values = paramsFromUrl[key].split(",");
      values.forEach(value => {
        const input = document.getElementById(`${key}[${value}]`);
        if (input) {
          input.checked = true;
          this.totalFilters++;
        }

        // Manually count country
        if (key == "country") {
          searchFilterAutocomplete.addSelectedItem("country", {label: decodeURI(value), value: decodeURI(value)});
          this.totalFilters++;
        }
      });
    });

    this.updateBadge();
    // this.updateChipButtonsState();
  },

  updateBadge() {
    const badgeParentEl = document.querySelector(".js-tab-buttons-button-filter");
    const badgeEl = document.querySelector(".js-total-filter-badge");

    if (this.totalFilters) {
      badgeEl.textContent = this.totalFilters;
      badgeEl.style["display"] = "block";
    } else {
      badgeParentEl.style["justify-content"] = "center";
    }
  },

  // updateChipButtonsState() {
  //   this.chipButtonEls.forEach(el => {
  //     const hasCheckedItems =
  //       el
  //         .closest(".js-search-filters-chip-list-item")
  //         .querySelectorAll("input:checked").length > 0;
  //     if (hasCheckedItems) {
  //       el.classList.add("search-filters-chip-selected");
  //     } else {
  //       el.classList.remove("search-filters-chip-selected");
  //     }
  //   });
  // },

  handleClearAllFilter(e) {
    e.preventDefault();
    const allCheckboxes = toArray(
      this.searchFiltersFormEl.querySelectorAll("input:checked")
    );

    allCheckboxes.forEach(el => (el.checked = false));
    // this.updateChipButtonsState();
    removeUrlParams(this.SEARCH_FILTER_KEYS);
    // load new url without filter
    location.href = location.href;
  },

  // handleClearAllForSection(e) {
  //   e.preventDefault();
  //   const category = e.target.getAttribute("data-field-name");
  //   const allCheckboxesForSection = toArray(
  //     this.searchFiltersFormEl
  //       .querySelector(`.js-keys-list[data-field-name=${category}`)
  //       .querySelectorAll("input")
  //   );
  //   allCheckboxesForSection.forEach(el => (el.checked = false));
  //   removeUrlParams(this.SEARCH_FILTER_KEYS);
  // },

  handleFormSubmit(e) {
    e.preventDefault();
    // remove old filters
    removeUrlParams(this.SEARCH_FILTER_KEYS);
    const selectedFilters = this.getState();
    // add new filters as params
    Object.keys(selectedFilters).forEach(key => {
      updateUrlParams(key, selectedFilters[key]);
    });
    // load new url
    location.href = location.href;
  },

  // handleChipButtonClick(e) {
  //   e.preventDefault();

  //   const allPopOvers = toArray(
  //     document.querySelectorAll(".js-filter-list-pop-over")
  //   );
  //   const currentPopOverEl = e.target
  //     .closest("li")
  //     .querySelector(".js-filter-list-pop-over");
  //   const currentButton = e.target;

  //   // close all other open pop overs before opening the current clicked one
  //   allPopOvers.forEach(el => {
  //     if (el !== currentPopOverEl) {
  //       el.classList.remove("show-filter-list-popover");
  //     }
  //   });

  //   // remove open class from chip button before opening a new one
  //   this.chipButtonEls.forEach(el =>
  //     el.classList.remove("search-filters-chip-open")
  //   );

  //   this.updateChipButtonsState();

  //   const xPosOfCurrentButton =
  //     currentButton.getBoundingClientRect().x +
  //     currentButton.getBoundingClientRect().width;
  //   const shouldRenderOnRight = window.innerWidth - xPosOfCurrentButton < 300;
  //   const isMobile = window.innerWidth < 801;

  //   // toggle current popover if you click the same chip button again
  //   if (currentPopOverEl.classList.contains("show-filter-list-popover")) {
  //     // hide popover
  //     currentButton.classList.remove("search-filters-chip-open");
  //     currentPopOverEl.classList.remove("show-filter-list-popover");
  //     currentPopOverEl.style.marginLeft = 0;
  //     this.updateChipButtonsState();
  //     document.activeElement.blur();
  //   } else {
  //     // show popover
  //     currentButton.classList.add("search-filters-chip-open");
  //     currentPopOverEl.classList.add("show-filter-list-popover");
  //     if (shouldRenderOnRight && !isMobile) {
  //       const popOverOffset =
  //         currentPopOverEl.clientWidth - currentButton.clientWidth;
  //       currentPopOverEl.style.marginLeft = `-${popOverOffset}px`;
  //     }
  //   }
  // }
};

export default searchFilterList;<|endoftext|>


<commit_before>'use strict';

/**
 *
 * @param {Object} _options configuration parameters
 * @param {String} _options.url  resource URL
 *
 */
function SpeechSynthesis (_options) {

	var options = _options || {
		// TODO: change to production server when released
		url: 'http://localhost:3000'
	}

	this._url = options.url;
	this._audioElement = options.audioElement;
	this._voices = [];

	var xhr = new XMLHttpRequest();
	xhr.open('GET', this._url + '/voices', true);
	xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
	xhr.onreadystatechange = function() {
		if (xhr.readyState === 4) {
			console.log('voices', JSON.parse(xhr.responseText));
			this._voices = JSON.parse(xhr.responseText).voices;
			this.onvoiceschanged();
		}
	}.bind(this);

	xhr.send();

}

// SpeechSynthesis methods
SpeechSynthesis.prototype.getVoices = function() {
	var i;
	var speechSynthesisVoiceCollection = [];
	var voices = this._voices;
	for (i=0; i < voices.length; i++) {
		var voice = voices[i];
		var speechSynthesisVoice = new SpeechSynthesisVoice(voice.url, voice.name, voice.language, voice.gender);
		speechSynthesisVoice.localService = false;
		speechSynthesisVoiceCollection.push(speechSynthesisVoice);
	}
	return speechSynthesisVoiceCollection;
}

SpeechSynthesis.prototype.speak = function(utterance) {
	var xhr = new XMLHttpRequest();
	var audio = this._audioElement || new Audio();
	var url = this._url + '/synthesize?' + serializeQueryString(utterance);
	xhr.open('GET', url, true);
	xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
	xhr.setRequestHeader("Accept", "audio/ogg; codecs=opus");
	xhr.responseType = 'blob';
	xhr.onreadystatechange = function() {
		if (xhr.readyState === 4) {
			var blob = new Blob([this.response], {type: 'audio/ogg'});
			if (!utterance.download) {
				var objectUrl = URL.createObjectURL(blob);
				audio.src = objectUrl;
				audio.play();
				utterance.start();
			} else {
				saveAs(blob, "transcript.ogg");
			}
		}
	};
	xhr.send();
};

// Functions used for speech synthesis  events listeners.
SpeechSynthesis.prototype.onvoiceschanged = function() {};

function SpeechSynthesisVoice(voiceURI, name, lang, _gender) {
	this._gender = _gender;
	this.voiceURI = voiceURI;
	this.name = name;
	this.lang = lang;
}

function SpeechSynthesisUtterance(_options) {
	var options = _options || {};
	this.text = options.text;
	this.lang = options.lang;
	this.voice = options.voice;
}

// Functions used for speech utterance  events listeners.
SpeechSynthesisUtterance.prototype.start = function() {};
SpeechSynthesisUtterance.prototype.end = function() {};
SpeechSynthesisUtterance.prototype.error = function() {};
SpeechSynthesisUtterance.prototype.pause = function() {};
SpeechSynthesisUtterance.prototype.resume = function() {};

/*
 * Utilities
 */
var serializeQueryString = function(obj) {
   var str = Object.keys(obj).map(function(key) {
			return encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
		});
   return str.join("&");
}

/* FileSaver.js
 * A saveAs() FileSaver implementation.
 * 2015-05-07.2
 *
 * By Eli Grey, http://eligrey.com
 * License: X11/MIT
 *   See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
 */

/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */

/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */

var saveAs = saveAs || (function(view) {
	"use strict";
	// IE <10 is explicitly unsupported
	if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
		return;
	}
	var
		  doc = view.document
		  // only get URL when necessary in case Blob.js hasn't overridden it yet
		, get_URL = function() {
			return view.URL || view.webkitURL || view;
		}
		, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
		, can_use_save_link = "download" in save_link
		, click = function(node) {
			var event = doc.createEvent("MouseEvents");
			event.initMouseEvent(
				"click", true, false, view, 0, 0, 0, 0, 0
				, false, false, false, false, 0, null
			);
			node.dispatchEvent(event);
		}
		, webkit_req_fs = view.webkitRequestFileSystem
		, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
		, throw_outside = function(ex) {
			(view.setImmediate || view.setTimeout)(function() {
				throw ex;
			}, 0);
		}
		, force_saveable_type = "application/octet-stream"
		, fs_min_size = 0
		// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and
		// https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047
		// for the reasoning behind the timeout and revocation flow
		, arbitrary_revoke_timeout = 500 // in ms
		, revoke = function(file) {
			var revoker = function() {
				if (typeof file === "string") { // file is an object URL
					get_URL().revokeObjectURL(file);
				} else { // file is a File
					file.remove();
				}
			};
			if (view.chrome) {
				revoker();
			} else {
				setTimeout(revoker, arbitrary_revoke_timeout);
			}
		}
		, dispatch = function(filesaver, event_types, event) {
			event_types = [].concat(event_types);
			var i = event_types.length;
			while (i--) {
				var listener = filesaver["on" + event_types[i]];
				if (typeof listener === "function") {
					try {
						listener.call(filesaver, event || filesaver);
					} catch (ex) {
						throw_outside(ex);
					}
				}
			}
		}
		, auto_bom = function(blob) {
			// prepend BOM for UTF-8 XML and text/* types (including HTML)
			if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
				return new Blob(["\ufeff", blob], {type: blob.type});
			}
			return blob;
		}
		, FileSaver = function(blob, name) {
			blob = auto_bom(blob);
			// First try a.download, then web filesystem, then object URLs
			var
				  filesaver = this
				, type = blob.type
				, blob_changed = false
				, object_url
				, target_view
				, dispatch_all = function() {
					dispatch(filesaver, "writestart progress write writeend".split(" "));
				}
				// on any filesys errors revert to saving with object URLs
				, fs_error = function() {
					// don't create more object URLs than needed
					if (blob_changed || !object_url) {
						object_url = get_URL().createObjectURL(blob);
					}
					if (target_view) {
						target_view.location.href = object_url;
					} else {
						var new_tab = view.open(object_url, "_blank");
						if (new_tab == undefined && typeof safari !== "undefined") {
							//Apple do not allow window.open, see http://bit.ly/1kZffRI
							view.location.href = object_url
						}
					}
					filesaver.readyState = filesaver.DONE;
					dispatch_all();
					revoke(object_url);
				}
				, abortable = function(func) {
					return function() {
						if (filesaver.readyState !== filesaver.DONE) {
							return func.apply(this, arguments);
						}
					};
				}
				, create_if_not_found = {create: true, exclusive: false}
				, slice
			;
			filesaver.readyState = filesaver.INIT;
			if (!name) {
				name = "download";
			}
			if (can_use_save_link) {
				object_url = get_URL().createObjectURL(blob);
				save_link.href = object_url;
				save_link.download = name;
				click(save_link);
				filesaver.readyState = filesaver.DONE;
				dispatch_all();
				revoke(object_url);
				return;
			}
			// Object and web filesystem URLs have a problem saving in Google Chrome when
			// viewed in a tab, so I force save with application/octet-stream
			// http://code.google.com/p/chromium/issues/detail?id=91158
			// Update: Google errantly closed 91158, I submitted it again:
			// https://code.google.com/p/chromium/issues/detail?id=389642
			if (view.chrome && type && type !== force_saveable_type) {
				slice = blob.slice || blob.webkitSlice;
				blob = slice.call(blob, 0, blob.size, force_saveable_type);
				blob_changed = true;
			}
			// Since I can't be sure that the guessed media type will trigger a download
			// in WebKit, I append .download to the filename.
			// https://bugs.webkit.org/show_bug.cgi?id=65440
			if (webkit_req_fs && name !== "download") {
				name += ".download";
			}
			if (type === force_saveable_type || webkit_req_fs) {
				target_view = view;
			}
			if (!req_fs) {
				fs_error();
				return;
			}
			fs_min_size += blob.size;
			req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
				fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
					var save = function() {
						dir.getFile(name, create_if_not_found, abortable(function(file) {
							file.createWriter(abortable(function(writer) {
								writer.onwriteend = function(event) {
									target_view.location.href = file.toURL();
									filesaver.readyState = filesaver.DONE;
									dispatch(filesaver, "writeend", event);
									revoke(file);
								};
								writer.onerror = function() {
									var error = writer.error;
									if (error.code !== error.ABORT_ERR) {
										fs_error();
									}
								};
								"writestart progress write abort".split(" ").forEach(function(event) {
									writer["on" + event] = filesaver["on" + event];
								});
								writer.write(blob);
								filesaver.abort = function() {
									writer.abort();
									filesaver.readyState = filesaver.DONE;
								};
								filesaver.readyState = filesaver.WRITING;
							}), fs_error);
						}), fs_error);
					};
					dir.getFile(name, {create: false}, abortable(function(file) {
						// delete file if it already exists
						file.remove();
						save();
					}), abortable(function(ex) {
						if (ex.code === ex.NOT_FOUND_ERR) {
							save();
						} else {
							fs_error();
						}
					}));
				}), fs_error);
			}), fs_error);
		}
		, FS_proto = FileSaver.prototype
		, saveAs = function(blob, name) {
			return new FileSaver(blob, name);
		}
	;
	// IE 10+ (native saveAs)
	if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
		return function(blob, name) {
			return navigator.msSaveOrOpenBlob(auto_bom(blob), name);
		};
	}

	FS_proto.abort = function() {
		var filesaver = this;
		filesaver.readyState = filesaver.DONE;
		dispatch(filesaver, "abort");
	};
	FS_proto.readyState = FS_proto.INIT = 0;
	FS_proto.WRITING = 1;
	FS_proto.DONE = 2;

	FS_proto.error =
	FS_proto.onwritestart =
	FS_proto.onprogress =
	FS_proto.onwrite =
	FS_proto.onabort =
	FS_proto.onerror =
	FS_proto.onwriteend =
		null;

	return saveAs;
}(
	   typeof self !== "undefined" && self
	|| typeof window !== "undefined" && window
	|| this.content
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window

if (typeof module !== "undefined" && module.exports) {
  module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) {
  define([], function() {
    return saveAs;
  });
}<commit_msg>Add default download value to SpeechUtterance<commit_after>'use strict';

/**
 *
 * @param {Object} _options configuration parameters
 * @param {String} _options.url  resource URL
 *
 */
function SpeechSynthesis (_options) {

	var options = _options || {
		// TODO: change to production server when released
		url: 'http://localhost:3000'
	}

	this._url = options.url;
	this._audioElement = options.audioElement;
	this._voices = [];

	var xhr = new XMLHttpRequest();
	xhr.open('GET', this._url + '/voices', true);
	xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
	xhr.onreadystatechange = function() {
		if (xhr.readyState === 4) {
			this._voices = JSON.parse(xhr.responseText).voices;
			this.onvoiceschanged();
		}
	}.bind(this);

	xhr.send();

}

// SpeechSynthesis methods
SpeechSynthesis.prototype.getVoices = function() {
	var i;
	var speechSynthesisVoiceCollection = [];
	var voices = this._voices;
	for (i=0; i < voices.length; i++) {
		var voice = voices[i];
		var speechSynthesisVoice = new SpeechSynthesisVoice(voice.url, voice.name, voice.language, voice.gender);
		speechSynthesisVoice.localService = false;
		speechSynthesisVoiceCollection.push(speechSynthesisVoice);
	}
	return speechSynthesisVoiceCollection;
}

SpeechSynthesis.prototype.speak = function(utterance) {
	var xhr = new XMLHttpRequest();
	var audio = this._audioElement || new Audio();
	var url = this._url + '/synthesize?' + serializeQueryString(utterance);
	xhr.open('GET', url, true);
	xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
	xhr.setRequestHeader("Accept", "audio/ogg; codecs=opus");
	xhr.responseType = 'blob';
	xhr.onreadystatechange = function() {
		if (xhr.readyState === 4) {
			var blob = new Blob([this.response], {type: 'audio/ogg'});
			if (!utterance.download) {
				var objectUrl = URL.createObjectURL(blob);
				audio.src = objectUrl;
				audio.play();
				utterance.start();
			} else {
				saveAs(blob, "transcript.ogg");
			}
		}
	};
	xhr.send();
};

// Functions used for speech synthesis  events listeners.
SpeechSynthesis.prototype.onvoiceschanged = function() {};

function SpeechSynthesisVoice(voiceURI, name, lang, _gender) {
	this._gender = _gender;
	this.voiceURI = voiceURI;
	this.name = name;
	this.lang = lang;
}

function SpeechSynthesisUtterance(_options) {
	var options = _options || {};
	this.text = options.text;
	this.lang = options.lang;
	this.voice = options.voice;
	this.download = options.download || false;
}

// Functions used for speech utterance  events listeners.
SpeechSynthesisUtterance.prototype.start = function() {};
SpeechSynthesisUtterance.prototype.end = function() {};
SpeechSynthesisUtterance.prototype.error = function() {};
SpeechSynthesisUtterance.prototype.pause = function() {};
SpeechSynthesisUtterance.prototype.resume = function() {};

/*
 * Utilities
 */
var serializeQueryString = function(obj) {
	var str = Object.keys(obj).map(function(key) {
		return encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
	});
	return str.join("&");
}

/* FileSaver.js
 * A saveAs() FileSaver implementation.
 * 2015-05-07.2
 *
 * By Eli Grey, http://eligrey.com
 * License: X11/MIT
 *   See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
 */

/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */

/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */

var saveAs = saveAs || (function(view) {
	"use strict";
	// IE <10 is explicitly unsupported
	if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
		return;
	}
	var
		  doc = view.document
		  // only get URL when necessary in case Blob.js hasn't overridden it yet
		, get_URL = function() {
			return view.URL || view.webkitURL || view;
		}
		, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
		, can_use_save_link = "download" in save_link
		, click = function(node) {
			var event = doc.createEvent("MouseEvents");
			event.initMouseEvent(
				"click", true, false, view, 0, 0, 0, 0, 0
				, false, false, false, false, 0, null
			);
			node.dispatchEvent(event);
		}
		, webkit_req_fs = view.webkitRequestFileSystem
		, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
		, throw_outside = function(ex) {
			(view.setImmediate || view.setTimeout)(function() {
				throw ex;
			}, 0);
		}
		, force_saveable_type = "application/octet-stream"
		, fs_min_size = 0
		// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and
		// https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047
		// for the reasoning behind the timeout and revocation flow
		, arbitrary_revoke_timeout = 500 // in ms
		, revoke = function(file) {
			var revoker = function() {
				if (typeof file === "string") { // file is an object URL
					get_URL().revokeObjectURL(file);
				} else { // file is a File
					file.remove();
				}
			};
			if (view.chrome) {
				revoker();
			} else {
				setTimeout(revoker, arbitrary_revoke_timeout);
			}
		}
		, dispatch = function(filesaver, event_types, event) {
			event_types = [].concat(event_types);
			var i = event_types.length;
			while (i--) {
				var listener = filesaver["on" + event_types[i]];
				if (typeof listener === "function") {
					try {
						listener.call(filesaver, event || filesaver);
					} catch (ex) {
						throw_outside(ex);
					}
				}
			}
		}
		, auto_bom = function(blob) {
			// prepend BOM for UTF-8 XML and text/* types (including HTML)
			if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
				return new Blob(["\ufeff", blob], {type: blob.type});
			}
			return blob;
		}
		, FileSaver = function(blob, name) {
			blob = auto_bom(blob);
			// First try a.download, then web filesystem, then object URLs
			var
				  filesaver = this
				, type = blob.type
				, blob_changed = false
				, object_url
				, target_view
				, dispatch_all = function() {
					dispatch(filesaver, "writestart progress write writeend".split(" "));
				}
				// on any filesys errors revert to saving with object URLs
				, fs_error = function() {
					// don't create more object URLs than needed
					if (blob_changed || !object_url) {
						object_url = get_URL().createObjectURL(blob);
					}
					if (target_view) {
						target_view.location.href = object_url;
					} else {
						var new_tab = view.open(object_url, "_blank");
						if (new_tab == undefined && typeof safari !== "undefined") {
							//Apple do not allow window.open, see http://bit.ly/1kZffRI
							view.location.href = object_url
						}
					}
					filesaver.readyState = filesaver.DONE;
					dispatch_all();
					revoke(object_url);
				}
				, abortable = function(func) {
					return function() {
						if (filesaver.readyState !== filesaver.DONE) {
							return func.apply(this, arguments);
						}
					};
				}
				, create_if_not_found = {create: true, exclusive: false}
				, slice
			;
			filesaver.readyState = filesaver.INIT;
			if (!name) {
				name = "download";
			}
			if (can_use_save_link) {
				object_url = get_URL().createObjectURL(blob);
				save_link.href = object_url;
				save_link.download = name;
				click(save_link);
				filesaver.readyState = filesaver.DONE;
				dispatch_all();
				revoke(object_url);
				return;
			}
			// Object and web filesystem URLs have a problem saving in Google Chrome when
			// viewed in a tab, so I force save with application/octet-stream
			// http://code.google.com/p/chromium/issues/detail?id=91158
			// Update: Google errantly closed 91158, I submitted it again:
			// https://code.google.com/p/chromium/issues/detail?id=389642
			if (view.chrome && type && type !== force_saveable_type) {
				slice = blob.slice || blob.webkitSlice;
				blob = slice.call(blob, 0, blob.size, force_saveable_type);
				blob_changed = true;
			}
			// Since I can't be sure that the guessed media type will trigger a download
			// in WebKit, I append .download to the filename.
			// https://bugs.webkit.org/show_bug.cgi?id=65440
			if (webkit_req_fs && name !== "download") {
				name += ".download";
			}
			if (type === force_saveable_type || webkit_req_fs) {
				target_view = view;
			}
			if (!req_fs) {
				fs_error();
				return;
			}
			fs_min_size += blob.size;
			req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
				fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
					var save = function() {
						dir.getFile(name, create_if_not_found, abortable(function(file) {
							file.createWriter(abortable(function(writer) {
								writer.onwriteend = function(event) {
									target_view.location.href = file.toURL();
									filesaver.readyState = filesaver.DONE;
									dispatch(filesaver, "writeend", event);
									revoke(file);
								};
								writer.onerror = function() {
									var error = writer.error;
									if (error.code !== error.ABORT_ERR) {
										fs_error();
									}
								};
								"writestart progress write abort".split(" ").forEach(function(event) {
									writer["on" + event] = filesaver["on" + event];
								});
								writer.write(blob);
								filesaver.abort = function() {
									writer.abort();
									filesaver.readyState = filesaver.DONE;
								};
								filesaver.readyState = filesaver.WRITING;
							}), fs_error);
						}), fs_error);
					};
					dir.getFile(name, {create: false}, abortable(function(file) {
						// delete file if it already exists
						file.remove();
						save();
					}), abortable(function(ex) {
						if (ex.code === ex.NOT_FOUND_ERR) {
							save();
						} else {
							fs_error();
						}
					}));
				}), fs_error);
			}), fs_error);
		}
		, FS_proto = FileSaver.prototype
		, saveAs = function(blob, name) {
			return new FileSaver(blob, name);
		}
	;
	// IE 10+ (native saveAs)
	if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
		return function(blob, name) {
			return navigator.msSaveOrOpenBlob(auto_bom(blob), name);
		};
	}

	FS_proto.abort = function() {
		var filesaver = this;
		filesaver.readyState = filesaver.DONE;
		dispatch(filesaver, "abort");
	};
	FS_proto.readyState = FS_proto.INIT = 0;
	FS_proto.WRITING = 1;
	FS_proto.DONE = 2;

	FS_proto.error =
	FS_proto.onwritestart =
	FS_proto.onprogress =
	FS_proto.onwrite =
	FS_proto.onabort =
	FS_proto.onerror =
	FS_proto.onwriteend =
		null;

	return saveAs;
}(
	   typeof self !== "undefined" && self
	|| typeof window !== "undefined" && window
	|| this.content
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window

if (typeof module !== "undefined" && module.exports) {
  module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) {
  define([], function() {
    return saveAs;
  });
}<|endoftext|>


<commit_before>// ==UserScript==
// @name       Plex beta update link fixer
// @namespace  http://mathemaniac.org/
// @version    1.0.0
// @description  Plex has been corrupting download links for users on the beta channel on linux. This user-script "turns up the geek", and fixes the links to point to the correct address.
// @match        https://app.plex.tv/desktop*
// @copyright  2022, Sebastian Paaske Tørholm
// @require    https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @grant none
// ==/UserScript==

// See https://forums.plex.tv/t/server-download-link-hiding-makes-it-useless/791780 for context.

/* jshint -W097 */
/* eslint-env jquery */
'use strict';

// Automatically fix broken links back into correct ones - and hide the link in the Shadow DOM, so the token isn't removed again.
function fixLink(elm) {
    let link = elm.href.replace('xxxxxxxxxxxxxxxxxxxx', localStorage.myPlexAccessToken);
    let shadowDOM = elm.parentNode.attachShadow({ mode: 'closed' });
    elm.remove();
    elm.href = link;

    let styleSheets = document.querySelectorAll('link[rel="stylesheet"]');
    for (let ss of styleSheets) {
        shadowDOM.appendChild(ss.cloneNode());
    }

    shadowDOM.appendChild(elm);
}

$(function () {
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    var config = { childList: true, characterData: false, attributes: true, subtree: true };
    var observer = new MutationObserver( function (mutations) {
        mutations.forEach( function (mutation) {
            if (mutation.type == 'childList') {
                for (let m of mutation.addedNodes) {
                    $('a[href^="https://plex.tv/downloads/latest"]', m).each(function () { fixLink(this) });
                }
            } else if (mutation.type == 'attributes') {
                if (mutation.attributeName == 'href' && $(mutation.target).attr('href').match(/^https:\/\/plex\.tv\/downloads\/latest/)) {
                    fixLink(mutation.target);
                }
            }
        });
    });

    observer.observe(document.querySelector('#plex'), config);

    $('a[href^="https://plex.tv/downloads/latest"]').each( function () {
        fixLink(this);
    });
});
<commit_msg>Better description<commit_after>// ==UserScript==
// @name       Plex Media Server beta update link fixer
// @namespace  http://mathemaniac.org/
// @version    1.0.1
// @description  Plex Media Server has been corrupting update download links for users on the beta channel on linux. This user-script "turns up the geek", and fixes the links to point to the correct address.
// @match        https://app.plex.tv/desktop*
// @copyright  2022, Sebastian Paaske Tørholm
// @require    https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @grant none
// ==/UserScript==

// See https://forums.plex.tv/t/server-download-link-hiding-makes-it-useless/791780 for context.

/* jshint -W097 */
/* eslint-env jquery */
'use strict';

// Automatically fix broken links back into correct ones - and hide the link in the Shadow DOM, so the token isn't removed again.
function fixLink(elm) {
    let link = elm.href.replace('xxxxxxxxxxxxxxxxxxxx', localStorage.myPlexAccessToken);
    let shadowDOM = elm.parentNode.attachShadow({ mode: 'closed' });
    elm.remove();
    elm.href = link;

    let styleSheets = document.querySelectorAll('link[rel="stylesheet"]');
    for (let ss of styleSheets) {
        shadowDOM.appendChild(ss.cloneNode());
    }

    shadowDOM.appendChild(elm);
}

$(function () {
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    var config = { childList: true, characterData: false, attributes: true, subtree: true };
    var observer = new MutationObserver( function (mutations) {
        mutations.forEach( function (mutation) {
            if (mutation.type == 'childList') {
                for (let m of mutation.addedNodes) {
                    $('a[href^="https://plex.tv/downloads/latest"]', m).each(function () { fixLink(this) });
                }
            } else if (mutation.type == 'attributes') {
                if (mutation.attributeName == 'href' && $(mutation.target).attr('href').match(/^https:\/\/plex\.tv\/downloads\/latest/)) {
                    fixLink(mutation.target);
                }
            }
        });
    });

    observer.observe(document.querySelector('#plex'), config);

    $('a[href^="https://plex.tv/downloads/latest"]').each( function () {
        fixLink(this);
    });
});
<|endoftext|>


<commit_before>'use strict';
var fs = require('fs');

var nbController = process.env.SOAJS_NX_NBCONTROLLER || 1;
var apiDomain = process.env.SOAJS_NX_APIDOMAIN || "api.yourdomain.com";
var dashDomain = process.env.SOAJS_NX_DASHDOMAIN || null;
var apiPort = process.env.SOAJS_NX_APIPORT || "8080";
var hostPrefix = process.env.SOAJS_NX_HOSTPREFIX || "controllerProxy";
var controllerPort = process.env.SOAJS_NX_CONTROLLERPORT || "4000";

var upstreamName = "soajs.controller";

var lib = {
    "writeUpstream": function (param, cb) {
        var wstream = fs.createWriteStream(param.loc + 'upstream.conf');
        wstream.write("upstream " + upstreamName + " {\n");
        for (var i = 1; i <= nbController; i++) {
            var s = "000" + i;
            s = s.substr(s.length-2);
            wstream.write("  server "+hostPrefix+s+":"+controllerPort+";\n");
        }
        wstream.write("}\n");
        wstream.end();
        return cb(null);
    },
    "writeApiConf": function (param, cb) {
        var wstream = fs.createWriteStream(param.loc + 'api.conf');
        wstream.write("server {\n");
        wstream.write("  listen       "+apiPort+";\n");
        wstream.write("  server_name  "+apiDomain+";\n");
        wstream.write("  location / {\n");
        wstream.write("    proxy_pass 		    http://" + upstreamName + ";\n");
        wstream.write("    proxy_set_header   	X-Forwarded-Proto 	    $scheme;\n");
        wstream.write("    proxy_set_header   	Host             		$http_host;\n");
        wstream.write("    proxy_set_header   	X-NginX-Proxy     	    true;\n");
        wstream.write("    proxy_set_header   	Connection        	    \"\";\n");
        wstream.write("  }\n");
        wstream.write("}\n");
        wstream.end();
        return cb(null);
    },
    "writeDashConf": function (param, cb) {
        var wstream = fs.createWriteStream(param.loc + 'dash.conf');
        wstream.write("server {\n");
        wstream.write("  server_name  "+dashDomain+";\n");
        wstream.write("  location / {\n");
        wstream.write("    root   /opt/soajs/open_source/dashboard;\n");
        wstream.write("    sendfile       off;\n");
        wstream.write("    index  index.html index.htm;\n");
        wstream.write("  }\n");
        wstream.write("}\n");
        wstream.end();
        return cb(null);
    }
};

lib.writeUpstream({"loc": "/etc/nginx/conf.d/"},function(err){
    console.log ("NGINX UPSTREAM DONE.");
});
lib.writeApiConf ({"loc": "/etc/nginx/sites-enabled/"},function(err){
    console.log ("NGINX API CONF DONE.");
});
if (dashDomain) {
    lib.writeDashConf({"loc": "/etc/nginx/sites-enabled/"}, function (err) {
        console.log("NGINX DASH CONF DONE.");
    });
}<commit_msg>remove open_source folder from nginx conf<commit_after>'use strict';
var fs = require('fs');

var nbController = process.env.SOAJS_NX_NBCONTROLLER || 1;
var apiDomain = process.env.SOAJS_NX_APIDOMAIN || "api.yourdomain.com";
var dashDomain = process.env.SOAJS_NX_DASHDOMAIN || null;
var apiPort = process.env.SOAJS_NX_APIPORT || "8080";
var hostPrefix = process.env.SOAJS_NX_HOSTPREFIX || "controllerProxy";
var controllerPort = process.env.SOAJS_NX_CONTROLLERPORT || "4000";

var upstreamName = "soajs.controller";

var lib = {
    "writeUpstream": function (param, cb) {
        var wstream = fs.createWriteStream(param.loc + 'upstream.conf');
        wstream.write("upstream " + upstreamName + " {\n");
        for (var i = 1; i <= nbController; i++) {
            var s = "000" + i;
            s = s.substr(s.length-2);
            wstream.write("  server "+hostPrefix+s+":"+controllerPort+";\n");
        }
        wstream.write("}\n");
        wstream.end();
        return cb(null);
    },
    "writeApiConf": function (param, cb) {
        var wstream = fs.createWriteStream(param.loc + 'api.conf');
        wstream.write("server {\n");
        wstream.write("  listen       "+apiPort+";\n");
        wstream.write("  server_name  "+apiDomain+";\n");
        wstream.write("  location / {\n");
        wstream.write("    proxy_pass 		    http://" + upstreamName + ";\n");
        wstream.write("    proxy_set_header   	X-Forwarded-Proto 	    $scheme;\n");
        wstream.write("    proxy_set_header   	Host             		$http_host;\n");
        wstream.write("    proxy_set_header   	X-NginX-Proxy     	    true;\n");
        wstream.write("    proxy_set_header   	Connection        	    \"\";\n");
        wstream.write("  }\n");
        wstream.write("}\n");
        wstream.end();
        return cb(null);
    },
    "writeDashConf": function (param, cb) {
        var wstream = fs.createWriteStream(param.loc + 'dash.conf');
        wstream.write("server {\n");
        wstream.write("  server_name  "+dashDomain+";\n");
        wstream.write("  location / {\n");
        wstream.write("    root   /opt/soajs/dashboard;\n");
        wstream.write("    sendfile       off;\n");
        wstream.write("    index  index.html index.htm;\n");
        wstream.write("  }\n");
        wstream.write("}\n");
        wstream.end();
        return cb(null);
    }
};

lib.writeUpstream({"loc": "/etc/nginx/conf.d/"},function(err){
    console.log ("NGINX UPSTREAM DONE.");
});
lib.writeApiConf ({"loc": "/etc/nginx/sites-enabled/"},function(err){
    console.log ("NGINX API CONF DONE.");
});
if (dashDomain) {
    lib.writeDashConf({"loc": "/etc/nginx/sites-enabled/"}, function (err) {
        console.log("NGINX DASH CONF DONE.");
    });
}<|endoftext|>


<commit_before>/**
 * Sends a Chrome message to all tabs that are Tiles tabs.
 * @param  {String} message The message to send to each tab.
 */
function sendMessageToExtensionTabs(message, ignoreCurrent) {
	if (ignoreCurrent == undefined) {
		ignoreCurrent = true;
	}

	chrome.tabs.getCurrent(function(currentTab) {
		chrome.windows.getAll({ populate: true }, function(windows) {
			for (i = 0; i < windows.length; i++) {
				(function() {
					var tabs = windows[i].tabs;

					for (var j = 0; j < tabs.length; j++) {
						if (ignoreCurrent && currentTab && (currentTab.id == tabs[j].id)) {
							continue;
						}

						if (isExtensionURL(tabs[j].url)) {
							chrome.tabs.sendMessage(tabs[j].id, { "message": message });
						}
					}
				})();
			}
		});
	});
}<commit_msg>Add addMessageListener and emitMessage functions.<commit_after>var messages = [];

/**
 * Sends a Chrome message to all tabs that are Tiles tabs.
 * @param  {String} message The message to send to each tab.
 */
function sendMessageToExtensionTabs(message, ignoreCurrent) {
	if (ignoreCurrent == undefined) {
		ignoreCurrent = true;
	}

	chrome.tabs.getCurrent(function(currentTab) {
		chrome.windows.getAll({ populate: true }, function(windows) {
			for (i = 0; i < windows.length; i++) {
				(function() {
					var tabs = windows[i].tabs;

					for (var j = 0; j < tabs.length; j++) {
						if (ignoreCurrent && currentTab && (currentTab.id == tabs[j].id)) {
							continue;
						}

						if (isExtensionURL(tabs[j].url)) {
							chrome.tabs.sendMessage(tabs[j].id, { "message": message });
						}
					}
				})();
			}
		});
	});
}

function indexOfMessage(message) {
	for (var i = 0; i < messages.length; i++) {
		if (messages[i].message == message) {
			return i;
		}
	}

	return -1;
}

function addMessageListener(message, callback) {
	var i = indexOfMessage(message);
	if (i >= 0) {
		messages[i].callbacks.push(callback);
	} else {
		messages.push({ message: message, callbacks: [callback] });
	}
}

function emitMessage(message, data) {
	var i = indexOfMessage(message);
	if (i >= 0) {
		for (var j = 0; j < messages[i].callbacks.length; j++) {
			messages[i].callbacks[j](data);
		}
	}
}<|endoftext|>