<commit_before>/* ==========================================================================
*   Projet d'Intégration 2 AEC Commerce Électronique
*   Author  : Luis Felipe Rosas
   ========================================================================== */
/***********************************************
        Testé en FIREFOX et  Google Chrome
    *******************************/
var Panier = (function () {
    var _creerPanier = function(){
        var htmlProduits = '';
        /* Condition d'existence de produits dans le web storage */
        if(localStorage.length > 0){
            $('#panierVide').hide(); // Panier avec contenu, display none pour le message Vide
            $('#panierConfirmation').hide(); // Panier avec contenu, display none pour le message Vide
            $('#champsVides').hide();
            for (var i = 0; i < localStorage.length; i++){
                var produit = JSON.parse(localStorage.getItem(localStorage.key(i))); // HTML géneré pour chaque produit créé!
                // Récuperation de la quantité de produits stockés et le valeur de prix total par produit
                htmlProduits += '
\n\

\n\

\n\
                                    \n\
                                        \n\
                                    \n\

\n\

\n\

'+produit.nom+'
\n\

\n\

\n\

'+produit.prix+' $CAD
\n\

\n\

\n\

\n\
                                        <input type="text" value='+produit.quant+' class="pPanier" name="quantite" size="1">\n\
                                        <button type="button" class="btn btn-default btn-xs up">\n\
                                            \n\
                                        </button>\n\
                                        <button type="button" class="btn btn-default btn-xs down">\n\
                                            \n\
                                        </button>\n\

\n\

\n\

\n\

\n\
                                    <button type="button" class="btn btn-default btn-xs remove">\n\
                                        \n\
                                    </button>\n\

\n\

\n\

'+(produit.prix*produit.quant).toFixed(2)+' $CAD
\n\

\n\

\n\

';

            }
            $('#produits').html(htmlProduits);

            _totalProduitsPanier();
            _totalPrix();

        }else{
            $('#panier').remove();
            $('#panierVide').show(); // Afficher un message de Panier Vide au utilisateur
        }
    }
    //Total produits au Panier
    var _totalProduitsPanier = function(){
        // Récuperation de chanque produit trouvé au Panier
        var totalCumulatif=0;
        $( "input[name=quantite]:text").each(function(index, element){
            var cumulatif = $(this).val();
            totalCumulatif += cumulatif*1;
        });
        //Condition pour savoir s'il y a au minimum un produit dans le Panier
        if(totalCumulatif > 0){
            totalProduits = $('#totalProduitsPanier');
            totalProduits.html(totalCumulatif.toFixed(0));
        }else{
            // Élimination du content Panier
            $('#panier').remove();
            $('#panierVide').show(); // Afficher un message de Alerte au utilisateur
        }
    }
    //Total prix du Panier
    var _totalPrix = function(){
        // Récuperation de chanque produit avec prix trouvé au Panier
        var totalCumulatif=0;
        $('.total').each(function(index, element){
            var cumulatif = $(this).html();
            totalCumulatif += cumulatif*1;
        });
        var totalPrixP     = $('#totalPrixPanier');
        totalPrixP.html(totalCumulatif.toFixed(2));

    }

    // Fonction qui enregistre la commande dans la base de données
    var _passerCommande =  function(id){
        try{
            console.log('PASSER LA COMMANDE', id);
            var courriel = $("input[name='courriel']").val();

            if(id != "" && courriel != ""){ // Vérification de Champs
                var produit = new Object();
                for (var i = 0; i < localStorage.length; i++){
                    produit[i] = JSON.parse(localStorage.getItem(localStorage.key(i)));
                }
                console.log(courriel,adresse);
                // Source : http://stackoverflow.com/questions/9001526/send-array-with-ajax-to-php-script
                var jsonString = JSON.stringify(produit);
                $.ajax({
                    type: "POST",
                    url: "ajaxControler.php?requete=confirmation_adresse",
                    data: {
                        nb_produit : localStorage.length,
                        email : courriel,
                        id_adresse : id,
                        data : jsonString
                    },
                    cache: false,
                    success: function(info){
                        //if(info == 1){ // Vérification de la réponse AJAX TRUE
                            //$('#ShippingAddress').remove();
                            $('#ShippingAddress').html(info);
                            //localStorage.clear();
                        /*}else{
                            $('#myModal').modal('hide');
                            $('#panier').remove();
                            $('#pasEnregistre').show();

                        }*/
                    },
                    error: function(error){
                        console.log("Error:", error);
                        $('#msgError').show();
                    }
                });
            }else{
              $('#champsVides').show();
            }
        }catch(e){
            $('#msgError').show(); // Affichage d'error
        }
    }

    return {
        creerPanier : _creerPanier,
        totalProduitsPanier : _totalProduitsPanier,
        totalPrix : _totalPrix,
        passerCommande : _passerCommande
    };

}());

$(function(){

    window.addEventListener('load', function () {
        "use strict";
        // Déclaration de variables
        var targetClickUp, targetClickDown, targetClickRemove;
        // Récuperation de targets
        targetClickUp = document.querySelectorAll('button[class*="up"]');
        targetClickDown = document.querySelectorAll('button[class*="down"]');
        targetClickRemove = document.querySelectorAll('button[class*="remove"]');

        for (var i = 0; i < targetClickUp.length; i++){
            targetClickUp[i].addEventListener('click', up.eTarget);
            targetClickDown[i].addEventListener('click', down.eTarget);
            targetClickRemove[i].addEventListener('click', remove.eTarget);
        }
    });

    $('#panierConfirmation').hide();

    var up = (function(){
        var val_cible, val_input;
        return {
            eTarget : function(e){
                e.preventDefault(); // Désactiver la configuration par défaut du navigaterur

                // On cherche l'input qu'indique la quantité
                val_cible = $(e.currentTarget).siblings();
                // On recupere la quantité additioné
                var val_input = val_cible.val();
                val_input++;
                // Réaffecter une nouvelle valeur de la quantité du même produit
                val_cible.val(val_input);

                // Pour retrouver la valeur du prix du produit
                prix  = $(e.currentTarget).offsetParent().offsetParent().siblings().children().children('.prix');
                // On recupere le prix du produit
                val_prix = prix.html();

                // Total prix par produit
                total  = $(e.currentTarget).offsetParent().offsetParent().siblings().children().children('.total');
                var val = (val_prix*val_input).toFixed(2);
                total.html(val);

                // ------------------------
                // Reformater l'information du produit stocké dans le WebStorage (quantité et prix)
                // ------------------------
                var val_cible_id = $(e.currentTarget).parents('.row');
                var idP = val_cible_id.attr('id');
                var produit = new Object();
                produit = JSON.parse(localStorage.getItem(idP));
                produit.quant = val_input;
                window.localStorage.setItem(produit.idP, JSON.stringify(produit));

                // Appelle de la fonction qui montre le total de produits au panier
                Panier.totalProduitsPanier();

                // Appelle à la fonction qui montre le prix total du Panier
                Panier.totalPrix();
            }
        };
    }());

    var down = (function(){
        var val_cible, val_input;
        return {
            eTarget : function(e){
                e.preventDefault(); // Désactiver la configuration par défaut du navigaterur

                // On cherche l'input qu'indique la quantité
                val_cible = $(e.currentTarget).siblings();
                // Condition un produit ne peux pas désendre moins que 1
                if(val_cible.val() > 1){
                    // On recupere la quantité resté
                    val_input = val_cible.val();
                    val_input--;
                    // Réaffecter une nouvelle valeur de la quantité du même produit
                    val_cible.val(val_input);

                    // Pour retrouver la valeur du prix du produit
                    prix  = $(e.currentTarget).offsetParent().offsetParent().siblings().children().children('.prix');
                    // On recupere le prix du produit
                    val_prix = prix.html();

                    // Total prix par produit
                    total  = $(e.currentTarget).offsetParent().offsetParent().siblings().children().children('.total');
                    var val = (val_prix*val_input).toFixed(2);
                    total.html(val);

                    // ------------------------
                    // Reformater l'information du produit stocké dans le WebStorage (quantité et prix)
                    // ------------------------
                    var val_cible_id = $(e.currentTarget).parents('.row');
                    var idP = val_cible_id.attr('id');
                    var produit = new Object();
                    produit = JSON.parse(localStorage.getItem(idP));
                    produit.quant = val_input;
                    window.localStorage.setItem(produit.idP, JSON.stringify(produit));

                    // Appelle de la fonction qui montre le total de produits au panier
                    Panier.totalProduitsPanier();

                    // Appelle à la fonction qui montre le prix total du Panier
                    Panier.totalPrix();
                }
            }
        };
    }());

    var remove = (function(){
        var val_cible, val_input;
        return {
            eTarget : function(e){
                e.preventDefault(); // Désactiver la configuration par défaut du navigaterur

                // Cibler le bon produit à éffacer
                val_cible = $(e.currentTarget).parents('.row');
                var idP = val_cible.attr('id');
                /* ICI j'efface aussi le produit du webStorage*/
                localStorage.removeItem(idP);
                // Effacé de l'article (produit) html
                val_cible.remove();

                // Appelle de la fonction qui montre le total de produits au panier
                Panier.totalProduitsPanier();
                // Appelle à la fonction qui montre le prix total du Panier
                Panier.totalPrix();

            }
        };
    }());

});
/*
$(function(){
    $('#etape2').on('click', function(){
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "ajaxControler.php?requete=formConnecter", true);
        xhr.onreadystatechange = function() {
            if (xhr.status == 200 && xhr.readyState == xhr.DONE) {
                //clearTimeout(timeout);
                //console.log(xhr.responseText);
                $('.modal-content').html(xhr.responseText);
                $('#myModal').modal('show');
            }
        };
        xhr.send();

    });
});*/<commit_msg>Module PANIER mise à jour<commit_after>/* ==========================================================================
*   Projet d'Intégration 2 AEC Commerce Électronique
*   Author  : Luis Felipe Rosas
   ========================================================================== */
/***********************************************
        Testé en FIREFOX et  Google Chrome
    *******************************/
var Panier = (function () {
    var _creerPanier = function(){
        var htmlProduits = '';
        /* Condition d'existence de produits dans le web storage */
        if(localStorage.length > 0){
            $('#panierVide').hide(); // Panier avec contenu, display none pour le message Vide
            $('#panierConfirmation').hide(); // Panier avec contenu, display none pour le message Vide
            $('#champsVides').hide();
            for (var i = 0; i < localStorage.length; i++){
                var produit = JSON.parse(localStorage.getItem(localStorage.key(i))); // HTML géneré pour chaque produit créé!
                // Récuperation de la quantité de produits stockés et le valeur de prix total par produit
                htmlProduits += '
\n\

\n\

\n\
                                    \n\
                                        \n\
                                    \n\

\n\

\n\

'+produit.nom+'
\n\

\n\

\n\

'+produit.prix+' $CAD
\n\

\n\

\n\

\n\
                                        <input type="text" value='+produit.quant+' class="pPanier" name="quantite" size="1">\n\
                                        <button type="button" class="btn btn-default btn-xs up">\n\
                                            \n\
                                        </button>\n\
                                        <button type="button" class="btn btn-default btn-xs down">\n\
                                            \n\
                                        </button>\n\

\n\

\n\

\n\

\n\
                                    <button type="button" class="btn btn-default btn-xs remove">\n\
                                        \n\
                                    </button>\n\

\n\

\n\

'+(produit.prix*produit.quant).toFixed(2)+' $CAD
\n\

\n\

\n\

';

            }
            $('#produits').html(htmlProduits);

            _totalProduitsPanier();
            _totalPrix();

        }else{
            $('#panier').remove();
            $('#panierVide').show(); // Afficher un message de Panier Vide au utilisateur
        }
    }
    //Total produits au Panier
    var _totalProduitsPanier = function(){
        // Récuperation de chanque produit trouvé au Panier
        var totalCumulatif=0;
        $( "input[name=quantite]:text").each(function(index, element){
            var cumulatif = $(this).val();
            totalCumulatif += cumulatif*1;
        });
        //Condition pour savoir s'il y a au minimum un produit dans le Panier
        if(totalCumulatif > 0){
            totalProduits = $('#totalProduitsPanier');
            totalProduits.html(totalCumulatif.toFixed(0));
        }else{
            // Élimination du content Panier
            $('#panier').remove();
            $('#panierVide').show(); // Afficher un message de Alerte au utilisateur
        }
    }
    //Total prix du Panier
    var _totalPrix = function(){
        // Récuperation de chanque produit avec prix trouvé au Panier
        var totalCumulatif=0;
        $('.total').each(function(index, element){
            var cumulatif = $(this).html();
            totalCumulatif += cumulatif*1;
        });
        var totalPrixP     = $('#totalPrixPanier');
        totalPrixP.html(totalCumulatif.toFixed(2));

    }

    // Fonction qui enregistre la commande dans la base de données
    var _passerCommande =  function(id){
        try{
            console.log('PASSER LA COMMANDE', id);
            var courriel = $("input[name='courriel']").val();

            if(id != "" && courriel != ""){ // Vérification de Champs
                var produit = new Object();
                for (var i = 0; i < localStorage.length; i++){
                    produit[i] = JSON.parse(localStorage.getItem(localStorage.key(i)));
                }
                console.log(courriel,adresse);
                // Source : http://stackoverflow.com/questions/9001526/send-array-with-ajax-to-php-script
                var jsonString = JSON.stringify(produit);
                $.ajax({
                    type: "POST",
                    url: "ajaxControler.php?requete=confirmation_adresse",
                    data: {
                        nb_produit : localStorage.length,
                        email : courriel,
                        id_adresse : id,
                        data : jsonString
                    },
                    cache: false,
                    success: function(info){
                        $('#ShippingAddress').html(info);
                        //if(info == 1){ // Vérification de la réponse AJAX TRUE
                            //$('#ShippingAddress').remove();
                            //localStorage.clear();
                        /*}else{
                            $('#myModal').modal('hide');
                            $('#panier').remove();
                            $('#pasEnregistre').show();

                        }*/
                    },
                    error: function(error){
                        console.log("Error:", error);
                        $('#msgError').show();
                    }
                });
            }else{
              $('#champsVides').show();
            }
        }catch(e){
            $('#msgError').show(); // Affichage d'error
        }
    }

    return {
        creerPanier : _creerPanier,
        totalProduitsPanier : _totalProduitsPanier,
        totalPrix : _totalPrix,
        passerCommande : _passerCommande
    };

}());

$(function(){

    window.addEventListener('load', function () {
        "use strict";
        // Déclaration de variables
        var targetClickUp, targetClickDown, targetClickRemove;
        // Récuperation de targets
        targetClickUp = document.querySelectorAll('button[class*="up"]');
        targetClickDown = document.querySelectorAll('button[class*="down"]');
        targetClickRemove = document.querySelectorAll('button[class*="remove"]');

        for (var i = 0; i < targetClickUp.length; i++){
            targetClickUp[i].addEventListener('click', up.eTarget);
            targetClickDown[i].addEventListener('click', down.eTarget);
            targetClickRemove[i].addEventListener('click', remove.eTarget);
        }
    });

    $('#panierConfirmation').hide();

    var up = (function(){
        var val_cible, val_input;
        return {
            eTarget : function(e){
                e.preventDefault(); // Désactiver la configuration par défaut du navigaterur

                // On cherche l'input qu'indique la quantité
                val_cible = $(e.currentTarget).siblings();
                // On recupere la quantité additioné
                var val_input = val_cible.val();
                val_input++;
                // Réaffecter une nouvelle valeur de la quantité du même produit
                val_cible.val(val_input);

                // Pour retrouver la valeur du prix du produit
                prix  = $(e.currentTarget).offsetParent().offsetParent().siblings().children().children('.prix');
                // On recupere le prix du produit
                val_prix = prix.html();

                // Total prix par produit
                total  = $(e.currentTarget).offsetParent().offsetParent().siblings().children().children('.total');
                var val = (val_prix*val_input).toFixed(2);
                total.html(val);

                // ------------------------
                // Reformater l'information du produit stocké dans le WebStorage (quantité et prix)
                // ------------------------
                var val_cible_id = $(e.currentTarget).parents('.row');
                var idP = val_cible_id.attr('id');
                var produit = new Object();
                produit = JSON.parse(localStorage.getItem(idP));
                produit.quant = val_input;
                window.localStorage.setItem(produit.idP, JSON.stringify(produit));

                // Appelle de la fonction qui montre le total de produits au panier
                Panier.totalProduitsPanier();

                // Appelle à la fonction qui montre le prix total du Panier
                Panier.totalPrix();
            }
        };
    }());

    var down = (function(){
        var val_cible, val_input;
        return {
            eTarget : function(e){
                e.preventDefault(); // Désactiver la configuration par défaut du navigaterur

                // On cherche l'input qu'indique la quantité
                val_cible = $(e.currentTarget).siblings();
                // Condition un produit ne peux pas désendre moins que 1
                if(val_cible.val() > 1){
                    // On recupere la quantité resté
                    val_input = val_cible.val();
                    val_input--;
                    // Réaffecter une nouvelle valeur de la quantité du même produit
                    val_cible.val(val_input);

                    // Pour retrouver la valeur du prix du produit
                    prix  = $(e.currentTarget).offsetParent().offsetParent().siblings().children().children('.prix');
                    // On recupere le prix du produit
                    val_prix = prix.html();

                    // Total prix par produit
                    total  = $(e.currentTarget).offsetParent().offsetParent().siblings().children().children('.total');
                    var val = (val_prix*val_input).toFixed(2);
                    total.html(val);

                    // ------------------------
                    // Reformater l'information du produit stocké dans le WebStorage (quantité et prix)
                    // ------------------------
                    var val_cible_id = $(e.currentTarget).parents('.row');
                    var idP = val_cible_id.attr('id');
                    var produit = new Object();
                    produit = JSON.parse(localStorage.getItem(idP));
                    produit.quant = val_input;
                    window.localStorage.setItem(produit.idP, JSON.stringify(produit));

                    // Appelle de la fonction qui montre le total de produits au panier
                    Panier.totalProduitsPanier();

                    // Appelle à la fonction qui montre le prix total du Panier
                    Panier.totalPrix();
                }
            }
        };
    }());

    var remove = (function(){
        var val_cible, val_input;
        return {
            eTarget : function(e){
                e.preventDefault(); // Désactiver la configuration par défaut du navigaterur

                // Cibler le bon produit à éffacer
                val_cible = $(e.currentTarget).parents('.row');
                var idP = val_cible.attr('id');
                /* ICI j'efface aussi le produit du webStorage*/
                localStorage.removeItem(idP);
                // Effacé de l'article (produit) html
                val_cible.remove();

                // Appelle de la fonction qui montre le total de produits au panier
                Panier.totalProduitsPanier();
                // Appelle à la fonction qui montre le prix total du Panier
                Panier.totalPrix();

            }
        };
    }());

});<|endoftext|>


<commit_before>/**
 * Class responsible for keeping track of disorders and their properties, and for
 * caching disorders data as loaded from the OMIM database.
 * This information is graphically displayed in a 'Legend' box.
 *
 * @class DisorderLegend
 * @constructor
 */
var DisorderLegend = Class.create(Legend, {

	initialize: function ($super) {
		$super('Disorders', true);

		this._disorderCache = {};
	},

	_getPrefix: function (id) {
		return "disorder";
	},

	/**
	 * Returns the disorder object with the given ID. If object is not in cache yet
	 * returns a newly created one which may have the disorder name & other attributes not loaded yet
	 *
	 * @method getDisorder
	 * @return {Object}
	 */
	getDisorder: function (disorderID) {
		if (!this._disorderCache.hasOwnProperty(disorderID)) {
			var whenNameIsLoaded = function () {
				this._updateDisorderName(disorderID);
			}
			this._disorderCache[disorderID] = new Disorder(disorderID, null, null, whenNameIsLoaded.bind(this));
		}
		return this._disorderCache[disorderID];
	},

	/**
	 * Returns a map disorderID -> disorderName
	 */
	getAllNames: function () {
		var result = {};
		for (var disorderID in this._affectedNodes) {
			if (this._affectedNodes.hasOwnProperty(disorderID)) {
				result[disorderID] = this.getDisorder(disorderID).getName();
			}
		}
		return result;
	},

	/**
	 * Registers an occurrence of a disorder. If disorder hasn't been documented yet,
	 * designates a color for it.
	 *
	 * @method addCase
	 * @param {Number|String} disorderID ID for this disorder taken from the OMIM database
	 * @param {String} disorderName The name of the disorder
	 * @param {Number} nodeID ID of the Person who has this disorder
	 */
    //Added by Soheil
	//valueAll is passed for GEL(GenomicsEngland)
	addCase: function($super, disorderID, disorderName, valueAll, nodeID) {
		if (!this._disorderCache.hasOwnProperty(disorderID))
			//valueAll is passed for GEL(GenomicsEngland)
			this._disorderCache[disorderID] = new Disorder(disorderID, disorderName, valueAll);
		//valueAll is passed for GEL(GenomicsEngland)
		$super(disorderID, disorderName, valueAll, nodeID);
	},

	/**
	 * Updates the displayed disorder name for the given disorder
	 *
	 * @method _updateDisorderName
	 * @param {Number} disorderID The identifier of the disorder to update
	 * @private
	 */
	_updateDisorderName: function (disorderID) {
		//console.log("updating disorder display for " + disorderID + ", name = " + this.getDisorder(disorderID).getName());
		var name = this._legendBox.down('li#' + this._getPrefix() + '-' + disorderID + ' .disorder-name');
		name.update(this.getDisorder(disorderID).getName());
	},

	/**
	 * Generate the element that will display information about the given disorder in the legend
	 *
	 * @method _generateElement
	 * @param {Number} disorderID The id for the disorder, taken from the OMIM database
	 * @param {String} name The human-readable disorder name
	 * @return {HTMLLIElement} List element to be insert in the legend
	 */
	//pass valueAll for GEL(GenomicsEngland)...........
	_generateElement: function($super, disorderID, name, valueAll) {
		if (!this._objectColors.hasOwnProperty(disorderID)) {
			var color = this._generateColor(disorderID);
			this._objectColors[disorderID] = color;
			document.fire('disorder:color', {'id': disorderID, color: color});
		}

		//Added by Soheil for Gel(GenomicsEngland) to include (disorderType) in disorderLegend
		var item = $super(disorderID, name, valueAll);
		var disorderType = "";
		if(valueAll != undefined){
			disorderType = valueAll.disorderType;
		}else{
			disorderType = "OMIM";
		}
		var disorderTypeContainer = new Element('span', {'class' : 'disorder-disorder-type'}).insert("(").insert(disorderType).insert(")");
		item.down('.disorder-name').insert(" ").insert(disorderTypeContainer);

		return item;
	},

	/**
	 * Callback for dragging an object from the legend onto nodes
	 *
	 * @method _onDropGeneric
	 * @param {Person} Person node
	 * @param {String|Number} id ID of the disorder being dropped
	 */
	_onDropObject: function (node, disorderID) {
		var currentDisorders = node.getDisorders().slice(0);
		if (currentDisorders.indexOf(disorderID) == -1) {   // only if the node does not have this disorder yet
			currentDisorders.push(disorderID);
			editor.getView().unmarkAll();
			var properties = { "setDisorders": currentDisorders };
			var event = { "nodeID": node.getID(), "properties": properties };
			document.fire("pedigree:node:setproperty", event);
		} else {
			this._onFailedDrag(node, "This person already has the selected disorder", "Can't drag this disorder to this person");
		}
	},

	/**
	 * Generates a CSS color.
	 * Has preference for some predefined colors that can be distinguished in gray-scale
	 * and are distint from gene colors.
	 *
	 * @method generateColor
	 * @return {String} CSS color
	 */
	_generateColor: function (disorderID) {
		if (this._objectColors.hasOwnProperty(disorderID)) {
			return this._objectColors[disorderID];
		}

		var usedColors = Object.values(this._objectColors),
		// [red/yellow]           prefColors = ["#FEE090", '#f8ebb7', '#eac080', '#bf6632', '#9a4500', '#a47841', '#c95555', '#ae6c57'];
		// [original yellow/blue] prefColors = ["#FEE090", '#E0F8F8', '#8ebbd6', '#4575B4', '#fca860', '#9a4500', '#81a270'];
		// [green]                prefColors = ['#81a270', '#c4e8c4', '#56a270', '#b3b16f', '#4a775a', '#65caa3'];
		//#E0F8F8
			prefColors = ['#D1E9E9', '#92c0db', '#4575B4', '#949ab8', "#FEE090", '#bf6632', '#fca860', '#9a4500', '#d12943', '#00a2bf'];
		if (disorderID == "affected") {
			prefColors = ["#FEE090", "#dbad71"];
		}
		if (this.getPreferedColor(disorderID) !== null) {
			prefColors.unshift(this.getPreferedColor(disorderID));
		}
		usedColors.each(function (color) {
			prefColors = prefColors.without(color);
		});
		if (prefColors.length > 0) {
			return prefColors[0];
		}
		else {
			var randomColor = Raphael.getColor();
			while (randomColor == "#ffffff" || usedColors.indexOf(randomColor) != -1) {
				randomColor = "#" + ((1 << 24) * Math.random() | 0).toString(16);
			}
			return randomColor;
		}
	}
});
<commit_msg>Hide disorderType display text if disorder is default 'affected' value<commit_after>/**
 * Class responsible for keeping track of disorders and their properties, and for
 * caching disorders data as loaded from the OMIM database.
 * This information is graphically displayed in a 'Legend' box.
 *
 * @class DisorderLegend
 * @constructor
 */
var DisorderLegend = Class.create(Legend, {

	initialize: function ($super) {
		$super('Disorders', true);

		this._disorderCache = {};
	},

	_getPrefix: function (id) {
		return "disorder";
	},

	/**
	 * Returns the disorder object with the given ID. If object is not in cache yet
	 * returns a newly created one which may have the disorder name & other attributes not loaded yet
	 *
	 * @method getDisorder
	 * @return {Object}
	 */
	getDisorder: function (disorderID) {
		if (!this._disorderCache.hasOwnProperty(disorderID)) {
			var whenNameIsLoaded = function () {
				this._updateDisorderName(disorderID);
			}
			this._disorderCache[disorderID] = new Disorder(disorderID, null, null, whenNameIsLoaded.bind(this));
		}
		return this._disorderCache[disorderID];
	},

	/**
	 * Returns a map disorderID -> disorderName
	 */
	getAllNames: function () {
		var result = {};
		for (var disorderID in this._affectedNodes) {
			if (this._affectedNodes.hasOwnProperty(disorderID)) {
				result[disorderID] = this.getDisorder(disorderID).getName();
			}
		}
		return result;
	},

	/**
	 * Registers an occurrence of a disorder. If disorder hasn't been documented yet,
	 * designates a color for it.
	 *
	 * @method addCase
	 * @param {Number|String} disorderID ID for this disorder taken from the OMIM database
	 * @param {String} disorderName The name of the disorder
	 * @param {Number} nodeID ID of the Person who has this disorder
	 */
    //Added by Soheil
	//valueAll is passed for GEL(GenomicsEngland)
	addCase: function($super, disorderID, disorderName, valueAll, nodeID) {
		if (!this._disorderCache.hasOwnProperty(disorderID))
			//valueAll is passed for GEL(GenomicsEngland)
			this._disorderCache[disorderID] = new Disorder(disorderID, disorderName, valueAll);
		//valueAll is passed for GEL(GenomicsEngland)
		$super(disorderID, disorderName, valueAll, nodeID);
	},

	/**
	 * Updates the displayed disorder name for the given disorder
	 *
	 * @method _updateDisorderName
	 * @param {Number} disorderID The identifier of the disorder to update
	 * @private
	 */
	_updateDisorderName: function (disorderID) {
		//console.log("updating disorder display for " + disorderID + ", name = " + this.getDisorder(disorderID).getName());
		var name = this._legendBox.down('li#' + this._getPrefix() + '-' + disorderID + ' .disorder-name');
		name.update(this.getDisorder(disorderID).getName());
	},

	/**
	 * Generate the element that will display information about the given disorder in the legend
	 *
	 * @method _generateElement
	 * @param {Number} disorderID The id for the disorder, taken from the OMIM database
	 * @param {String} name The human-readable disorder name
	 * @return {HTMLLIElement} List element to be insert in the legend
	 */
	//pass valueAll for GEL(GenomicsEngland)...........
	_generateElement: function($super, disorderID, name, valueAll) {
		if (!this._objectColors.hasOwnProperty(disorderID)) {
			var color = this._generateColor(disorderID);
			this._objectColors[disorderID] = color;
			document.fire('disorder:color', {'id': disorderID, color: color});
		}

		//Added by Soheil for Gel(GenomicsEngland) to include (disorderType) in disorderLegend
		var item = $super(disorderID, name, valueAll);
		var disorderType = "";
		if(valueAll != undefined){
			disorderType = valueAll.disorderType;
		}else{
			disorderType = "OMIM";
		}
		if(name != "affected") {
			var disorderTypeContainer = new Element('span', {'class': 'disorder-disorder-type'}).insert("(").insert(disorderType).insert(")");
			item.down('.disorder-name').insert(" ").insert(disorderTypeContainer);
		}

		return item;
	},

	/**
	 * Callback for dragging an object from the legend onto nodes
	 *
	 * @method _onDropGeneric
	 * @param {Person} Person node
	 * @param {String|Number} id ID of the disorder being dropped
	 */
	_onDropObject: function (node, disorderID) {
		var currentDisorders = node.getDisorders().slice(0);
		if (currentDisorders.indexOf(disorderID) == -1) {   // only if the node does not have this disorder yet
			currentDisorders.push(disorderID);
			editor.getView().unmarkAll();
			var properties = { "setDisorders": currentDisorders };
			var event = { "nodeID": node.getID(), "properties": properties };
			document.fire("pedigree:node:setproperty", event);
		} else {
			this._onFailedDrag(node, "This person already has the selected disorder", "Can't drag this disorder to this person");
		}
	},

	/**
	 * Generates a CSS color.
	 * Has preference for some predefined colors that can be distinguished in gray-scale
	 * and are distint from gene colors.
	 *
	 * @method generateColor
	 * @return {String} CSS color
	 */
	_generateColor: function (disorderID) {
		if (this._objectColors.hasOwnProperty(disorderID)) {
			return this._objectColors[disorderID];
		}

		var usedColors = Object.values(this._objectColors),
		// [red/yellow]           prefColors = ["#FEE090", '#f8ebb7', '#eac080', '#bf6632', '#9a4500', '#a47841', '#c95555', '#ae6c57'];
		// [original yellow/blue] prefColors = ["#FEE090", '#E0F8F8', '#8ebbd6', '#4575B4', '#fca860', '#9a4500', '#81a270'];
		// [green]                prefColors = ['#81a270', '#c4e8c4', '#56a270', '#b3b16f', '#4a775a', '#65caa3'];
		//#E0F8F8
			prefColors = ['#D1E9E9', '#92c0db', '#4575B4', '#949ab8', "#FEE090", '#bf6632', '#fca860', '#9a4500', '#d12943', '#00a2bf'];
		if (disorderID == "affected") {
			prefColors = ["#FEE090", "#dbad71"];
		}
		if (this.getPreferedColor(disorderID) !== null) {
			prefColors.unshift(this.getPreferedColor(disorderID));
		}
		usedColors.each(function (color) {
			prefColors = prefColors.without(color);
		});
		if (prefColors.length > 0) {
			return prefColors[0];
		}
		else {
			var randomColor = Raphael.getColor();
			while (randomColor == "#ffffff" || usedColors.indexOf(randomColor) != -1) {
				randomColor = "#" + ((1 << 24) * Math.random() | 0).toString(16);
			}
			return randomColor;
		}
	}
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { translate } from 'react-i18next';
import style from './style.styl';

class Dropdown extends Component {
    constructor (props) {
        super()
        this.state = {
            selected: props.selected || Object.keys(props.options)[0]
        }
        this.apply = props.apply || function () {}
    }

    onSelect (o) {
        this.setState({selected: o})
        this.apply(o)
    }

    render() {

        let {props, state} = this

        //Item template
        const Item = (props) => (

                    {props.config.showText ? props.value : null}

        )

        //Label template
        const Label = (props) => (

        )

        // Get items
        const getItems = (props) => Object.keys(props.options).map((k, i) => (
            state.selected === k ? null : <Item  key={i} value={props.options[k]} {...props}/>
        ))

        return  (

                     {props.config.showLabel === true ?   : null}
                    {state.selected}

{getItems(props)}

                    {props.children}

        )
    }
}

Dropdown.defaultProps = {
    config : {
        //Dropdon Type
        type: "text",
        //Display item text
        showText: true,
        //Display selected item label
        showLabel: false
    }
}

const colorOpts = {
    type: "color",
    showText: false,
    showLabel: true
}

let DropdownItem = (props) => (

{props.value}

)

let DropdownColor =  (props) => (
    <Dropdown config={colorOpts}  {...props}>
        <DropdownItem value="More colors..."/>
    </Dropdown>
)

module.exports = {
    Dropdown,
    DropdownColor
}
<commit_msg>make item -> DropdownItem component<commit_after>import React, { Component } from 'react';
import { translate } from 'react-i18next';
import style from './style.styl';

//Item template
let DropdownItem = (props) => (

{props.value}

)

class Dropdown extends Component {
    constructor (props) {
        super()
        this.state = {
            selected: props.selected || Object.keys(props.options)[0]
        }
        this.apply = props.apply || function () {}
    }

    onSelect (o) {
        this.setState({selected: o})
        this.apply(o)
    }

    render() {

        let {props, state} = this

        //Label template
        const Label = (props) => (

        )

        // Get items
        const getItems = (props) => Object.keys(props.options).map((k, i) => (
            state.selected === k ? null :
            <DropdownItem  key={i}
                   style={{backgroundColor: props.config.type === 'color' ? k : null}}
                   onClick={this.onSelect.bind(this, k)}
                   value={props.config.showText ? props.options[k] : null}
            />
        ))

        return  (

                     {props.config.showLabel === true ?   : null}
                    {state.selected}

{getItems(props)}

                    {props.children}

        )
    }
}

Dropdown.defaultProps = {
    config : {
        //Dropdon Type
        type: "text",
        //Display item text
        showText: true,
        //Display selected item label
        showLabel: false
    }
}

const colorOpts = {
    type: "color",
    showText: false,
    showLabel: true
}

let DropdownColor =  (props) => (
    <Dropdown config={colorOpts}  {...props}>
        <DropdownItem value="More colors..."/>
    </Dropdown>
)

module.exports = {
    Dropdown,
    DropdownColor
}
<|endoftext|>


<commit_before>// dependencies
//
import React from 'react';
import classnames from 'classnames';
import uniqueId from 'lodash/uniqueId';
import isRegExp from 'lodash/isRegExp';
import isFunction from 'lodash/isFunction';

// @TODO add different validation error messages for failing different rules
// @TODO horizontal form - label & input element widths
// @TODO permitted characters regex

/**
 * The TextInput component
 *
 */
class TextInput extends React.Component {
    /**
     * Construct the component instance
     * @param  {Object} props The component props
     */
    constructor(props) {
        super(props);

        // format the value if we have a format method
        //
        const value = props.format ? props.format(props.value) : props.value;

        // initialize the state for the component
        //
        this.state = {
            value,
            isValid: true,
        };

        // generate a unique ID for this component instance if an ID prop has
        // not been provided
        //
        this.id = this.props.id || uniqueId('form-textinput-');

        // intialize the validation message for the component
        //
        this.validationMessage = props.validationMessage
            || `${this.props.description} is required`;

        // bind `this` to the component event handlers
        //
        this.onBlur = this.onBlur.bind(this);
        this.onFocus = this.onFocus.bind(this);
        this.onChange = this.onChange.bind(this);
    }

    /**
     * Handle the "component is about to mount" event
     */
    componentWillMount() {
        // determine if it's valid
        //
        const isValid = this.validate(this.state.value);

        // call the `onChildValidationEvent` handler once with
        // `hasValidated`=false, just to ensure that the parent knows about this
        // child
        //
        this.context.onChildValidationEvent(
            this.id,
            false,
            isValid ? null : this.validationMessage
        );
    }

    /**
     * Handle the blur event for the input element
     * @param  {Object} event The event object
     */
    onBlur() {
        // get the new value, formatting it if necessary
        //
        const value = this.props.format ? this.props.format(this.state.value) : this.state.value;

        // determine if it's valid
        //
        const isValid = this.validate(value);

        // set the `isValid` state
        //
        this.setState({ value, isValid });

        // call the `onChildValidationEvent` handler
        //
        this.context.onChildValidationEvent(this.id, true, isValid ? null : this.validationMessage);

        // call the `onChange` handler
        //
        if (this.props.onChange) {
            this.props.onChange(value);
        }
    }

    /**
     * Handle the `onFocus` event
     */
    onFocus() {
        // do we have a parse prop?
        //
        if (this.props.parse) {
            // yes! parse the value before editing begins
            //
            this.setState({ value: this.props.parse(this.state.value) });
        }
    }

    /**
     * Handle the change event for the input element
     * @param  {Object} event The event object
     */
    onChange(event) {
        // get the value from the event object
        //
        const value = event.target.value;

        // update the component state
        //
        this.setState({ value });

        // do we have an `onChange` handler? if so call it with the new value
        //
        if (this.props.onChange) {
            this.props.onChange(value);
        }
    }

    /**
     * Validate a value
     * @param  {String} value The value to validate
     * @return {Boolean}      True if the value is valid, false otherwise
     */
    validate(value) {
        // declare a variable to hold the result
        //
        let isValid = true;

        // do we have a value?
        //
        if (!value) {
            // we do not have a value - in this case, whether it's valid or not
            // is determined solely by whether a valid is required or not
            //
            isValid = !this.props.required;
        } else {
            // we do have a value. Do we have a pattern prop?
            //
            if (this.props.pattern) {
                // yes, we have a pattern prop. is the pattern a regex or is it
                // a function?
                //
                if (isRegExp(this.props.pattern)) {
                    // the pattern is a regex. Test the value against it
                    //
                    isValid = this.props.pattern.test(`${value}`);
                } else if (isFunction(this.props.pattern)) {
                    // the pattern is a function. Pass the value to the function
                    //
                    isValid = this.props.pattern(value);
                }
            } else {
                // we don't have a pattern prop. That means if we get here that
                // we have a value and it doesn't matter whether it's required
                // or not because we have a value
                //
                isValid = true;
            }
        }

        // return the results of the validation
        //
        return isValid;
    }

    /**
     * Render the component
     * @return {React.Element} The React element describing the component
     */
    render() {
        return (

                {this.props.label ? {this.props.label} : ''}
                <input
                    type="text"
                    readOnly={!!this.props.readOnly}
                    id={this.id}
                    value={this.state.value}
                    placeholder={this.props.placeholder}
                    className="form-control"
                    onBlur={this.onBlur}
                    onFocus={this.onFocus}
                    onChange={this.onChange}
                />
                {!this.state.isValid
                    ? {this.validationMessage}
                    : null
                }

        );
    }
}

// set the property types for the component
//
TextInput.propTypes = {
    required:               React.PropTypes.bool,
    id:                     React.PropTypes.string,
    value:                  React.PropTypes.string,
    label:                  React.PropTypes.string,
    description:            React.PropTypes.string,
    validationMessage:      React.PropTypes.string,
    pattern:                (props, propName, componentName) => {
        if (props.pattern && !isRegExp(props.pattern) && !isFunction(props.pattern)) {
            return new Error(
                `Invalid prop ${propName} supplied to ${componentName}` +
                ' - should be a regular expression'
            );
        }
        return null;
    },
    format:                 React.PropTypes.func,
    parse:                  React.PropTypes.func,
    onChange:               React.PropTypes.func,
    validationKey:          React.PropTypes.string,
    onChildValidationEvent: React.PropTypes.func,
};

// set the context types for values received from higher up the food chain
//
TextInput.contextTypes = {
    onChildValidationEvent: React.PropTypes.func,
};

// export the component
//
export default TextInput;
<commit_msg>added readOnly to proptypes<commit_after>// dependencies
//
import React from 'react';
import classnames from 'classnames';
import uniqueId from 'lodash/uniqueId';
import isRegExp from 'lodash/isRegExp';
import isFunction from 'lodash/isFunction';

// @TODO add different validation error messages for failing different rules
// @TODO horizontal form - label & input element widths
// @TODO permitted characters regex

/**
 * The TextInput component
 *
 */
class TextInput extends React.Component {
    /**
     * Construct the component instance
     * @param  {Object} props The component props
     */
    constructor(props) {
        super(props);

        // format the value if we have a format method
        //
        const value = props.format ? props.format(props.value) : props.value;

        // initialize the state for the component
        //
        this.state = {
            value,
            isValid: true,
        };

        // generate a unique ID for this component instance if an ID prop has
        // not been provided
        //
        this.id = this.props.id || uniqueId('form-textinput-');

        // intialize the validation message for the component
        //
        this.validationMessage = props.validationMessage
            || `${this.props.description} is required`;

        // bind `this` to the component event handlers
        //
        this.onBlur = this.onBlur.bind(this);
        this.onFocus = this.onFocus.bind(this);
        this.onChange = this.onChange.bind(this);
    }

    /**
     * Handle the "component is about to mount" event
     */
    componentWillMount() {
        // determine if it's valid
        //
        const isValid = this.validate(this.state.value);

        // call the `onChildValidationEvent` handler once with
        // `hasValidated`=false, just to ensure that the parent knows about this
        // child
        //
        this.context.onChildValidationEvent(
            this.id,
            false,
            isValid ? null : this.validationMessage
        );
    }

    /**
     * Handle the blur event for the input element
     * @param  {Object} event The event object
     */
    onBlur() {
        // get the new value, formatting it if necessary
        //
        const value = this.props.format ? this.props.format(this.state.value) : this.state.value;

        // determine if it's valid
        //
        const isValid = this.validate(value);

        // set the `isValid` state
        //
        this.setState({ value, isValid });

        // call the `onChildValidationEvent` handler
        //
        this.context.onChildValidationEvent(this.id, true, isValid ? null : this.validationMessage);

        // call the `onChange` handler
        //
        if (this.props.onChange) {
            this.props.onChange(value);
        }
    }

    /**
     * Handle the `onFocus` event
     */
    onFocus() {
        // do we have a parse prop?
        //
        if (this.props.parse) {
            // yes! parse the value before editing begins
            //
            this.setState({ value: this.props.parse(this.state.value) });
        }
    }

    /**
     * Handle the change event for the input element
     * @param  {Object} event The event object
     */
    onChange(event) {
        // get the value from the event object
        //
        const value = event.target.value;

        // update the component state
        //
        this.setState({ value });

        // do we have an `onChange` handler? if so call it with the new value
        //
        if (this.props.onChange) {
            this.props.onChange(value);
        }
    }

    /**
     * Validate a value
     * @param  {String} value The value to validate
     * @return {Boolean}      True if the value is valid, false otherwise
     */
    validate(value) {
        // declare a variable to hold the result
        //
        let isValid = true;

        // do we have a value?
        //
        if (!value) {
            // we do not have a value - in this case, whether it's valid or not
            // is determined solely by whether a valid is required or not
            //
            isValid = !this.props.required;
        } else {
            // we do have a value. Do we have a pattern prop?
            //
            if (this.props.pattern) {
                // yes, we have a pattern prop. is the pattern a regex or is it
                // a function?
                //
                if (isRegExp(this.props.pattern)) {
                    // the pattern is a regex. Test the value against it
                    //
                    isValid = this.props.pattern.test(`${value}`);
                } else if (isFunction(this.props.pattern)) {
                    // the pattern is a function. Pass the value to the function
                    //
                    isValid = this.props.pattern(value);
                }
            } else {
                // we don't have a pattern prop. That means if we get here that
                // we have a value and it doesn't matter whether it's required
                // or not because we have a value
                //
                isValid = true;
            }
        }

        // return the results of the validation
        //
        return isValid;
    }

    /**
     * Render the component
     * @return {React.Element} The React element describing the component
     */
    render() {
        return (

                {this.props.label ? {this.props.label} : ''}
                <input
                    type="text"
                    readOnly={!!this.props.readOnly}
                    id={this.id}
                    value={this.state.value}
                    placeholder={this.props.placeholder}
                    className="form-control"
                    onBlur={this.onBlur}
                    onFocus={this.onFocus}
                    onChange={this.onChange}
                />
                {!this.state.isValid
                    ? {this.validationMessage}
                    : null
                }

        );
    }
}

// set the property types for the component
//
TextInput.propTypes = {
    required:               React.PropTypes.bool,
    readOnly:               React.PropTypes.bool,
    id:                     React.PropTypes.string,
    value:                  React.PropTypes.string,
    label:                  React.PropTypes.string,
    description:            React.PropTypes.string,
    validationMessage:      React.PropTypes.string,
    pattern:                (props, propName, componentName) => {
        if (props.pattern && !isRegExp(props.pattern) && !isFunction(props.pattern)) {
            return new Error(
                `Invalid prop ${propName} supplied to ${componentName}` +
                ' - should be a regular expression'
            );
        }
        return null;
    },
    format:                 React.PropTypes.func,
    parse:                  React.PropTypes.func,
    onChange:               React.PropTypes.func,
    validationKey:          React.PropTypes.string,
    onChildValidationEvent: React.PropTypes.func,
};

// set the context types for values received from higher up the food chain
//
TextInput.contextTypes = {
    onChildValidationEvent: React.PropTypes.func,
};

// export the component
//
export default TextInput;
<|endoftext|>


<commit_before>/* @flow */
import React, {Component} from 'react'
import {Box, Text, Icon, PopupMenu} from '../common-adapters'
import {globalStyles, globalColors} from '../styles/style-guide'
import type {Props as IconProps} from '../common-adapters/icon'

import type {Props} from './render'

type RevokedHeaderProps = {children?: Array<any>}
type RevokedHeaderState = {expanded: boolean}
class RevokedHeader extends Component<void, RevokedHeaderProps, RevokedHeaderState> {
  state: RevokedHeaderState;

  constructor (props: Props) {
    super(props)
    this.state = {
      expanded: false,
    }
  }

  _toggleHeader (e) {
    this.setState({expanded: !this.state.expanded})
  }

  render () {
    const iconType = this.state.expanded ? 'fa-caret-down' : 'fa-caret-up'
    return (
      <Box>
        <Box style={stylesRevokedRow} onClick={e => this._toggleHeader(e)}>
          <Text type='BodySemibold'>Revoked devices</Text>
          <Icon type={iconType} style={{padding: 5}} />
        </Box>
        {this.state.expanded && this.props.children}
      </Box>
    )
  }
}

const DeviceRow = ({device, revoked, showRemoveDevicePage, showExistingDevicePage}) => {
  const icon: IconProps.type = {
    'mobile': 'phone-bw-m',
    'desktop': 'computer-bw-s-2',
    'backup': 'paper-key-m',
  }[device.type]

  let textStyle = {fontStyle: 'italic'}
  if (revoked) {
    textStyle = {
      ...textStyle,
      color: globalColors.black_40,
      textDecoration: 'line-through',
    }
  }

  return (
    <Box
      className='existing-device-container'
      key={device.name}
      style={{...stylesCommonRow, backgroundColor: revoked ? globalColors.white_40 : globalColors.white}}>
      <Box style={revoked ? stylesRevokedIconColumn : stylesIconColumn}>
        <Icon type={icon} />
      </Box>
      <Box style={stylesCommonColumn} onClick={() => showExistingDevicePage(device)}>
        <Box style={{...globalStyles.flexBoxRow}}>
          <Text style={textStyle} type='Header'>{device.name}</Text>
        </Box>
        <Box style={{...globalStyles.flexBoxRow}}>
          {device.currentDevice && <Text type='BodySmall'>Current device</Text>}
        </Box>
      </Box>
      <Box style={{...stylesRevokedColumn}}>
        {!revoked && <Text className='existing-device-item' style={{color: globalColors.red}} onClick={() => showRemoveDevicePage(device)} type='BodyPrimaryLink'>Revoke</Text>}
      </Box>
    </Box>
  )
}

const RevokedDescription = () => (
  <Box style={stylesRevokedDescription}>
    <Text type='BodySemibold' style={{color: globalColors.black_40}}>Revoked devices will no longer be able to access your Keybase account.</Text>
  </Box>
)

const RevokedDevices = ({revokedDevices, showExistingDevicePage}) => (
  <RevokedHeader>
    <RevokedDescription />
    {revokedDevices.map(device => <DeviceRow key={device.name} device={device} revoked showExistingDevicePage={showExistingDevicePage} />)}
  </RevokedHeader>
)

const DeviceHeader = ({addNewDevice, showingMenu, onHidden, menuItems}) => (
  <Box style={{...stylesCommonRow, ...globalStyles.clickable}} onClick={addNewDevice}>
    <Box style={stylesCommonColumn}>
      <Icon type='devices-add-s' />
    </Box>
    <Box style={stylesCommonColumn}>
      <Text type='BodyPrimaryLink' onClick={addNewDevice}>Add new...</Text>
    </Box>
    <PopupMenu style={stylesPopup} visible={showingMenu} items={menuItems} onHidden={onHidden} />
  </Box>
)

type State = {showingMenu: boolean}

class Render extends Component<void, Props, State> {
  state: State;

  constructor (props: Props) {
    super(props)

    this.state = {
      showingMenu: false,
    }
  }

  _items () {
    return [
      {title: 'New Phone', onClick: () => this.props.addNewPhone()},
      {title: 'New Computer', onClick: () => this.props.addNewComputer()},
      {title: 'New Paper Key', onClick: () => this.props.addNewPaperKey()},
    ]
  }

  render () {
    const {devices, revokedDevices, showRemoveDevicePage, showExistingDevicePage} = this.props

    const realCSS = `
    .existing-device-container .existing-device-item {
      display: none;
    }
    .existing-device-container:hover .existing-device-item {
      display: block;
    }
    `
    return (
      <Box style={stylesContainer}>
        <DeviceHeader
          menuItems={this._items()}
          addNewDevice={() => this.setState({showingMenu: true})}
          showingMenu={this.state.showingMenu}
          onHidden={() => this.setState({showingMenu: false})} />
        <style>{realCSS}</style>
        {devices && devices.map(device => <DeviceRow key={device.name} device={device} showRemoveDevicePage={showRemoveDevicePage} showExistingDevicePage={showExistingDevicePage} />)}
        {revokedDevices && <RevokedDevices revokedDevices={revokedDevices} showExistingDevicePage={showExistingDevicePage} />}
      </Box>
    )
  }
}

const stylesContainer = {
  ...globalStyles.scrollable,
}

const stylesCommonRow = {
  ...globalStyles.flexBoxRow,
  alignItems: 'center',
  borderTop: 'solid 1px rgba(0, 0, 0, .1)',
  height: 60,
  justifyContent: 'center',
  padding: 8,
}

const stylesRevokedRow = {
  ...stylesCommonRow,
  height: 30,
  justifyContent: 'flex-start',
  backgroundColor: globalColors.lightGrey,
}

const stylesRevokedDescription = {
  ...stylesCommonRow,
  backgroundColor: globalColors.lightGrey,
}

const stylesCommonColumn = {
  padding: 20,
}

const stylesRevokedColumn = {
  ...stylesCommonColumn,
  alignSelf: 'center',
  flex: 1,
  textAlign: 'right',
  paddingRight: 20,
}

const stylesIconColumn = {
  ...stylesCommonColumn,
  width: 65,
}

const stylesRevokedIconColumn = {
  ...stylesIconColumn,
  opacity: 0.2,
}

const stylesPopup = {
  marginLeft: 'auto',
  marginRight: 'auto',
  marginTop: 50,
}

export default Render
<commit_msg>Per Cecile: pages with lists should have a grey background, not white<commit_after>/* @flow */
import React, {Component} from 'react'
import {Box, Text, Icon, PopupMenu} from '../common-adapters'
import {globalStyles, globalColors} from '../styles/style-guide'
import type {Props as IconProps} from '../common-adapters/icon'

import type {Props} from './render'

type RevokedHeaderProps = {children?: Array<any>}
type RevokedHeaderState = {expanded: boolean}
class RevokedHeader extends Component<void, RevokedHeaderProps, RevokedHeaderState> {
  state: RevokedHeaderState;

  constructor (props: Props) {
    super(props)
    this.state = {
      expanded: false,
    }
  }

  _toggleHeader (e) {
    this.setState({expanded: !this.state.expanded})
  }

  render () {
    const iconType = this.state.expanded ? 'fa-caret-down' : 'fa-caret-up'
    return (
      <Box>
        <Box style={stylesRevokedRow} onClick={e => this._toggleHeader(e)}>
          <Text type='BodySemibold'>Revoked devices</Text>
          <Icon type={iconType} style={{padding: 5}} />
        </Box>
        {this.state.expanded && this.props.children}
      </Box>
    )
  }
}

const DeviceRow = ({device, revoked, showRemoveDevicePage, showExistingDevicePage}) => {
  const icon: IconProps.type = {
    'mobile': 'phone-bw-m',
    'desktop': 'computer-bw-s-2',
    'backup': 'paper-key-m',
  }[device.type]

  let textStyle = {fontStyle: 'italic'}
  if (revoked) {
    textStyle = {
      ...textStyle,
      color: globalColors.black_40,
      textDecoration: 'line-through',
    }
  }

  return (
    <Box
      className='existing-device-container'
      key={device.name}
      style={{...stylesCommonRow, backgroundColor: revoked ? globalColors.white_40 : globalColors.white}}>
      <Box style={revoked ? stylesRevokedIconColumn : stylesIconColumn}>
        <Icon type={icon} />
      </Box>
      <Box style={stylesCommonColumn} onClick={() => showExistingDevicePage(device)}>
        <Box style={{...globalStyles.flexBoxRow}}>
          <Text style={textStyle} type='Header'>{device.name}</Text>
        </Box>
        <Box style={{...globalStyles.flexBoxRow}}>
          {device.currentDevice && <Text type='BodySmall'>Current device</Text>}
        </Box>
      </Box>
      <Box style={{...stylesRevokedColumn}}>
        {!revoked && <Text className='existing-device-item' style={{color: globalColors.red}} onClick={() => showRemoveDevicePage(device)} type='BodyPrimaryLink'>Revoke</Text>}
      </Box>
    </Box>
  )
}

const RevokedDescription = () => (
  <Box style={stylesRevokedDescription}>
    <Text type='BodySemibold' style={{color: globalColors.black_40}}>Revoked devices will no longer be able to access your Keybase account.</Text>
  </Box>
)

const RevokedDevices = ({revokedDevices, showExistingDevicePage}) => (
  <RevokedHeader>
    <RevokedDescription />
    {revokedDevices.map(device => <DeviceRow key={device.name} device={device} revoked showExistingDevicePage={showExistingDevicePage} />)}
  </RevokedHeader>
)

const DeviceHeader = ({addNewDevice, showingMenu, onHidden, menuItems}) => (
  <Box style={{...stylesCommonRow, ...globalStyles.clickable, backgroundColor: globalColors.white}} onClick={addNewDevice}>
    <Box style={stylesCommonColumn}>
      <Icon type='devices-add-s' />
    </Box>
    <Box style={stylesCommonColumn}>
      <Text type='BodyPrimaryLink' onClick={addNewDevice}>Add new...</Text>
    </Box>
    <PopupMenu style={stylesPopup} visible={showingMenu} items={menuItems} onHidden={onHidden} />
  </Box>
)

type State = {showingMenu: boolean}

class Render extends Component<void, Props, State> {
  state: State;

  constructor (props: Props) {
    super(props)

    this.state = {
      showingMenu: false,
    }
  }

  _items () {
    return [
      {title: 'New Phone', onClick: () => this.props.addNewPhone()},
      {title: 'New Computer', onClick: () => this.props.addNewComputer()},
      {title: 'New Paper Key', onClick: () => this.props.addNewPaperKey()},
    ]
  }

  render () {
    const {devices, revokedDevices, showRemoveDevicePage, showExistingDevicePage} = this.props

    const realCSS = `
    .existing-device-container .existing-device-item {
      display: none;
    }
    .existing-device-container:hover .existing-device-item {
      display: block;
    }
    `
    return (
      <Box style={{stylesContainer, backgroundColor: globalColors.lightGrey}}>
        <DeviceHeader
          menuItems={this._items()}
          addNewDevice={() => this.setState({showingMenu: true})}
          showingMenu={this.state.showingMenu}
          onHidden={() => this.setState({showingMenu: false})} />
        <style>{realCSS}</style>
        {devices && devices.map(device => <DeviceRow key={device.name} device={device} showRemoveDevicePage={showRemoveDevicePage} showExistingDevicePage={showExistingDevicePage} />)}
        {revokedDevices && <RevokedDevices revokedDevices={revokedDevices} showExistingDevicePage={showExistingDevicePage} />}
      </Box>
    )
  }
}

const stylesContainer = {
  ...globalStyles.scrollable,
}

const stylesCommonRow = {
  ...globalStyles.flexBoxRow,
  alignItems: 'center',
  borderTop: 'solid 1px rgba(0, 0, 0, .1)',
  height: 60,
  justifyContent: 'center',
  padding: 8,
}

const stylesRevokedRow = {
  ...stylesCommonRow,
  height: 30,
  justifyContent: 'flex-start',
  backgroundColor: globalColors.lightGrey,
}

const stylesRevokedDescription = {
  ...stylesCommonRow,
  backgroundColor: globalColors.lightGrey,
}

const stylesCommonColumn = {
  padding: 20,
}

const stylesRevokedColumn = {
  ...stylesCommonColumn,
  alignSelf: 'center',
  flex: 1,
  textAlign: 'right',
  paddingRight: 20,
}

const stylesIconColumn = {
  ...stylesCommonColumn,
  width: 65,
}

const stylesRevokedIconColumn = {
  ...stylesIconColumn,
  opacity: 0.2,
}

const stylesPopup = {
  marginLeft: 'auto',
  marginRight: 'auto',
  marginTop: 50,
}

export default Render
<|endoftext|>


<commit_before>/**
 * Created by azu on 2013/11/09.
 */
$(function () {
    // yyyy-MM-dd に該当する付きのjsonを開く
    function editOnGithub(dateString) {
        var editURL = "https://github.com/azu/jser.info/edit/gh-pages/data/" + dateString + "/index.json";
        location.href = editURL;
    }

    var $edit = $("#edit-on-github");
    $edit.button().on("click", function (event) {
        console.log("event", event);
        var dirPath = $("#content").data("file-dir-path");
        if (dirPath) {
            editOnGithub(dirPath);
        }
    });
});<commit_msg>fix data path<commit_after>/**
 * Created by azu on 2013/11/09.
 */
$(function () {
    // yyyy-MM-dd に該当する付きのjsonを開く
    function editOnGithub(dateString) {
        var editURL = "https://github.com/azu/jser.info/edit/gh-pages/" + dateString + "/index.json";
        location.href = editURL;
    }

    var $edit = $("#edit-on-github");
    $edit.button().on("click", function (event) {
        var dirPath = $("#content").data("file-dir-path");
        if (dirPath) {
            editOnGithub(dirPath);
        }
    });
});<|endoftext|>


<commit_before>/**
  This is a jQuery plugin to support autocompleting values in our text fields.

  @module $.fn.autocomplete
**/
export var CANCELLED_STATUS = "__CANCELLED";

const allowedLettersRegex = /[\s\t\[\{\(\/]/;

var keys = {
  backSpace: 8,
  tab: 9,
  enter: 13,
  shift: 16,
  ctrl: 17,
  alt: 18,
  esc: 27,
  space: 32,
  leftWindows: 91,
  rightWindows: 92,
  pageUp: 33,
  pageDown: 34,
  end: 35,
  home: 36,
  leftArrow: 37,
  upArrow: 38,
  rightArrow: 39,
  downArrow: 40,
  insert: 45,
  deleteKey: 46,
  zero: 48,
  a: 65,
  z: 90,
};

let inputTimeout;

export default function(options) {
  var autocompletePlugin = this;

  if (this.length === 0) return;

  if (options === 'destroy') {
    Ember.run.cancel(inputTimeout);

    $(this).off('keyup.autocomplete')
           .off('keydown.autocomplete')
           .off('paste.autocomplete')
           .off('click.autocomplete');

    return;
  }

  if (options && options.cancel && this.data("closeAutocomplete")) {
    this.data("closeAutocomplete")();
    return this;
  }

  if (this.length !== 1) {
    if (window.console) {
      window.console.log("WARNING: passed multiple elements to $.autocomplete, skipping.");
      if (window.Error) {
        window.console.log((new window.Error()).stack);
      }
    }
    return this;
  }

  var disabled = options && options.disabled;
  var wrap = null;
  var autocompleteOptions = null;
  var selectedOption = null;
  var completeStart = null;
  var completeEnd = null;
  var me = this;
  var div = null;
  var prevTerm = null;

  // input is handled differently
  var isInput = this[0].tagName === "INPUT";
  var inputSelectedItems = [];

  var closeAutocomplete = function() {
    if (div) {
      div.hide().remove();
    }
    div = null;
    completeStart = null;
    autocompleteOptions = null;
    prevTerm = null;
  };

  var addInputSelectedItem = function(item) {
    var transformed,
        transformedItem = item;

    if (options.transformComplete) { transformedItem = options.transformComplete(transformedItem); }
    // dump what we have in single mode, just in case
    if (options.single) { inputSelectedItems = []; }
    transformed = _.isArray(transformedItem) ? transformedItem : [transformedItem || item];

    var divs = transformed.map(function(itm) {
      var d = $("
" + itm + "
");
      var prev = me.parent().find('.item:last');
      if (prev.length === 0) {
        me.parent().prepend(d);
      } else {
        prev.after(d);
      }
      inputSelectedItems.push(itm);
      return d[0];
    });

    if (options.onChangeItems) { options.onChangeItems(inputSelectedItems); }

    $(divs).find('a').click(function() {
      closeAutocomplete();
      inputSelectedItems.splice($.inArray(transformedItem, inputSelectedItems), 1);
      $(this).parent().parent().remove();
      if (options.single) {
        me.show();
      }
      if (options.onChangeItems) {
        options.onChangeItems(inputSelectedItems);
      }
      return false;
    });
  };

  var completeTerm = function(term) {
    if (term) {
      if (isInput) {
        me.val("");
        if(options.single){
          me.hide();
        }
        addInputSelectedItem(term);
      } else {
        if (options.transformComplete) {
          term = options.transformComplete(term);
        }

        if (term) {
          var text = me.val();
          text = text.substring(0, completeStart) + (options.key || "") + term + ' ' + text.substring(completeEnd + 1, text.length);
          me.val(text);
          Discourse.Utilities.setCaretPosition(me[0], completeStart + 1 + term.length);

          if (options && options.afterComplete) {
            options.afterComplete(text);
          }
        }
      }
    }
    closeAutocomplete();
  };

  if (isInput) {
    var width = this.width();
    wrap = this.wrap("
").parent();
    wrap.width(width);
    if(options.single) {
      this.css("width","100%");
    } else {
      this.width(150);
    }
    this.attr('name', this.attr('name') + "-renamed");
    var vals = this.val().split(",");
    _.each(vals,function(x) {
      if (x !== "") {
        if (options.reverseTransform) {
          x = options.reverseTransform(x);
        }
        addInputSelectedItem(x);
      }
    });
    if(options.items) {
      _.each(options.items, function(item){
        addInputSelectedItem(item);
      });
    }
    this.val("");
    completeStart = 0;
    wrap.click(function() {
      autocompletePlugin.focus();
      return true;
    });
  }

  var markSelected = function() {
    var links = div.find('li a');
    links.removeClass('selected');
    return $(links[selectedOption]).addClass('selected');
  };

  var renderAutocomplete = function() {
    if (div) {
      div.hide().remove();
    }
    if (autocompleteOptions.length === 0) return;

    div = $(options.template({ options: autocompleteOptions }));

    var ul = div.find('ul');
    selectedOption = 0;
    markSelected();
    ul.find('li').click(function() {
      selectedOption = ul.find('li').index(this);
      completeTerm(autocompleteOptions[selectedOption]);
      return false;
    });
    var pos = null;
    var vOffset = 0;
    var hOffset = 0;
    if (isInput) {
      pos = {
        left: 0,
        top: 0
      };
      vOffset = -32;
      hOffset = 0;
    } else {
      pos = me.caretPosition({
        pos: completeStart,
        key: options.key
      });
      hOffset = 27;
    }
    div.css({
      left: "-1000px"
    });

    me.parent().append(div);

    if(!isInput){
      vOffset = div.height();
    }

    if (Discourse.Site.currentProp('mobileView') && !isInput) {
      div.css('width', 'auto');

      if ((me.height() / 2) >= pos.top) { vOffset = -23; }
      if ((me.width() / 2) <= pos.left) { hOffset = -div.width(); }
    }

    var mePos = me.position();
    var borderTop = parseInt(me.css('border-top-width'), 10) || 0;
    div.css({
      position: 'absolute',
      top: (mePos.top + pos.top - vOffset + borderTop) + 'px',
      left: (mePos.left + pos.left + hOffset) + 'px'
    });
  };

  const SKIP = "skip";

  const dataSource = (term, opts) => {
    if (prevTerm === term) {
      return SKIP;
    }

    prevTerm = term;
    if (term.length !== 0 && term.trim().length === 0) {
      closeAutocomplete();
      return null;
    } else {
      return opts.dataSource(term);
    }
  };

  const updateAutoComplete = function(r) {

    if (completeStart === null || r === SKIP) return;

    if (r && r.then && typeof(r.then) === "function") {
      if (div) {
        div.hide().remove();
      }
      r.then(updateAutoComplete);
      return;
    }

    // Allow an update method to cancel. This allows us to debounce
    // promises without leaking
    if (r === CANCELLED_STATUS) {
      return;
    }

    autocompleteOptions = r;
    if (!r || r.length === 0) {
      closeAutocomplete();
    } else {
      renderAutocomplete();
    }
  };

  // chain to allow multiples
  var oldClose = me.data("closeAutocomplete");
  me.data("closeAutocomplete", function() {
    if (oldClose) {
      oldClose();
    }
    closeAutocomplete();
  });

  $(this).on('click.autocomplete', function() {
    closeAutocomplete();
  });

  $(this).on('paste.autocomplete', function() {
    _.delay(function(){
      me.trigger("keydown");
    }, 50);
  });

  const checkTriggerRule = (opts) => {
    if (options.triggerRule) {
      return options.triggerRule(me[0], opts);
    } else {
      return true;
    }
  };

  $(this).on('keyup.autocomplete', function() {

    var caretPosition = Discourse.Utilities.caretPosition(me[0]);

    if (options.key && completeStart === null && caretPosition > 0) {
      var key = me[0].value[caretPosition-1];
      if (key === options.key) {
        var prevChar = me.val().charAt(caretPosition-2);
        if (checkTriggerRule() && (!prevChar || allowedLettersRegex.test(prevChar))) {
          completeStart = completeEnd = caretPosition-1;
          updateAutoComplete(dataSource("", options));
        }
      }
    } else if (completeStart !== null) {
      var term = me.val().substring(completeStart + (options.key ? 1 : 0), caretPosition);
      updateAutoComplete(dataSource(term, options));
    }
  });

  $(this).on('keydown.autocomplete', function(e) {
    var c, caretPosition, i, initial, prev, prevIsGood, stopFound, term, total, userToComplete;

    if(e.ctrlKey || e.altKey || e.metaKey){
      return true;
    }

    if(options.allowAny){
      // saves us wiring up a change event as well

      Ember.run.cancel(inputTimeout);
      inputTimeout = Ember.run.later(function(){
        if(inputSelectedItems.length === 0) {
          inputSelectedItems.push("");
        }

        if(_.isString(inputSelectedItems[0]) && me.val().length > 0) {
          inputSelectedItems.pop();
          inputSelectedItems.push(me.val());
          if (options.onChangeItems) {
            options.onChangeItems(inputSelectedItems);
          }
        }

      }, 50);
    }

    if (!options.key) {
      completeStart = 0;
    }
    if (e.which === keys.shift) return;
    if ((completeStart === null) && e.which === keys.backSpace && options.key) {
      c = Discourse.Utilities.caretPosition(me[0]);
      c -= 1;
      initial = c;
      prevIsGood = true;
      while (prevIsGood && c >= 0) {
        c -= 1;
        prev = me[0].value[c];
        stopFound = prev === options.key;
        if (stopFound) {
          prev = me[0].value[c - 1];
          if (checkTriggerRule({ backSpace: true }) && (!prev || allowedLettersRegex.test(prev))) {
            completeStart = c;
            caretPosition = completeEnd = initial;
            term = me[0].value.substring(c + 1, initial);
            updateAutoComplete(dataSource(term, options));
            return true;
          }
        }
        prevIsGood = /[a-zA-Z\.-]/.test(prev);
      }
    }

    // ESC
    if (e.which === keys.esc) {
      if (div !== null) {
        closeAutocomplete();
        return false;
      }
      return true;
    }

    if (completeStart !== null) {
      caretPosition = Discourse.Utilities.caretPosition(me[0]);

      // allow people to right arrow out of completion
      if (e.which === keys.rightArrow && me[0].value[caretPosition] === ' ') {
        closeAutocomplete();
        return true;
      }

      // If we've backspaced past the beginning, cancel unless no key
      if (caretPosition <= completeStart && options.key) {
        closeAutocomplete();
        return true;
      }

      // Keyboard codes! So 80's.
      switch (e.which) {
        case keys.enter:
        case keys.tab:
          if (!autocompleteOptions) return true;
          if (selectedOption >= 0 && (userToComplete = autocompleteOptions[selectedOption])) {
            completeTerm(userToComplete);
          } else {
            // We're cancelling it, really.
            return true;
          }
          e.stopImmediatePropagation();
          return false;
        case keys.upArrow:
          selectedOption = selectedOption - 1;
          if (selectedOption < 0) {
            selectedOption = 0;
          }
          markSelected();
          return false;
        case keys.downArrow:
          total = autocompleteOptions.length;
          selectedOption = selectedOption + 1;
          if (selectedOption >= total) {
            selectedOption = total - 1;
          }
          if (selectedOption < 0) {
            selectedOption = 0;
          }
          markSelected();
          return false;
        case keys.backSpace:
          completeEnd = caretPosition;
          caretPosition--;

          if (caretPosition < 0) {
            closeAutocomplete();
            if (isInput) {
              i = wrap.find('a:last');
              if (i) {
                i.click();
              }
            }
            return true;
          }

          term = me.val().substring(completeStart + (options.key ? 1 : 0), caretPosition);

          if ((completeStart === caretPosition) && (term === options.key)) {
            closeAutocomplete();
          }

          updateAutoComplete(dataSource(term, options));
          return true;
        default:
          completeEnd = caretPosition;
          return true;
      }
    }
  });

  return this;
}
<commit_msg>FIX: Autocomplete retriggering when term is blank on keyup.<commit_after>/**
  This is a jQuery plugin to support autocompleting values in our text fields.

  @module $.fn.autocomplete
**/
export var CANCELLED_STATUS = "__CANCELLED";

const allowedLettersRegex = /[\s\t\[\{\(\/]/;

var keys = {
  backSpace: 8,
  tab: 9,
  enter: 13,
  shift: 16,
  ctrl: 17,
  alt: 18,
  esc: 27,
  space: 32,
  leftWindows: 91,
  rightWindows: 92,
  pageUp: 33,
  pageDown: 34,
  end: 35,
  home: 36,
  leftArrow: 37,
  upArrow: 38,
  rightArrow: 39,
  downArrow: 40,
  insert: 45,
  deleteKey: 46,
  zero: 48,
  a: 65,
  z: 90,
};

let inputTimeout;

export default function(options) {
  var autocompletePlugin = this;

  if (this.length === 0) return;

  if (options === 'destroy') {
    Ember.run.cancel(inputTimeout);

    $(this).off('keyup.autocomplete')
           .off('keydown.autocomplete')
           .off('paste.autocomplete')
           .off('click.autocomplete');

    return;
  }

  if (options && options.cancel && this.data("closeAutocomplete")) {
    this.data("closeAutocomplete")();
    return this;
  }

  if (this.length !== 1) {
    if (window.console) {
      window.console.log("WARNING: passed multiple elements to $.autocomplete, skipping.");
      if (window.Error) {
        window.console.log((new window.Error()).stack);
      }
    }
    return this;
  }

  var disabled = options && options.disabled;
  var wrap = null;
  var autocompleteOptions = null;
  var selectedOption = null;
  var completeStart = null;
  var completeEnd = null;
  var me = this;
  var div = null;
  var prevTerm = null;

  // input is handled differently
  var isInput = this[0].tagName === "INPUT";
  var inputSelectedItems = [];

  var closeAutocomplete = function() {
    if (div) {
      div.hide().remove();
    }
    div = null;
    completeStart = null;
    autocompleteOptions = null;
    prevTerm = null;
  };

  var addInputSelectedItem = function(item) {
    var transformed,
        transformedItem = item;

    if (options.transformComplete) { transformedItem = options.transformComplete(transformedItem); }
    // dump what we have in single mode, just in case
    if (options.single) { inputSelectedItems = []; }
    transformed = _.isArray(transformedItem) ? transformedItem : [transformedItem || item];

    var divs = transformed.map(function(itm) {
      var d = $("
" + itm + "
");
      var prev = me.parent().find('.item:last');
      if (prev.length === 0) {
        me.parent().prepend(d);
      } else {
        prev.after(d);
      }
      inputSelectedItems.push(itm);
      return d[0];
    });

    if (options.onChangeItems) { options.onChangeItems(inputSelectedItems); }

    $(divs).find('a').click(function() {
      closeAutocomplete();
      inputSelectedItems.splice($.inArray(transformedItem, inputSelectedItems), 1);
      $(this).parent().parent().remove();
      if (options.single) {
        me.show();
      }
      if (options.onChangeItems) {
        options.onChangeItems(inputSelectedItems);
      }
      return false;
    });
  };

  var completeTerm = function(term) {
    if (term) {
      if (isInput) {
        me.val("");
        if(options.single){
          me.hide();
        }
        addInputSelectedItem(term);
      } else {
        if (options.transformComplete) {
          term = options.transformComplete(term);
        }

        if (term) {
          var text = me.val();
          text = text.substring(0, completeStart) + (options.key || "") + term + ' ' + text.substring(completeEnd + 1, text.length);
          me.val(text);
          Discourse.Utilities.setCaretPosition(me[0], completeStart + 1 + term.length);

          if (options && options.afterComplete) {
            options.afterComplete(text);
          }
        }
      }
    }
    closeAutocomplete();
  };

  if (isInput) {
    var width = this.width();
    wrap = this.wrap("
").parent();
    wrap.width(width);
    if(options.single) {
      this.css("width","100%");
    } else {
      this.width(150);
    }
    this.attr('name', this.attr('name') + "-renamed");
    var vals = this.val().split(",");
    _.each(vals,function(x) {
      if (x !== "") {
        if (options.reverseTransform) {
          x = options.reverseTransform(x);
        }
        addInputSelectedItem(x);
      }
    });
    if(options.items) {
      _.each(options.items, function(item){
        addInputSelectedItem(item);
      });
    }
    this.val("");
    completeStart = 0;
    wrap.click(function() {
      autocompletePlugin.focus();
      return true;
    });
  }

  var markSelected = function() {
    var links = div.find('li a');
    links.removeClass('selected');
    return $(links[selectedOption]).addClass('selected');
  };

  var renderAutocomplete = function() {
    if (div) {
      div.hide().remove();
    }
    if (autocompleteOptions.length === 0) return;

    div = $(options.template({ options: autocompleteOptions }));

    var ul = div.find('ul');
    selectedOption = 0;
    markSelected();
    ul.find('li').click(function() {
      selectedOption = ul.find('li').index(this);
      completeTerm(autocompleteOptions[selectedOption]);
      return false;
    });
    var pos = null;
    var vOffset = 0;
    var hOffset = 0;
    if (isInput) {
      pos = {
        left: 0,
        top: 0
      };
      vOffset = -32;
      hOffset = 0;
    } else {
      pos = me.caretPosition({
        pos: completeStart,
        key: options.key
      });
      hOffset = 27;
    }
    div.css({
      left: "-1000px"
    });

    me.parent().append(div);

    if(!isInput){
      vOffset = div.height();
    }

    if (Discourse.Site.currentProp('mobileView') && !isInput) {
      div.css('width', 'auto');

      if ((me.height() / 2) >= pos.top) { vOffset = -23; }
      if ((me.width() / 2) <= pos.left) { hOffset = -div.width(); }
    }

    var mePos = me.position();
    var borderTop = parseInt(me.css('border-top-width'), 10) || 0;
    div.css({
      position: 'absolute',
      top: (mePos.top + pos.top - vOffset + borderTop) + 'px',
      left: (mePos.left + pos.left + hOffset) + 'px'
    });
  };

  const SKIP = "skip";

  const dataSource = (term, opts) => {
    if (prevTerm === term) {
      return SKIP;
    }

    prevTerm = term;
    if (term.length !== 0 && term.trim().length === 0) {
      closeAutocomplete();
      return null;
    } else {
      return opts.dataSource(term);
    }
  };

  const updateAutoComplete = function(r) {

    if (completeStart === null || r === SKIP) return;

    if (r && r.then && typeof(r.then) === "function") {
      if (div) {
        div.hide().remove();
      }
      r.then(updateAutoComplete);
      return;
    }

    // Allow an update method to cancel. This allows us to debounce
    // promises without leaking
    if (r === CANCELLED_STATUS) {
      return;
    }

    autocompleteOptions = r;
    if (!r || r.length === 0) {
      closeAutocomplete();
    } else {
      renderAutocomplete();
    }
  };

  // chain to allow multiples
  var oldClose = me.data("closeAutocomplete");
  me.data("closeAutocomplete", function() {
    if (oldClose) {
      oldClose();
    }
    closeAutocomplete();
  });

  $(this).on('click.autocomplete', function() {
    closeAutocomplete();
  });

  $(this).on('paste.autocomplete', function() {
    _.delay(function(){
      me.trigger("keydown");
    }, 50);
  });

  const checkTriggerRule = (opts) => {
    if (options.triggerRule) {
      return options.triggerRule(me[0], opts);
    } else {
      return true;
    }
  };

  $(this).on('keyup.autocomplete', function(e) {
    if ([keys.esc, keys.enter].indexOf(e.which) !== -1) return true;

    var caretPosition = Discourse.Utilities.caretPosition(me[0]);

    if (options.key && completeStart === null && caretPosition > 0) {
      var key = me[0].value[caretPosition-1];
      if (key === options.key) {
        var prevChar = me.val().charAt(caretPosition-2);
        if (checkTriggerRule() && (!prevChar || allowedLettersRegex.test(prevChar))) {
          completeStart = completeEnd = caretPosition-1;
          updateAutoComplete(dataSource("", options));
        }
      }
    } else if (completeStart !== null) {
      var term = me.val().substring(completeStart + (options.key ? 1 : 0), caretPosition);
      updateAutoComplete(dataSource(term, options));
    }
  });

  $(this).on('keydown.autocomplete', function(e) {
    var c, caretPosition, i, initial, prev, prevIsGood, stopFound, term, total, userToComplete;

    if(e.ctrlKey || e.altKey || e.metaKey){
      return true;
    }

    if(options.allowAny){
      // saves us wiring up a change event as well

      Ember.run.cancel(inputTimeout);
      inputTimeout = Ember.run.later(function(){
        if(inputSelectedItems.length === 0) {
          inputSelectedItems.push("");
        }

        if(_.isString(inputSelectedItems[0]) && me.val().length > 0) {
          inputSelectedItems.pop();
          inputSelectedItems.push(me.val());
          if (options.onChangeItems) {
            options.onChangeItems(inputSelectedItems);
          }
        }

      }, 50);
    }

    if (!options.key) {
      completeStart = 0;
    }
    if (e.which === keys.shift) return;
    if ((completeStart === null) && e.which === keys.backSpace && options.key) {
      c = Discourse.Utilities.caretPosition(me[0]);
      c -= 1;
      initial = c;
      prevIsGood = true;
      while (prevIsGood && c >= 0) {
        c -= 1;
        prev = me[0].value[c];
        stopFound = prev === options.key;
        if (stopFound) {
          prev = me[0].value[c - 1];
          if (checkTriggerRule({ backSpace: true }) && (!prev || allowedLettersRegex.test(prev))) {
            completeStart = c;
            caretPosition = completeEnd = initial;
            term = me[0].value.substring(c + 1, initial);
            updateAutoComplete(dataSource(term, options));
            return true;
          }
        }
        prevIsGood = /[a-zA-Z\.-]/.test(prev);
      }
    }

    // ESC
    if (e.which === keys.esc) {
      if (div !== null) {
        closeAutocomplete();
        return false;
      }
      return true;
    }

    if (completeStart !== null) {
      caretPosition = Discourse.Utilities.caretPosition(me[0]);

      // allow people to right arrow out of completion
      if (e.which === keys.rightArrow && me[0].value[caretPosition] === ' ') {
        closeAutocomplete();
        return true;
      }

      // If we've backspaced past the beginning, cancel unless no key
      if (caretPosition <= completeStart && options.key) {
        closeAutocomplete();
        return true;
      }

      // Keyboard codes! So 80's.
      switch (e.which) {
        case keys.enter:
        case keys.tab:
          if (!autocompleteOptions) return true;
          if (selectedOption >= 0 && (userToComplete = autocompleteOptions[selectedOption])) {
            completeTerm(userToComplete);
          } else {
            // We're cancelling it, really.
            return true;
          }
          e.stopImmediatePropagation();
          return false;
        case keys.upArrow:
          selectedOption = selectedOption - 1;
          if (selectedOption < 0) {
            selectedOption = 0;
          }
          markSelected();
          return false;
        case keys.downArrow:
          total = autocompleteOptions.length;
          selectedOption = selectedOption + 1;
          if (selectedOption >= total) {
            selectedOption = total - 1;
          }
          if (selectedOption < 0) {
            selectedOption = 0;
          }
          markSelected();
          return false;
        case keys.backSpace:
          completeEnd = caretPosition;
          caretPosition--;

          if (caretPosition < 0) {
            closeAutocomplete();
            if (isInput) {
              i = wrap.find('a:last');
              if (i) {
                i.click();
              }
            }
            return true;
          }

          term = me.val().substring(completeStart + (options.key ? 1 : 0), caretPosition);

          if ((completeStart === caretPosition) && (term === options.key)) {
            closeAutocomplete();
          }

          updateAutoComplete(dataSource(term, options));
          return true;
        default:
          completeEnd = caretPosition;
          return true;
      }
    }
  });

  return this;
}
<|endoftext|>


<commit_before>(function(angular, undefined) {
'use strict';

// module: django.shop, TODO: move this into a summary JS file
var djangoShopModule = angular.module('django.shop.forms-set', ['djng.urls', 'djng.forms']);

// Directive <ANY shop-forms-set ...>
// Use this as a wrapper around self validating <form ...> elements (see directive below), so that
// we can disable the proceed button whenever one of those forms does not validate.
// Such a proceed button shall be rendered as:
// <button shop-dialog-proceed ng-click="proceedWith('PURCHASE_NOW')" ng-disabled="stepIsValid===false">Purchase Now</button>
djangoShopModule.directive('shopFormsSet', function() {
	return {
		require: 'shopFormsSet',
		scope: true,
		controller: function($scope) {
			// check each child form's $valid state and reduce it to one single state scope.stepIsValid
			this.reduceValidation = function(formId, formIsValid) {
				$scope.digestValidatedForms[formId] = formIsValid;
				$scope.stepIsValid = true;
				angular.forEach($scope.digestValidatedForms, function(validatedForm) {
					$scope.stepIsValid = $scope.stepIsValid && validatedForm;
				});
			};
		},
		link: {
			pre: function(scope) {
				// a map of booleans keeping the validation state for each of the child forms
				scope.digestValidatedForms = {};
			}
		}
	};
});

djangoShopModule.directive('form', ['$timeout', function($timeout) {
	return {
		restrict: 'E',
		require: ['^?shopFormsSet', 'form'],
		priority: 1,
		link: function(scope, element, attrs, controllers) {
			if (!controllers[0])
				return;  // not for forms outside <ANY shop-forms-digest></ANY shop-form-digest>

			// create new isolated scope for this form
			scope = scope.$new(true);

			element.find('input').on('keyup change', function() {
				// delay until validation is ready
				$timeout(reduceValidation);
			});
			element.find('select').on('change', function() {
				$timeout(reduceValidation);
			});
			element.find('textarea').on('blur', function() {
				$timeout(reduceValidation);
			});

			// delay first evaluation until form is fully validated
			$timeout(reduceValidation);

			function reduceValidation() {
				controllers[0].reduceValidation(scope.$id, controllers[1].$valid);
			}
		}
	};
}]);

})(window.angular);
<commit_msg>explain <form> directive<commit_after>(function(angular, undefined) {
'use strict';

// module: django.shop, TODO: move this into a summary JS file
var djangoShopModule = angular.module('django.shop.forms-set', ['djng.urls', 'djng.forms']);

// Directive <ANY shop-forms-set ...>
// Use this as a wrapper around self validating <form ...> elements (see directive below), so that
// we can disable the proceed button whenever one of those forms does not validate.
// Such a proceed button shall be rendered as:
// <button shop-dialog-proceed ng-click="proceedWith('PURCHASE_NOW')" ng-disabled="stepIsValid===false">Purchase Now</button>
djangoShopModule.directive('shopFormsSet', function() {
	return {
		require: 'shopFormsSet',
		scope: true,
		controller: function($scope) {
			// check each child form's $valid state and reduce it to one single state scope.stepIsValid
			this.reduceValidation = function(formId, formIsValid) {
				$scope.digestValidatedForms[formId] = formIsValid;
				$scope.stepIsValid = true;
				angular.forEach($scope.digestValidatedForms, function(validatedForm) {
					$scope.stepIsValid = $scope.stepIsValid && validatedForm;
				});
			};
		},
		link: {
			pre: function(scope) {
				// a map of booleans keeping the validation state for each of the child forms
				scope.digestValidatedForms = {};
			}
		}
	};
});

// This directive enriches AngularJS's internal form-controller if it is wrapped inside a
// <ANY shop-forms-set ...> directive. It's only purpose is to summarize the validity of the given forms,
// so that buttons rendered outside of the forms can check the ``stepIsValid`` attribute.
djangoShopModule.directive('form', ['$timeout', function($timeout) {
	return {
		restrict: 'E',
		require: ['^?shopFormsSet', 'form'],
		priority: 1,
		link: function(scope, element, attrs, controllers) {
			if (!controllers[0])
				return;  // not for forms outside <ANY shop-forms-digest></ANY shop-form-digest>

			// create new isolated scope for this form
			scope = scope.$new(true);

			element.find('input').on('keyup change', function() {
				// delay until validation is ready
				$timeout(reduceValidation);
			});
			element.find('select').on('change', function() {
				$timeout(reduceValidation);
			});
			element.find('textarea').on('blur', function() {
				$timeout(reduceValidation);
			});

			// delay first evaluation until form is fully validated
			$timeout(reduceValidation);

			function reduceValidation() {
				controllers[0].reduceValidation(scope.$id, controllers[1].$valid);
			}
		}
	};
}]);

})(window.angular);
<|endoftext|>