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

    var geojsonFeature = [];
    var allowedPoints = d3.map([]);

    var geoportal_count = 0;
    var naturalista_count = 0;

    var geojsonMarkerGeoportalOptions = {
        radius: 5,
        fillColor: "#ff0000",
        color: "white",
        weight: 2,
        opacity: 1,
        fillOpacity: 0.6
    };

    var geojsonMarkerNaturaListaInvOptions = {
        radius: 5,
        fillColor: "#0b9c31",
        color: "white",
        weight: 2,
        opacity: 1,
        fillOpacity: 0.6
    };

    var geojsonMarkerNaturaListaCasualOptions = {
        radius: 5,
        fillColor: "#FFFF00",
        color: "white",
        weight: 2,
        opacity: 1,
        fillOpacity: 0.6
    };

    var customOptions ={
        'maxWidth': '500',
        'className' : 'custom'
    };

    /***************************************************************** Layer creation */
    var OSM_layer = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png');

    // Google terrain map layer
    var GTM_layer = L.tileLayer('http://{s}.google.com/vt/lyrs=p&x={x}&y={y}&z={z}',{
        maxZoom: 20,
        subdomains:['mt0','mt1','mt2','mt3']
    });
    // Google Hybrid
    var GHM_layer = L.tileLayer('http://{s}.google.com/vt/lyrs=y&x={x}&y={y}&z={z}',{
        maxZoom: 20,
        subdomains:['mt0','mt1','mt2','mt3']
    });
    var drawnItems = new L.FeatureGroup();

    var milliseconds = new Date().getTime();

    var species_layer;
    var markersLayer;

    /***************************************************************** map switcher */
    /* Quite var, para poder tener acceso a la variable fuera del scope*/
    map = L.map('map', {
        center: [23.79162789, -102.04376221],
        zoom: 5,
        //maxBounds: L.latLngBounds(L.latLng(14.3227,-86.4236),L.latLng(32.4306,-118.2727)),
        layers: [
            OSM_layer,
            GTM_layer,
            GHM_layer
        ]
    });

    /***************************************************************** layer switcher */
    var baseMaps = {
        "Open Street Maps": OSM_layer,
        "Vista de terreno": GTM_layer,
        "Vista Híbrida": GHM_layer
    };

    var layer_control = L.control.layers(baseMaps).addTo(map);

    /***************************************************************** aditional controls */
    function addPointLayerGeoportal(){
        geojsonFeature =  { "type": "FeatureCollection",
            "features": allowedPoints.values()};

        markersLayer = L.markerClusterGroup({ maxClusterRadius: 30, chunkedLoading: true, which_layer: 'geoportal'});

        species_layer = L.geoJson(geojsonFeature, {
            pointToLayer: function (feature, latlng) {
                return L.circleMarker(latlng, geojsonMarkerGeoportalOptions);
            },
            onEachFeature: function (feature, layer) {
                coordinates = parseFloat(feature.geometry.coordinates[1]).toFixed(2) + ", " +  parseFloat(feature.geometry.coordinates[0]).toFixed(2);
                var p_contenido = content_geoportal(feature.properties.d);
                layer.bindPopup(p_contenido);
            }
        });

        markersLayer.addLayer(species_layer);
        map.addLayer(markersLayer);
        layer_control.addOverlay(markersLayer, "" + geoportal_count + " registros del SNIB
 (museos, colectas y proyectos de CONABIO)");
    }

    function addPointLayerNaturaLista(){
        geojsonFeature =  { "type": "FeatureCollection",
            "features": allowedPoints.values()};

        markersLayer = L.markerClusterGroup({ maxClusterRadius: 30, chunkedLoading: true, which_layer: 'naturalista'});

        species_layer = L.geoJson(geojsonFeature, {
            pointToLayer: function (feature, latlng) {
                // Para cuando es una observacion casual o de investigacion
                if (feature.properties.d.quality_grade == 'research')
                    return L.circleMarker(latlng, geojsonMarkerNaturaListaInvOptions);
                else
                    return L.circleMarker(latlng, geojsonMarkerNaturaListaCasualOptions);
                //para cuando tenga tiempo, poner el ícono como DEBE de ser!!!
                //return L.marker(latlng, {icon: L.divIcon({className: "glyphicon glyphicon-map-marker"})});
            },
            onEachFeature: function (feature, layer) {
                coordinates = parseFloat(feature.geometry.coordinates[1]).toFixed(2) + ", " +  parseFloat(feature.geometry.coordinates[0]).toFixed(2);
                var p_contenido = content_naturalista(feature.properties.d);
                layer.bindPopup(p_contenido);
            }
        });

        markersLayer.addLayer(species_layer);
        map.addLayer(markersLayer);
        layer_control.addOverlay(markersLayer, "" + naturalista_count + " observaciones de ");
    }

    function wms_distribucion_potencial() {
        var distribucion_potencial = L.tileLayer.wms(GEO.geoserver_url, {
            layers: GEO.geoserver_layer,
            format: 'image/png',
            transparent: true,
            opacity:.5,
            maxZoom: 20
        });

        map.addLayer(distribucion_potencial);
        layer_control.addOverlay(distribucion_potencial, "Distribución potencial (CONABIO)");
    }

    function content_geoportal(feature){
        var contenido = "";

        contenido += "
" + name() + "
";
        contenido += "
Localidad:

" + feature.localidad + "
";
        contenido += "
Municipio:

" + feature.municipiomapa + "
";
        contenido += "
Estado:

" + feature.estadomapa + "
";
        contenido += "
País:

" + feature.paismapa + "
";
        contenido += "
Fecha:

" + feature.fechacolecta + "
";
        contenido += "
Colector:

" + feature.colector + "
";
        contenido += "
Colección:

" + feature.coleccion + "
";
        contenido += "
Institución:

" + feature.institucion + "
";
        contenido += "
País de la colección:

" + feature.paiscoleccion + "
";

        if (feature.proyecto.length > 0 && feature.urlproyecto.length > 0)
            contenido += "
Proyecto:

" + feature.proyecto + "
";

        contenido += "
Más información:

consultar
";

        return "
" + contenido + "
";
    }

    function content_naturalista(feature){
        var contenido = "";

        contenido += "
" + name() + "
";

        if (feature.photos.length > 0)
        {
            contenido += "

"
            contenido += "
Atribución:

" + feature.photos[0].attribution + "
";
        }

        /*contenido += "
Ubicación:

" + feature.place_guess + "
";*/
        contenido += "
Fecha:

" + feature.observed_on + "
";
        contenido += "
¿silvestre / naturalizado?:

" + (feature.captive == true ? 'sí' : 'no') + "
";
        contenido += "
Grado de calidad:

" + I18n.t('quality_grade.' + feature.quality_grade) + "
";
        contenido += "
URL NaturaLista:

ver la observación
";

        return "
" + contenido + "
";
    }

    function name()
    {
        if (I18n.locale == 'es')
        {
            if (NOMBRE_COMUN_PRINCIPAL.length > 0)
                return NOMBRE_COMUN_PRINCIPAL + " (" + TAXON.nombre_cientifico + ")";
            else
                return "(" + TAXON.nombre_cientifico + ")";
        } else {
            return "(" + TAXON.nombre_cientifico + ")";
        }
    }

    var geojson_geoportal = function()
    {
        $.ajax({
            url: GEO.geoportal_url,
            dataType : "json",
            success : function (d){
                geoportal_count = d.length;
                allowedPoints = d3.map([]);

                for(i=0;i<d.length;i++)
                {
                    item_id = 'geoportal-' + i.toString();

                    allowedPoints.set(item_id, {
                        "type"      : "Feature",
                        "properties": {d: d[i]},
                        "geometry"  : JSON.parse(d[i].json_geom)
                    });
                }

                addPointLayerGeoportal();
            },
            error: function( jqXHR ,  textStatus,  errorThrown ){
                console.log("error: " + textStatus);
                console.log(errorThrown);
                console.log(jqXHR.responseText);
            }
        });  // termina ajax
    };

    var geojson_naturalista = function(){
        $.ajax({
            url: "/especies/" + TAXON.id + "/naturalista",
            dataType : "json",
            beforeSend: function(xhr){
                xhr.setRequestHeader('X-Test-Header', 'test-value');
                xhr.setRequestHeader("Accept","text/json");
            },
            success : function (d){
                naturalista_count = d.length;
                allowedPoints = d3.map([]);

                for(i=0;i<d.length;i++)
                {
                    item_id = '-' + i.toString();

                    // this map is fill with the records in the database from an specie, so it discards repetive elemnts.
                    allowedPoints.set(item_id, {
                        "type"      : "Feature",
                        "properties": {d: d[i]},
                        "geometry"  : {coordinates: [parseFloat(d[i].longitude), parseFloat(d[i].latitude)], type: "Point"}
                    });
                }
                addPointLayerNaturaLista();
            },
            error: function( jqXHR ,  textStatus,  errorThrown ){
                console.log("error: " + textStatus);
                console.log(errorThrown);
                console.log(jqXHR.responseText);
            }
        });  // termina ajax
    };

    if (GEO.cuales.indexOf("naturalista") >= 0) geojson_naturalista();
    if (GEO.cuales.indexOf("geoportal") >= 0) geojson_geoportal();
    if (GEO.cuales.indexOf("geoserver") >= 0) wms_distribucion_potencial();
});

<commit_msg>la simbologia de los puntos en el mapa<commit_after>$(document).ready(function(){

    var geojsonFeature = [];
    var allowedPoints = d3.map([]);

    var geoportal_count = 0;
    var naturalista_count = 0;

    var geojsonMarkerGeoportalOptions = {
        radius: 5,
        fillColor: "#ff0000",
        color: "white",
        weight: 2,
        opacity: 1,
        fillOpacity: 0.6
    };

    var geojsonMarkerGeoportalFosilOptions = {
        radius: 5,
        fillColor: "#2a2a2a",
        color: "white",
        weight: 2,
        opacity: 1,
        fillOpacity: 0.6
    };

    var geojsonMarkerGeoportalAveravesOptions = {
        radius: 5,
        fillColor: "#FFA500",
        color: "black",
        weight: 2,
        opacity: 1,
        fillOpacity: 0.6
    };

    var geojsonMarkerNaturaListaInvOptions = {
        radius: 5,
        fillColor: "#0b9c31",
        color: "white",
        weight: 2,
        opacity: 1,
        fillOpacity: 0.6
    };

    var geojsonMarkerNaturaListaCasualOptions = {
        radius: 5,
        fillColor: "#FFFF00",
        color: "white",
        weight: 2,
        opacity: 1,
        fillOpacity: 0.6
    };

    var customOptions ={
        'maxWidth': '500',
        'className' : 'custom'
    };

    /***************************************************************** Layer creation */
    var OSM_layer = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png');

    // Google terrain map layer
    var GTM_layer = L.tileLayer('http://{s}.google.com/vt/lyrs=p&x={x}&y={y}&z={z}',{
        maxZoom: 20,
        subdomains:['mt0','mt1','mt2','mt3']
    });
    // Google Hybrid
    var GHM_layer = L.tileLayer('http://{s}.google.com/vt/lyrs=y&x={x}&y={y}&z={z}',{
        maxZoom: 20,
        subdomains:['mt0','mt1','mt2','mt3']
    });
    var drawnItems = new L.FeatureGroup();

    var milliseconds = new Date().getTime();

    var species_layer;
    var markersLayer;

    /***************************************************************** map switcher */
    /* Quite var, para poder tener acceso a la variable fuera del scope*/
    map = L.map('map', {
        center: [23.79162789, -102.04376221],
        zoom: 5,
        //maxBounds: L.latLngBounds(L.latLng(14.3227,-86.4236),L.latLng(32.4306,-118.2727)),
        layers: [
            OSM_layer,
            GTM_layer,
            GHM_layer
        ]
    });

    /***************************************************************** layer switcher */
    var baseMaps = {
        "Open Street Maps": OSM_layer,
        "Vista de terreno": GTM_layer,
        "Vista Híbrida": GHM_layer
    };

    var layer_control = L.control.layers(baseMaps).addTo(map);

    /***************************************************************** aditional controls */
    function addPointLayerGeoportal(){
        geojsonFeature =  { "type": "FeatureCollection",
            "features": allowedPoints.values()};

        markersLayer = L.markerClusterGroup({ maxClusterRadius: 30, chunkedLoading: true, which_layer: 'geoportal'});

        species_layer = L.geoJson(geojsonFeature, {
            pointToLayer: function (feature, latlng) {

                // Para saber si es de ebird o averaves
                var coleccion = feature.properties.d.coleccion.toLowerCase();
                var fosil = feature.properties.d.taxonfosil;
                var array_coleccion = coleccion.split(" ");
                var indice_coleccion_ebird = array_coleccion.indexOf("ebird");
                var indice_coleccion_averaves = array_coleccion.indexOf("averaves");

                if (indice_coleccion_averaves >= 0 || indice_coleccion_ebird >= 0)
                    return L.circleMarker(latlng, geojsonMarkerGeoportalAveravesOptions);
                else if (fosil != undefined && fosil != "")
                    return L.circleMarker(latlng, geojsonMarkerGeoportalFosilOptions);
                else
                    return L.circleMarker(latlng, geojsonMarkerGeoportalOptions);
            },
            onEachFeature: function (feature, layer) {
                coordinates = parseFloat(feature.geometry.coordinates[1]).toFixed(2) + ", " +  parseFloat(feature.geometry.coordinates[0]).toFixed(2);
                var p_contenido = content_geoportal(feature.properties.d);
                layer.bindPopup(p_contenido);
            }
        });

        markersLayer.addLayer(species_layer);
        map.addLayer(markersLayer);

        var punto_rojo = '<svg height="50" width="200"><circle cx="10" cy="10" r="6" stroke="black" stroke-width="2" stroke-opacity="1" fill="#FF0000" fill-opacity="0.6"/>';
        punto_rojo+= '<text x="20" y="13" font-family="sans-serif" font-size="10px">Registros del SNIB</text>';

        var punto_naranja = punto_rojo + '<circle cx="10" cy="25" r="6" stroke="black" stroke-width="2" stroke-opacity="1" fill="#FFA500" fill-opacity="0.6"/>';
        punto_naranja+= '<text x="20" y="28" font-family="sans-serif" font-size="10px">Registros de AverAves</text>';

        var punto_gris = punto_naranja + '<circle cx="10" cy="40" r="6" stroke="black" stroke-width="2" stroke-opacity="1" fill="##2A2A2A" fill-opacity="0.6"/>';
        punto_gris+= '<text x="20" y="43" font-family="sans-serif" font-size="10px">Registros de Fósiles</text></svg>';

        layer_control.addOverlay(markersLayer,
            "" + geoportal_count + " Registros del SNIB
 (museos, colectas y proyectos de CONABIO)" +
            "
"+punto_gris+"
"
        );
    }

    function addPointLayerNaturaLista(){
        geojsonFeature =  { "type": "FeatureCollection",
            "features": allowedPoints.values()};

        markersLayer = L.markerClusterGroup({ maxClusterRadius: 30, chunkedLoading: true, which_layer: 'naturalista'});

        species_layer = L.geoJson(geojsonFeature, {
            pointToLayer: function (feature, latlng) {
                // Para cuando es una observacion casual o de investigacion
                if (feature.properties.d.quality_grade == 'research')
                    return L.circleMarker(latlng, geojsonMarkerNaturaListaInvOptions);
                else
                    return L.circleMarker(latlng, geojsonMarkerNaturaListaCasualOptions);
                //para cuando tenga tiempo, poner el ícono como DEBE de ser!!!
                //return L.marker(latlng, {icon: L.divIcon({className: "glyphicon glyphicon-map-marker"})});
            },
            onEachFeature: function (feature, layer) {
                coordinates = parseFloat(feature.geometry.coordinates[1]).toFixed(2) + ", " +  parseFloat(feature.geometry.coordinates[0]).toFixed(2);
                var p_contenido = content_naturalista(feature.properties.d);
                layer.bindPopup(p_contenido);
            }
        });

        markersLayer.addLayer(species_layer);
        map.addLayer(markersLayer);

        var punto_verde = '<svg height="35" width="200"><circle cx="10" cy="10" r="6" stroke="black" stroke-width="2" stroke-opacity="1" fill="#0b9c31" fill-opacity="0.6"/>';
        punto_verde+= '<text x="20" y="13" font-family="sans-serif" font-size="10px">Grado de investigación</text>';

        var punto_amarillo = punto_verde + '<circle cx="10" cy="25" r="6" stroke="black" stroke-width="2" stroke-opacity="1" fill="#FFFF00" fill-opacity="0.6"/>';
        punto_amarillo+= '<text x="20" y="28" font-family="sans-serif" font-size="10px">Grado casual</text></svg>';

        layer_control.addOverlay(markersLayer,
            "" + naturalista_count + " observaciones de " +
            "
"+punto_amarillo+"
"
        );
    }

    function wms_distribucion_potencial() {
        var distribucion_potencial = L.tileLayer.wms(GEO.geoserver_url, {
            layers: GEO.geoserver_layer,
            format: 'image/png',
            transparent: true,
            opacity:.5,
            maxZoom: 20
        });

        map.addLayer(distribucion_potencial);
        layer_control.addOverlay(distribucion_potencial, "Distribución potencial (CONABIO)");
    }

    function content_geoportal(feature){
        var contenido = "";

        contenido += "
" + name() + "
";
        contenido += "
Localidad:

" + feature.localidad + "
";
        contenido += "
Municipio:

" + feature.municipiomapa + "
";
        contenido += "
Estado:

" + feature.estadomapa + "
";
        contenido += "
País:

" + feature.paismapa + "
";
        contenido += "
Fecha:

" + feature.fechacolecta + "
";
        contenido += "
Colector:

" + feature.colector + "
";
        contenido += "
Colección:

" + feature.coleccion + "
";
        contenido += "
Institución:

" + feature.institucion + "
";
        contenido += "
País de la colección:

" + feature.paiscoleccion + "
";

        if (feature.proyecto.length > 0 && feature.urlproyecto.length > 0)
            contenido += "
Proyecto:

" + feature.proyecto + "
";

        contenido += "
Más información:

consultar
";

        return "
" + contenido + "
";
    }

    function content_naturalista(feature){
        var contenido = "";

        contenido += "
" + name() + "
";

        if (feature.photos.length > 0)
        {
            contenido += "

"
            contenido += "
Atribución:

" + feature.photos[0].attribution + "
";
        }

        /*contenido += "
Ubicación:

" + feature.place_guess + "
";*/
        contenido += "
Fecha:

" + feature.observed_on + "
";
        contenido += "
¿silvestre / naturalizado?:

" + (feature.captive == true ? 'sí' : 'no') + "
";
        contenido += "
Grado de calidad:

" + I18n.t('quality_grade.' + feature.quality_grade) + "
";
        contenido += "
URL NaturaLista:

ver la observación
";

        return "
" + contenido + "
";
    }

    function name()
    {
        if (I18n.locale == 'es')
        {
            if (NOMBRE_COMUN_PRINCIPAL.length > 0)
                return NOMBRE_COMUN_PRINCIPAL + " (" + TAXON.nombre_cientifico + ")";
            else
                return "(" + TAXON.nombre_cientifico + ")";
        } else {
            return "(" + TAXON.nombre_cientifico + ")";
        }
    }

    var geojson_geoportal = function()
    {
        $.ajax({
            url: GEO.geoportal_url,
            dataType : "json",
            success : function (d){
                geoportal_count = d.length;
                allowedPoints = d3.map([]);

                for(i=0;i<d.length;i++)
                {
                    item_id = 'geoportal-' + i.toString();

                    allowedPoints.set(item_id, {
                        "type"      : "Feature",
                        "properties": {d: d[i]},
                        "geometry"  : JSON.parse(d[i].json_geom)
                    });
                }

                addPointLayerGeoportal();
            },
            error: function( jqXHR ,  textStatus,  errorThrown ){
                console.log("error: " + textStatus);
                console.log(errorThrown);
                console.log(jqXHR.responseText);
            }
        });  // termina ajax
    };

    var geojson_naturalista = function(){
        $.ajax({
            url: "/especies/" + TAXON.id + "/naturalista",
            dataType : "json",
            beforeSend: function(xhr){
                xhr.setRequestHeader('X-Test-Header', 'test-value');
                xhr.setRequestHeader("Accept","text/json");
            },
            success : function (d){
                naturalista_count = d.length;
                allowedPoints = d3.map([]);

                for(i=0;i<d.length;i++)
                {
                    item_id = '-' + i.toString();

                    // this map is fill with the records in the database from an specie, so it discards repetive elemnts.
                    allowedPoints.set(item_id, {
                        "type"      : "Feature",
                        "properties": {d: d[i]},
                        "geometry"  : {coordinates: [parseFloat(d[i].longitude), parseFloat(d[i].latitude)], type: "Point"}
                    });
                }
                addPointLayerNaturaLista();
            },
            error: function( jqXHR ,  textStatus,  errorThrown ){
                console.log("error: " + textStatus);
                console.log(errorThrown);
                console.log(jqXHR.responseText);
            }
        });  // termina ajax
    };

    if (GEO.cuales.indexOf("naturalista") >= 0) geojson_naturalista();
    if (GEO.cuales.indexOf("geoportal") >= 0) geojson_geoportal();
    if (GEO.cuales.indexOf("geoserver") >= 0) wms_distribucion_potencial();
});

<|endoftext|>


<commit_before> $(function() {

     // uniqify an array
     Array.prototype.unique =
         function() {
             var a = [];
             var l = this.length;
             for(var i=0; i<l; i++) {
                 for(var j=i+1; j<l; j++) {
                     // If this[i] is found later in the array
                     if (this[i] === this[j])
                         j = ++i;
                 }
                 a.push(this[i]);
             }
             return a;
         };

     debug = function(line) {
         $('#scratch').val($('#scratch').val() + line + '\n');
     }
     $('#scratch').val('');

     $.ajax({
         url: "trace.txt",
         context: document.body,
         dataType: "text",
         success: function(text) {

             lines = text.split('\n')

             // Parse lines starting with "Evaluating" or "Realizing"
             evals = lines.filter(function(line) {
                 return line.substring(0, 3) == "###";
             })

             // Find lines of interest
             evals = evals.map(function(line) {
                 var pieces = line.split(' ');
                 var name = pieces[2].replace('.', '_');
                 var coords = pieces.slice(4);

                 //debug(pieces[1] + ', ' + name + ', ' + coords[0] + '**' + coords[1]);

                 coords = coords.map(function(l) {return parseInt(l);});

                 return {type:pieces[1], name:name, coords:coords};

             })

             // Detect unique functions
             var functions = evals.map(function(e) {return e.name;}).unique();

             // Make the canvas for each
             surfaces = {};
             functions.map(function(f) {
                 // Detect dimensionality (Assume 2 for now)

                 // Detect bounds (Assume 100 for now)

                 // Make a canvas
                 $('#main').append('<canvas id="' + f + '" width=300 height=300></canvas>');

                 // And a context
                 var surface = $('#' + f)[0].getContext("2d");
                 surface.strokeStyle = "rgba(0, 0, 0, 1)";
                 surface.strokeRect(0, 0, 300, 300);
                 surface.lineWidth = 2;
                 surface.lineJoin = 'miter';
                 surfaces[f] = surface;
             });

             // Make the function that will draw our events
             drawEvent = function(event) {

                 if (event.type == "Evaluating") {
                     surfaces[event.name].fillStyle = "rgba(0, 0, 200, 1)";
                     surfaces[event.name].fillRect(event.coords[0]*7+1, event.coords[1]*7+1, 5, 5);
                 } else if (event.type == "Realizing") {
                     //surfaces[event.name].fillStyle = "rgba(255, 255, 255, 1)";
                     //surfaces[event.name].fillRect(1, 1, 298, 298);
                     surfaces[event.name].strokeStyle = "rgba(0, 0, 0, 1)";
                     surfaces[event.name].strokeRect(event.coords[0]*7, event.coords[2]*7, event.coords[1]*7, event.coords[3]*7);
                 } else if (event.type == "Discarding") {
                     //debug(event.coords);
                     for (var y = event.coords[2]; y < event.coords[2] + event.coords[3]; y++) {
                         for (var x = event.coords[0]; x < event.coords[0] + event.coords[1]; x++) {
                             surfaces[event.name].strokeStyle = "rgba(255, 255, 255, 1)";
                             surfaces[event.name].strokeRect(x*7+1, y*7+1, 5, 5);
                         }
                     }
                     surfaces[event.name].strokeRect(event.coords[0]*7, event.coords[2]*7, event.coords[1]*7, event.coords[3]*7);
                 }
             };

             var count = 0;

             loop = function() {
                 if (evals.length < count) return;
                 drawEvent(evals[count]);
                 count++;
                 t = setTimeout("loop()", 1);
             }

             loop();

         }
     });
 });<commit_msg>Updated visualizer. Still super-basic.<commit_after> $(function() {

     // uniqify an array
     Array.prototype.unique =
         function() {
             var a = [];
             var l = this.length;
             for(var i=0; i<l; i++) {
                 for(var j=i+1; j<l; j++) {
                     // If this[i] is found later in the array
                     if (this[i] === this[j])
                         j = ++i;
                 }
                 a.push(this[i]);
             }
             return a;
         };

     debug = function(line) {
         $('#scratch').val($('#scratch').val() + line + '\n');
     }
     $('#scratch').val('');

     $.ajax({
         url: "trace.txt",
         context: document.body,
         dataType: "text",
         success: function(text) {

             lines = text.split('\n')

             // Parse lines starting with "Evaluating" or "Realizing"
             evals = lines.filter(function(line) {
                 return line.substring(0, 3) == "###";
             })

             // Find lines of interest
             evals = evals.map(function(line) {
                 var pieces = line.split(' ');
                 var name = pieces[2].replace('.', '_');
                 var coords = pieces.slice(4);

                 //debug(pieces[1] + ', ' + name + ', ' + coords[0] + '**' + coords[1]);

                 coords = coords.map(function(l) {return parseInt(l);});

                 return {type:pieces[1], name:name, coords:coords};

             })

             // Detect unique functions
             var functions = evals.map(function(e) {return e.name;}).unique();

             // Make the canvas for each
             surfaces = {};
             functions.map(function(f) {
                 // Detect dimensionality (Assume 2 for now)

                 // Detect bounds (Assume 100 for now)

                 // Make a canvas
                 $('#main').append('<canvas id="' + f + '" width=300 height=300></canvas>');

                 // And a context
                 var surface = $('#' + f)[0].getContext("2d");
                 surface.strokeStyle = "rgba(0, 0, 0, 1)";
                 surface.strokeRect(0, 0, 300, 300);
                 surface.lineWidth = 2;
                 surface.lineJoin = 'miter';
                 surfaces[f] = surface;
             });

             // Make the function that will draw our events
             drawEvent = function(event) {

                 if (event.type == "Evaluating" || event.type == "Initializing") {
                     surfaces[event.name].fillStyle = "rgba(0, 0, 200, 1)";
                     surfaces[event.name].fillRect(event.coords[0]*7+1, event.coords[1]*7+1, 5, 5);
                 } else if (event.type == "Modifying") {
                     surfaces[event.name].fillStyle = "rgba(0, 200, 0, 1)";
                     surfaces[event.name].fillRect(event.coords[0]*7+1, event.coords[1]*7+1, 5, 5);
                 } else if (event.type == "Realizing") {
                     //surfaces[event.name].fillStyle = "rgba(255, 255, 255, 1)";
                     //surfaces[event.name].fillRect(1, 1, 298, 298);
                     surfaces[event.name].strokeStyle = "rgba(0, 0, 0, 1)";
                     surfaces[event.name].strokeRect(event.coords[0]*7, event.coords[2]*7, event.coords[1]*7, event.coords[3]*7);
                 } else if (event.type == "Discarding") {
                     //debug(event.coords);
                     for (var y = event.coords[2]; y < event.coords[2] + event.coords[3]; y++) {
                         for (var x = event.coords[0]; x < event.coords[0] + event.coords[1]; x++) {
                             surfaces[event.name].strokeStyle = "rgba(255, 255, 255, 1)";
                             surfaces[event.name].strokeRect(x*7+1, y*7+1, 5, 5);
                         }
                     }
                     surfaces[event.name].strokeRect(event.coords[0]*7, event.coords[2]*7, event.coords[1]*7, event.coords[3]*7);
                 }
             };

             var count = 0;

             loop = function() {
                 if (evals.length < count) return;
                 drawEvent(evals[count]);
                 count++;
                 t = setTimeout("loop()", 1);
             }

             loop();

         }
     });
 });<|endoftext|>


<commit_before>'use strict';

function duel(){
    if(!can_duel){
        return;
    }

    can_duel = false;
    var healths = [
      players[0]['health-current'],
      players[1]['health-current'],
    ];
    var manas = [
      players[0]['mana-current'],
      players[1]['mana-current'],
    ];
    var output = '

Player 0 goes first!';
    var turn_counter = 0;

    while(healths[0] > 0
      && healths[1] > 0
      && turn_counter < 100){
        for(var player in players){
            if(random_number(100) < players[player]['health-regen%']
              && healths[player] < players[player]['health']){
                healths[player] = Math.min(
                  players[player]['health'],
                  healths[player] + 1
                );

                output += '
Player '
                  + player
                  + ' regenerated 1 health. '
                  + healths[player]
                  + '/'
                  + players[player]['health'];
            }

            if(healths[player] <= 0){
                output += '
Player '
                  + player
                  + ' lost the duel.'

                continue;
            }

            if(random_number(100) < players[player]['mana-regen%']
              && manas[player] < players[player]['mana']){
                manas[player] = Math.min(
                  players[player]['mana'],
                  manas[player] + 1
                );

                output += '
Player '
                  + player
                  + ' replenished 1 mana. '
                  + manas[player]
                  + '/'
                  + players[player]['mana'];
            }

            if(random_number(100) <= players[player]['hit%']
              && random_number(100) >= players[1 - player]['dodge%']){
                var damage = Math.max(0, players[player]['damage'] - players[1 - player]['defense']);

                healths[1 - player] -= damage;

                output += '
Player '
                  + player
                  + ' hits Player '
                  + (1 - player)
                  + ' for '
                  + damage
                  + ' damage. '
                  + healths[1 - player]
                  + '/'
                  + players[1 - player]['health'];

                if(players[1 - player]['reflect'] > 0){
                    damage = Math.max(0, players[1 - player]['reflect'] - players[1 - player]['defense']);

                    healths[player] -= damage;

                    output += '
Player '
                      + (1 - player)
                      + ' reflects '
                      + damage
                      + ' damage to Player '
                      + player
                      + '. '
                      + healths[player]
                      + '/'
                      + players[player]['health'];
                }

            }else{
                output += '
Player '
                  + player
                  + ' missed.';
            }
        }

        turn_counter += 1;
    }

    if(turn_counter > 99){
        output += '
Tie!';
    }

    output += '
';

    document.getElementById('duel').innerHTML = output;
}

function load(){
    var imported = window.prompt(
      'Input JSON for both players:',
      ''
    );
    if(imported == null
      || imported.length === 0){
        return;
    }
    players = JSON.parse(imported);

    setmode(0);
}

function random_number(i){
    return Math.floor(Math.random() * i);
}

function reset(skip){
    if(!skip
      && !window.confirm('Reset?')){
        return;
    }

    for(var player in players){
        for(var stat in stats){
            players[player][stat] = stats[stat];
        }
    }

    setmode(0);
}

function save(){
    window.prompt(
      'Save this String:',
      JSON.stringify(players)
    );
}

function setmode(newmode){
    mode = newmode;

    var output = '';

    if(mode > 0){
        for(var player in players){
            for(var stat in stats){
                players[player][stat] = parseInt(
                  document.getElementById(player + '-' + stat).value,
                  10
                );
            }
        }

        can_duel = true;

        output += 'Edit Players'
          + ' | Save'
          + '

Player 0
';

        for(var stat in stats){
            output += '
<input readonly value='
              + players[0][stat]
              + '> '
              + stat;
        }

        output += '

Player 1
';

        for(var stat in stats){
            output += '
<input readonly value='
              + players[1][stat]
              + '> '
              + stat;
        }

        output += '

Duel
';

    }else{
        output += 'Duel

Player 0
';

        for(var stat in stats){
            output += '
<input id="0-'
              + stat
              + '" value='
              + players[0][stat]
              + '> '
              + stat;
        }

        output += '

Player 1
';

        for(var stat in stats){
            output += '
<input id="1-'
              + stat
              + '" value='
              + players[1][stat]
              + '> '
              + stat;
        }

        output += '

Reset'
          + ' | Load';
    }

    document.body.innerHTML = output;
}

var can_duel = false;
var mode = 0;
var players = [
  {},
  {},
];
var stats = {
  'damage': 1,
  'defense': 0,
  'dodge%': 0,
  'health': 10,
  'health-current': 10,
  'health-regen%': 0,
  'hit%': 100,
  'mana': 5,
  'mana-current': 5,
  'mana-regen%': 0,
  'reflect': 0,
};

window.onload = function(e){
    init_input(
      {
        13: {
          'todo': function(){
              if(mode > 0){
                  duel();

              }else{
                  setmode(1);
              }
          },
        },
        27: {
          'todo': function(){
              if(mode > 0){
                  setmode(0);
              }
          },
        },
      }
    );
    reset(true);
    setmode(0);
};
<commit_msg>namespaced common input.js<commit_after>'use strict';

function duel(){
    if(!can_duel){
        return;
    }

    can_duel = false;
    var healths = [
      players[0]['health-current'],
      players[1]['health-current'],
    ];
    var manas = [
      players[0]['mana-current'],
      players[1]['mana-current'],
    ];
    var output = '

Player 0 goes first!';
    var turn_counter = 0;

    while(healths[0] > 0
      && healths[1] > 0
      && turn_counter < 100){
        for(var player in players){
            if(random_number(100) < players[player]['health-regen%']
              && healths[player] < players[player]['health']){
                healths[player] = Math.min(
                  players[player]['health'],
                  healths[player] + 1
                );

                output += '
Player '
                  + player
                  + ' regenerated 1 health. '
                  + healths[player]
                  + '/'
                  + players[player]['health'];
            }

            if(healths[player] <= 0){
                output += '
Player '
                  + player
                  + ' lost the duel.'

                continue;
            }

            if(random_number(100) < players[player]['mana-regen%']
              && manas[player] < players[player]['mana']){
                manas[player] = Math.min(
                  players[player]['mana'],
                  manas[player] + 1
                );

                output += '
Player '
                  + player
                  + ' replenished 1 mana. '
                  + manas[player]
                  + '/'
                  + players[player]['mana'];
            }

            if(random_number(100) <= players[player]['hit%']
              && random_number(100) >= players[1 - player]['dodge%']){
                var damage = Math.max(0, players[player]['damage'] - players[1 - player]['defense']);

                healths[1 - player] -= damage;

                output += '
Player '
                  + player
                  + ' hits Player '
                  + (1 - player)
                  + ' for '
                  + damage
                  + ' damage. '
                  + healths[1 - player]
                  + '/'
                  + players[1 - player]['health'];

                if(players[1 - player]['reflect'] > 0){
                    damage = Math.max(0, players[1 - player]['reflect'] - players[1 - player]['defense']);

                    healths[player] -= damage;

                    output += '
Player '
                      + (1 - player)
                      + ' reflects '
                      + damage
                      + ' damage to Player '
                      + player
                      + '. '
                      + healths[player]
                      + '/'
                      + players[player]['health'];
                }

            }else{
                output += '
Player '
                  + player
                  + ' missed.';
            }
        }

        turn_counter += 1;
    }

    if(turn_counter > 99){
        output += '
Tie!';
    }

    output += '
';

    document.getElementById('duel').innerHTML = output;
}

function load(){
    var imported = window.prompt(
      'Input JSON for both players:',
      ''
    );
    if(imported == null
      || imported.length === 0){
        return;
    }
    players = JSON.parse(imported);

    setmode(0);
}

function random_number(i){
    return Math.floor(Math.random() * i);
}

function reset(skip){
    if(!skip
      && !window.confirm('Reset?')){
        return;
    }

    for(var player in players){
        for(var stat in stats){
            players[player][stat] = stats[stat];
        }
    }

    setmode(0);
}

function save(){
    window.prompt(
      'Save this String:',
      JSON.stringify(players)
    );
}

function setmode(newmode){
    mode = newmode;

    var output = '';

    if(mode > 0){
        for(var player in players){
            for(var stat in stats){
                players[player][stat] = parseInt(
                  document.getElementById(player + '-' + stat).value,
                  10
                );
            }
        }

        can_duel = true;

        output += 'Edit Players'
          + ' | Save'
          + '

Player 0
';

        for(var stat in stats){
            output += '
<input readonly value='
              + players[0][stat]
              + '> '
              + stat;
        }

        output += '

Player 1
';

        for(var stat in stats){
            output += '
<input readonly value='
              + players[1][stat]
              + '> '
              + stat;
        }

        output += '

Duel
';

    }else{
        output += 'Duel

Player 0
';

        for(var stat in stats){
            output += '
<input id="0-'
              + stat
              + '" value='
              + players[0][stat]
              + '> '
              + stat;
        }

        output += '

Player 1
';

        for(var stat in stats){
            output += '
<input id="1-'
              + stat
              + '" value='
              + players[1][stat]
              + '> '
              + stat;
        }

        output += '

Reset'
          + ' | Load';
    }

    document.body.innerHTML = output;
}

var can_duel = false;
var mode = 0;
var players = [
  {},
  {},
];
var stats = {
  'damage': 1,
  'defense': 0,
  'dodge%': 0,
  'health': 10,
  'health-current': 10,
  'health-regen%': 0,
  'hit%': 100,
  'mana': 5,
  'mana-current': 5,
  'mana-regen%': 0,
  'reflect': 0,
};

window.onload = function(e){
    input_init(
      {
        13: {
          'todo': function(){
              if(mode > 0){
                  duel();

              }else{
                  setmode(1);
              }
          },
        },
        27: {
          'todo': function(){
              if(mode > 0){
                  setmode(0);
              }
          },
        },
      }
    );
    reset(true);
    setmode(0);
};
<|endoftext|>


<commit_before>import _ from 'underscore';
import $ from 'jquery';
import { Terminal } from 'xterm';
import * as fit from 'xterm/lib/addons/fit/fit';
import { canScrollUp, canScrollDown } from '~/lib/utils/dom_utils';

const SCROLL_MARGIN = 5;

Terminal.applyAddon(fit);

export default class GLTerminal {
  constructor(element, options = {}) {
    this.options = Object.assign(
      {},
      {
        cursorBlink: true,
        screenKeys: true,
      },
      options,
    );

    this.container = element;
    this.onDispose = [];

    this.setSocketUrl();
    this.createTerminal();

    $(window)
      .off('resize.terminal')
      .on('resize.terminal', () => {
        this.terminal.fit();
      });
  }

  setSocketUrl() {
    const { protocol, hostname, port } = window.location;
    const wsProtocol = protocol === 'https:' ? 'wss://' : 'ws://';
    const path = this.container.dataset.projectPath;

    this.socketUrl = `${wsProtocol}${hostname}:${port}${path}`;
  }

  createTerminal() {
    this.terminal = new Terminal(this.options);

    this.socket = new WebSocket(this.socketUrl, ['terminal.gitlab.com']);
    this.socket.binaryType = 'arraybuffer';

    this.terminal.open(this.container);
    this.terminal.fit();
    this.terminal.focus();

    this.socket.onopen = () => {
      this.runTerminal();
    };
    this.socket.onerror = () => {
      this.handleSocketFailure();
    };
  }

  runTerminal() {
    const decoder = new TextDecoder('utf-8');
    const encoder = new TextEncoder('utf-8');

    this.terminal.on('data', data => {
      this.socket.send(encoder.encode(data));
    });

    this.socket.addEventListener('message', ev => {
      this.terminal.write(decoder.decode(ev.data));
    });

    this.isTerminalInitialized = true;
    this.terminal.fit();
  }

  handleSocketFailure() {
    this.terminal.write('\r\nConnection failure');
  }

  addScrollListener(onScrollLimit) {
    const viewport = this.container.querySelector('.xterm-viewport');
    const listener = _.throttle(() => {
      onScrollLimit({
        canScrollUp: canScrollUp(viewport, SCROLL_MARGIN),
        canScrollDown: canScrollDown(viewport, SCROLL_MARGIN),
      });
    });

    this.onDispose.push(() => viewport.removeEventListener('scroll', listener));
    viewport.addEventListener('scroll', listener);

    // don't forget to initialize value before scroll!
    listener({ target: viewport });
  }

  disable() {
    this.terminal.setOption('cursorBlink', false);
    this.terminal.setOption('theme', { foreground: '#707070' });
    this.terminal.setOption('disableStdin', true);
    this.socket.close();
  }

  dispose() {
    this.terminal.off('data');
    this.terminal.dispose();
    this.socket.close();

    this.onDispose.forEach(fn => fn());
    this.onDispose.length = 0;
  }

  scrollToTop() {
    this.terminal.scrollToTop();
  }

  scrollToBottom() {
    this.terminal.scrollToBottom();
  }

  fit() {
    this.terminal.fit();
  }
}
<commit_msg>web-terminal: Add support for URL to Link parsing<commit_after>import _ from 'underscore';
import $ from 'jquery';
import { Terminal } from 'xterm';
import * as fit from 'xterm/lib/addons/fit/fit';
import * as webLinks from 'xterm/lib/addons/webLinks/webLinks';
import { canScrollUp, canScrollDown } from '~/lib/utils/dom_utils';

const SCROLL_MARGIN = 5;

Terminal.applyAddon(fit);
Terminal.applyAddon(webLinks);

export default class GLTerminal {
  constructor(element, options = {}) {
    this.options = Object.assign(
      {},
      {
        cursorBlink: true,
        screenKeys: true,
      },
      options,
    );

    this.container = element;
    this.onDispose = [];

    this.setSocketUrl();
    this.createTerminal();

    $(window)
      .off('resize.terminal')
      .on('resize.terminal', () => {
        this.terminal.fit();
      });
  }

  setSocketUrl() {
    const { protocol, hostname, port } = window.location;
    const wsProtocol = protocol === 'https:' ? 'wss://' : 'ws://';
    const path = this.container.dataset.projectPath;

    this.socketUrl = `${wsProtocol}${hostname}:${port}${path}`;
  }

  createTerminal() {
    this.terminal = new Terminal(this.options);

    this.socket = new WebSocket(this.socketUrl, ['terminal.gitlab.com']);
    this.socket.binaryType = 'arraybuffer';

    this.terminal.open(this.container);
    this.terminal.fit();
    this.terminal.webLinksInit();
    this.terminal.focus();

    this.socket.onopen = () => {
      this.runTerminal();
    };
    this.socket.onerror = () => {
      this.handleSocketFailure();
    };
  }

  runTerminal() {
    const decoder = new TextDecoder('utf-8');
    const encoder = new TextEncoder('utf-8');

    this.terminal.on('data', data => {
      this.socket.send(encoder.encode(data));
    });

    this.socket.addEventListener('message', ev => {
      this.terminal.write(decoder.decode(ev.data));
    });

    this.isTerminalInitialized = true;
    this.terminal.fit();
  }

  handleSocketFailure() {
    this.terminal.write('\r\nConnection failure');
  }

  addScrollListener(onScrollLimit) {
    const viewport = this.container.querySelector('.xterm-viewport');
    const listener = _.throttle(() => {
      onScrollLimit({
        canScrollUp: canScrollUp(viewport, SCROLL_MARGIN),
        canScrollDown: canScrollDown(viewport, SCROLL_MARGIN),
      });
    });

    this.onDispose.push(() => viewport.removeEventListener('scroll', listener));
    viewport.addEventListener('scroll', listener);

    // don't forget to initialize value before scroll!
    listener({ target: viewport });
  }

  disable() {
    this.terminal.setOption('cursorBlink', false);
    this.terminal.setOption('theme', { foreground: '#707070' });
    this.terminal.setOption('disableStdin', true);
    this.socket.close();
  }

  dispose() {
    this.terminal.off('data');
    this.terminal.dispose();
    this.socket.close();

    this.onDispose.forEach(fn => fn());
    this.onDispose.length = 0;
  }

  scrollToTop() {
    this.terminal.scrollToTop();
  }

  scrollToBottom() {
    this.terminal.scrollToBottom();
  }

  fit() {
    this.terminal.fit();
  }
}
<|endoftext|>


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

    var configs, jar, xlock,

    // self overriding fnc
    progress, showErrors;

    function poverride(key, fnc) {
        Form.prototype[key] = fnc;
    }

    function forEach(obj, callback) {
        Object.keys(obj).forEach(function (k) {
            callback(obj[k], k, obj);
        });
    }

    progress = function () {
        var prg = $("

"),
        timer,
        width;

        $(document.body).append(prg);

        progress = function (display) {
            prg.css({
                width: (display ? 0 : 100) + "%",
                height: (display ? 3 : 0) + "px",
                borderWidth: (display ? 1 : 0) + "px"
            });

            if (display) {
                width = 5;
                clearInterval(timer);

                timer = setInterval(function () {
                    width += 5;

                    if (width < 100) {
                        prg.css({ width: width + "%" });
                    } else {
                        clearInterval(timer);
                    }
                }, 500);
            } else {
                clearInterval(timer);

                setTimeout(function () {
                    prg.css({ width: 0 });
                }, 1000);
            }
        };

        poverride("progress", progress);
        progress.apply(undefined, arguments);
    };

    showErrors = function () {
        var elm = $(".j-error", jar),
        frg = $(document.createDocumentFragment()),
        errorFrg = $($.parseHTML("

")),
        defaultError = "Invalid request. Please refresh the page or try again later.",
        timer;

        showErrors = function (errors) {
            if (configs.hideErrors) {
                clearTimeout(timer);

                timer = setTimeout(function () {
                    elm.addClass("hide");
                }, 10000);
            }

            if ($.isArray(errors)) {
                errors.forEach(function (v) {
                    frg.append(errorFrg.clone().append(v));
                });
            } else {
                frg.append(errorFrg.clone().append(defaultError));
            }

            elm.children().remove();
            elm.append(frg).removeClass("hide");
            scrollTo(0, elm[0].getBoundingClientRect().top - document.body.getBoundingClientRect().top);
        };

        poverride("showErrors", showErrors);
        showErrors.apply(undefined, arguments);
    };

    function isValidFile(file, atypes, limit) {
        var rx = new RegExp("^" + atypes.join("$|^") + "$", "i");

        if (!(rx.test(file.name.split(".").pop()))) {
            showErrors(["The filetype you are attempting to upload is not allowed."]);
            return false;
        }

        if (!file.size || file.size > limit) {
            showErrors(["The uploaded file exceeds the maximum upload file size limit."]);
            return false;
        }

        return true;
    }

    function submit(xconfigs) {
        if (xlock) {
            return;
        }

        var data = {
            method: "POST",
            url: xconfigs.url || location.href,
            error: function () {
                if (xconfigs.load) {
                    xconfigs.load();
                }

                showErrors();
                progress(false);
                xlock = false;
            },
            data: xconfigs.data
        };

        if (xconfigs.upload) {
            data.processData = false;
            data.contentType = false;
        }

        xlock = true;
        $(".j-error", jar).addClass("hide");
        progress(true);

        $.ajax(data).done(function (response, status) {
            if (xconfigs.load) {
                xconfigs.load();
            }

            progress(false);

            if (status !== "success" || !response.status || response.errors) {
                showErrors(response.errors);
                xlock = false;
                return;
            }

            xconfigs.success(response.data);
            xlock = false;
        });
    }

    function init(uconfigs) {
        $ = jQuery;
        configs = {
            hideErrors: true
        };

        if (uconfigs) {
            forEach(uconfigs, function (v, k) {
                if (configs.hasOwnProperty(k)) {
                    configs[k] = v;
                }
            });
        }

        jar = $("#j-ar");

        if (configs.hideErrors) {
            $(".j-error", jar).on("click", function () {
                $(this).addClass("hide");
            });
        }
    }

    function Form() {}

    Form.prototype = {
        init: init,
        submit: submit,
        isValidFile: isValidFile,
        showErrors: showErrors,
        progress: progress,

        get lock() { return xlock; },
        set lock(status) { xlock = status; }
    };

    function GForm() {
        if (this instanceof GForm) {
            return new Form();
        }

        return new GForm();
    }

    window.GForm = GForm;
}());<commit_msg>Updated scrollto<commit_after>(function ($) {
    "use strict";

    var configs, jar, xlock,

    // self overriding fnc
    progress, showErrors;

    function poverride(key, fnc) {
        Form.prototype[key] = fnc;
    }

    function forEach(obj, callback) {
        Object.keys(obj).forEach(function (k) {
            callback(obj[k], k, obj);
        });
    }

    progress = function () {
        var prg = $("

"),
        timer,
        width;

        $(document.body).append(prg);

        progress = function (display) {
            prg.css({
                width: (display ? 0 : 100) + "%",
                height: (display ? 3 : 0) + "px",
                borderWidth: (display ? 1 : 0) + "px"
            });

            if (display) {
                width = 5;
                clearInterval(timer);

                timer = setInterval(function () {
                    width += 5;

                    if (width < 100) {
                        prg.css({ width: width + "%" });
                    } else {
                        clearInterval(timer);
                    }
                }, 500);
            } else {
                clearInterval(timer);

                setTimeout(function () {
                    prg.css({ width: 0 });
                }, 1000);
            }
        };

        poverride("progress", progress);
        progress.apply(undefined, arguments);
    };

    showErrors = function () {
        var elm = $(".j-error", jar),
        frg = $(document.createDocumentFragment()),
        errorFrg = $($.parseHTML("

")),
        defaultError = "Invalid request. Please refresh the page or try again later.",
        timer;

        showErrors = function (errors) {
            if (configs.hideErrors) {
                clearTimeout(timer);

                timer = setTimeout(function () {
                    elm.addClass("hide");
                }, 10000);
            }

            if ($.isArray(errors)) {
                errors.forEach(function (v) {
                    frg.append(errorFrg.clone().append(v));
                });
            } else {
                frg.append(errorFrg.clone().append(defaultError));
            }

            elm.children().remove();
            elm.append(frg).removeClass("hide");
            $("html, body").animate({ scrollTop: elm.offset().top });
        };

        poverride("showErrors", showErrors);
        showErrors.apply(undefined, arguments);
    };

    function isValidFile(file, atypes, limit) {
        var rx = new RegExp("^" + atypes.join("$|^") + "$", "i");

        if (!rx.test(file.name.split(".").pop())) {
            showErrors(["The filetype you are attempting to upload is not allowed."]);
            return false;
        }

        if (!file.size || file.size > limit) {
            showErrors(["The uploaded file exceeds the maximum upload file size limit."]);
            return false;
        }

        return true;
    }

    function submit(xconfigs) {
        if (xlock) {
            return;
        }

        var data = {
            method: "POST",
            url: xconfigs.url || location.href,
            error: function () {
                if (xconfigs.load) {
                    xconfigs.load();
                }

                showErrors();
                progress(false);
                xlock = false;
            },
            data: xconfigs.data
        };

        if (xconfigs.upload) {
            data.processData = false;
            data.contentType = false;
        }

        xlock = true;
        $(".j-error", jar).addClass("hide");
        progress(true);

        $.ajax(data).done(function (response, status) {
            if (xconfigs.load) {
                xconfigs.load();
            }

            progress(false);

            if (status !== "success" || !response.status || response.errors) {
                showErrors(response.errors);
                xlock = false;
                return;
            }

            xconfigs.success(response.data);
            xlock = false;
        });
    }

    function init(uconfigs) {
        $ = jQuery;
        configs = {
            hideErrors: true
        };

        if (uconfigs) {
            forEach(uconfigs, function (v, k) {
                if (configs.hasOwnProperty(k)) {
                    configs[k] = v;
                }
            });
        }

        jar = $("#j-ar");

        if (configs.hideErrors) {
            $(".j-error", jar).on("click", function () {
                $(this).addClass("hide");
            });
        }
    }

    function Form() {}

    Form.prototype = {
        init: init,
        submit: submit,
        isValidFile: isValidFile,
        showErrors: showErrors,
        progress: progress,

        get lock() { return xlock; },
        set lock(status) { xlock = status; }
    };

    function GForm() {
        if (this instanceof GForm) {
            return new Form();
        }

        return new GForm();
    }

    window.GForm = GForm;
}());<|endoftext|>


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

  if($("#dashboard-timeline").length) {
    $.ajax({
        type: 'GET',
        url: '/info/timeline_events',
        dataType: 'json',
        contentType: 'application/json',
        success: function (json) {
            createTimeline(json);
        }
     });
  }

  function createTimeline(source){
    $("#loading").hide();
    //get the events.json format from https://github.com/VeriteCo/TimelineJS#file-formats
    var timeline_dates = source.timeline.date;
    var start_index = 1;
    var target_date = new Date();
    // console.log(target_date);
    for(x in timeline_dates) {
      var slide_date = new Date( timeline_dates[x].startDate );
      console.log(timeline_dates[x]);
      if( slide_date < target_date ) {
        start_index++
        console.log(start_index);
        console.log(x);
      };

      if (start_index > (x+1)) {
        start_index = start_index - 1;
      };
    }

      createStoryJS({
        type: 'timeline',
        width: '100%',
        height: '600',
        hash_bookmark: 'true',
        source: source,
        embed_id: 'time_line',
        start_at_slide: start_index
      });
  }
});
<commit_msg>Working timeline when past end of course<commit_after>$(document).ready(function(){

  if($("#dashboard-timeline").length) {
    $.ajax({
        type: 'GET',
        url: '/info/timeline_events',
        dataType: 'json',
        contentType: 'application/json',
        success: function (json) {
            createTimeline(json);
        }
     });
  }

  function createTimeline(source){
    $("#loading").hide();
    //get the events.json format from https://github.com/VeriteCo/TimelineJS#file-formats
    var timeline_dates = source.timeline.date;
    var start_index = 1;
    var target_date = new Date();
    for(x in timeline_dates) {
      var slide_date = new Date( timeline_dates[x].startDate );
      if( slide_date < target_date ) {
        start_index++
      };

      if (start_index > (x+1)) {
        start_index = start_index - 1;
      };
    }

      createStoryJS({
        type: 'timeline',
        width: '100%',
        height: '600',
        hash_bookmark: 'true',
        source: source,
        embed_id: 'time_line',
        start_at_slide: start_index
      });
  }
});
<|endoftext|>


<commit_before>function addTeam () {
	var parent = document.getElementById('team_form');
	var previous = parseInt(parent.elements[parent.length - 1].name);
	var current = previous + 1;

	var breakLine = document.createElement('br');
	var breakLine1 = document.createElement('br');
	var space = document.createElement('span');
	space.innerHTML = " ";

	var teamText = document.createElement('input');
	teamText.setAttribute('type', 'text');
	teamText.setAttribute('name', current);
	teamText.setAttribute('placeholder', 'Team ' + current);
	teamText.setAttribute('size', '25');
	teamText.setAttribute('maxlength', '25');

	if ((previous % 2) == 0) {
		parent.appendChild(breakLine);
		parent.appendChild(breakLine1);
	}
	else {
		parent.appendChild(space);
	}
	parent.appendChild(teamText);
}

function startGame () {
	document.getElementById('setup_div').style.display = "none";
	document.getElementById('board_div').style.display = "block";
}

function restartGame () {
	document.getElementById('board_div').style.display = "none";
	document.getElementById('setup_div').style.display = "block";
}<commit_msg>Array of questions<commit_after>var questions;	// Array that will contain the parsed JSON

function addTeam () {
	var parent = document.getElementById('team_form');
	var previous = parseInt(parent.elements[parent.length - 1].name);
	var current = previous + 1;

	var breakLine = document.createElement('br');
	var breakLine1 = document.createElement('br');
	var space = document.createElement('span');
	space.innerHTML = " ";

	var teamText = document.createElement('input');
	teamText.setAttribute('type', 'text');
	teamText.setAttribute('name', current);
	teamText.setAttribute('placeholder', 'Team ' + current);
	teamText.setAttribute('size', '25');
	teamText.setAttribute('maxlength', '25');

	if ((previous % 2) == 0) {
		parent.appendChild(breakLine);
		parent.appendChild(breakLine1);
	}
	else {
		parent.appendChild(space);
	}
	parent.appendChild(teamText);
}

function startGame () {
	questions = JSON.parse(document.getElementById('hidden_json').innerHTML);

	document.getElementById('setup_div').style.display = "none";
	document.getElementById('board_div').style.display = "block";
}

function restartGame () {
	document.getElementById('board_div').style.display = "none";
	document.getElementById('setup_div').style.display = "block";
}<|endoftext|>


<commit_before>/*
Copyright 2016 First People's Cultural Council

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { Component, PropTypes } from 'react';
import Immutable, { List } from 'immutable';

import provide from 'react-redux-provide';
import selectn from 'selectn';
import classNames from 'classnames';

import ProviderHelpers from 'common/ProviderHelpers';

import GridList from 'material-ui/lib/grid-list/grid-list';
import GridTile from 'material-ui/lib/grid-list/grid-tile';
import CircularProgress from 'material-ui/lib/circular-progress';
import Paper from 'material-ui/lib/paper';
import RaisedButton from 'material-ui/lib/raised-button';

import TextField from 'material-ui/lib/text-field';

import IconMenu from 'material-ui/lib/menus/icon-menu';
import MenuItem from 'material-ui/lib/menus/menu-item';

/**
* Explore Archive page shows all the families in the archive
*/
@provide
export default class PageHome extends Component {

  static propTypes = {
    properties: PropTypes.object.isRequired,
    windowPath: PropTypes.string.isRequired,
    pushWindowPath: PropTypes.func.isRequired
  };

  /*static contextTypes = {
      muiTheme: React.PropTypes.object.isRequired
  };*/

  constructor(props, context) {
    super(props, context);

    this.state = {
      mapVisible: false,
      pagePath: '/' + this.props.properties.domain + '/sections/Site/Resources/',
      dialectsPath: '/' + this.props.properties.domain + '/sections/',
    };

    ['_onNavigateRequest'].forEach( (method => this[method] = this[method].bind(this)) );
  }

  _onNavigateRequest(path) {
    this.props.pushWindowPath(path);
  }

  render() {

    const introMarginStyle = {
      position: 'relative',
      minHeight: '325px',
      backgroundColor: 'rgb(170, 18, 37)',
      overflow: 'hidden',
      padding: '50px 0'
    }

    return

                        <RaisedButton label="Enter FirstVoices" onTouchTap={(e, v) => this._onNavigateRequest('/home')} style={{textAlign: 'center'}} />

We gratefully acknowledge the following supporters:

;
  }
}<commit_msg>FV-137: Link to legacy.firstvoices.com<commit_after>/*
Copyright 2016 First People's Cultural Council

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { Component, PropTypes } from 'react';
import Immutable, { List } from 'immutable';

import provide from 'react-redux-provide';
import selectn from 'selectn';
import classNames from 'classnames';

import ProviderHelpers from 'common/ProviderHelpers';

import GridList from 'material-ui/lib/grid-list/grid-list';
import GridTile from 'material-ui/lib/grid-list/grid-tile';
import CircularProgress from 'material-ui/lib/circular-progress';
import Paper from 'material-ui/lib/paper';
import RaisedButton from 'material-ui/lib/raised-button';

import TextField from 'material-ui/lib/text-field';

import IconMenu from 'material-ui/lib/menus/icon-menu';
import MenuItem from 'material-ui/lib/menus/menu-item';

/**
* Explore Archive page shows all the families in the archive
*/
@provide
export default class PageHome extends Component {

  static propTypes = {
    properties: PropTypes.object.isRequired,
    windowPath: PropTypes.string.isRequired,
    pushWindowPath: PropTypes.func.isRequired
  };

  /*static contextTypes = {
      muiTheme: React.PropTypes.object.isRequired
  };*/

  constructor(props, context) {
    super(props, context);

    this.state = {
      mapVisible: false,
      pagePath: '/' + this.props.properties.domain + '/sections/Site/Resources/',
      dialectsPath: '/' + this.props.properties.domain + '/sections/',
    };

    ['_onNavigateRequest'].forEach( (method => this[method] = this[method].bind(this)) );
  }

  _onNavigateRequest(path) {
    this.props.pushWindowPath(path);
  }

  render() {

    const introMarginStyle = {
      position: 'relative',
      minHeight: '325px',
      backgroundColor: 'rgb(170, 18, 37)',
      overflow: 'hidden',
      padding: '50px 0'
    }

    return

                        <RaisedButton label="Legacy Site" style={{textAlign: 'center', marginRight:'14px'}} />
                        <RaisedButton label="Enter FirstVoices" onTouchTap={(e, v) => this._onNavigateRequest('/home')} style={{textAlign: 'center'}} />

We gratefully acknowledge the following supporters:

;
  }
}<|endoftext|>


<commit_before>
/* Game namespace */
var game = {

    // an object where to store game information
    data : {
    },

    // Run on page load.
    onload : function () {
        // Initialize the video.
        if (!me.video.init(160, 144, { wrapper : 'screen', scale : 3 })) {
            alert('Your browser does not support HTML5 canvas.');
            return;
        }

        // Initialize the audio.
        me.audio.init('mp3,ogg');

        // Set a callback to run when loading is complete.
        me.loader.onload = this.loaded.bind(this);

        // Load the resources.
        me.loader.preload(game.resources);

        // Initialize melonJS and display a loading screen.
        me.state.change(me.state.LOADING);
    },

    // Run on game resources loaded.
    loaded : function () {
        me.state.set(me.state.MENU, new game.TitleScreen());
        me.state.set(me.state.PLAY, new game.PlayScreen());

        // add our player entity in the entity pool
        me.pool.register('player', game.PlayerEntity);

        // enable the keyboard
        me.input.bindKey(me.input.KEY.LEFT, 'left');
        me.input.bindKey(me.input.KEY.RIGHT, 'right');
        me.input.bindKey(me.input.KEY.UP, 'up', true);

        me.sys.gravity = 0.7;

        // Start the game.
        me.state.change(me.state.PLAY);
    }
};
<commit_msg>handle HiDPI screens<commit_after>
/* Game namespace */
var game = {

    // an object where to store game information
    data : {
    },

    // Run on page load.
    onload : function () {
        // Initialize the video.
        var scale = 3 * window.devicePixelRatio;
        if (!me.video.init(160, 144, { wrapper : 'screen', scale : scale })) {
            alert('Your browser does not support HTML5 canvas.');
            return;
        }

        // Initialize the audio.
        me.audio.init('mp3,ogg');

        // Set a callback to run when loading is complete.
        me.loader.onload = this.loaded.bind(this);

        // Load the resources.
        me.loader.preload(game.resources);

        // Initialize melonJS and display a loading screen.
        me.state.change(me.state.LOADING);
    },

    // Run on game resources loaded.
    loaded : function () {
        me.state.set(me.state.MENU, new game.TitleScreen());
        me.state.set(me.state.PLAY, new game.PlayScreen());

        // add our player entity in the entity pool
        me.pool.register('player', game.PlayerEntity);

        // enable the keyboard
        me.input.bindKey(me.input.KEY.LEFT, 'left');
        me.input.bindKey(me.input.KEY.RIGHT, 'right');
        me.input.bindKey(me.input.KEY.UP, 'up', true);

        me.sys.gravity = 0.7;

        // Start the game.
        me.state.change(me.state.PLAY);
    }
};
<|endoftext|>


<commit_before>var properties = {
  ball: {
    angle: -60,
    startX: 65,
    speed: 300,
  },
  board: {
    width: 800,
    height: 400,
  },
  leftPlayer: {
    x: 50,
  },
  rightPlayer: {
    x: 750
  },
};

var graphicAssets = {
  ball: {
    name: 'ball',
    url: 'assets/ball.png'
  },
  player: {
    name: 'player',
    url: 'assets/player.png'
  },
};

var mainState = function(game) {
  this.ballSprite;
  this.leftPlayerSprite;
  this.rightPlayerSprite;
};

mainState.prototype = {
  preload: function () {
    game.load.image(graphicAssets.ball.name, graphicAssets.ball.url);
    game.load.image(graphicAssets.player.name, graphicAssets.player.url);
  },

  create: function () {
    this.initGraphics();
    this.initPhysics();

    game.input.onDown.add(this.startGame, this);
  },

  update: function () {

  },

  initGraphics: function () {
    // this.backgroundGraphics = game.add.graphics(0, 0);
    // this.backgroundGraphics.lineStyle(2, 0xFFFFFF, 1);

    this.ballSprite = game.add.sprite(
      properties.ball.startX,
      game.world.centerY,
      graphicAssets.ball.name
    );
    this.ballSprite.anchor.set(0.5, 0.5);

    this.leftPlayerSprite = game.add.sprite(
      properties.leftPlayer.x,
      game.world.centerY,
      graphicAssets.player.name
    );
    this.leftPlayerSprite.anchor.set(0.5, 0.5);

    this.rightPlayerSprite = game.add.sprite(
      properties.rightPlayer.x,
      game.world.centerY,
      graphicAssets.player.name
    );
    this.rightPlayerSprite.anchor.set(0.5, 0.5);
  },

  initPhysics: function() {
    game.physics.startSystem(Phaser.Physics.ARCADE);
    game.physics.enable(this.ballSprite, Phaser.Physics.ARCADE);

    this.ballSprite.checkWorldBounds = true;
    this.ballSprite.body.collideWorldBounds = true;
    this.ballSprite.body.immovable = true;
    this.ballSprite.body.bounce.set(1);
  },

  startGame: function () {
    game.input.onDown.remove(this.startGame, this);
    game.physics.arcade.velocityFromAngle(
      properties.ball.angle,
      properties.ball.speed,
      this.ballSprite.body.velocity
    );
  },
};

var game = new Phaser.Game(
  properties.board.width,
  properties.board.height,
  Phaser.AUTO,
  'gameDiv'
);

game.state.add('main', mainState);
game.state.start('main');
<commit_msg>Players move with the keyboard<commit_after>var properties = {
  ball: {
    angle: -60,
    startX: 65,
    speed: 300,
  },
  board: {
    width: 800,
    height: 400,
  },
  player: {
    velocity: 400,
  },
  leftPlayer: {
    x: 50,
  },
  rightPlayer: {
    x: 750
  },
};

var graphicAssets = {
  ball: {
    name: 'ball',
    url: 'assets/ball.png'
  },
  player: {
    name: 'player',
    url: 'assets/player.png'
  },
};

var mainState = function(game) {
  this.ballSprite;
  this.leftPlayerSprite;
  this.rightPlayerSprite;
};

mainState.prototype = {
  preload: function () {
    game.load.image(graphicAssets.ball.name, graphicAssets.ball.url);
    game.load.image(graphicAssets.player.name, graphicAssets.player.url);
  },

  create: function () {
    this.initGraphics();
    this.initKeyboard();
    this.initPhysics();

    game.input.onDown.add(this.startGame, this);
  },

  update: function () {
    this.updatePlayer(
      this.leftPlayerSprite,
      this.leftPlayerUpKey,
      this.leftPlayerDownKey
    );
    this.updatePlayer(
      this.rightPlayerSprite,
      this.rightPlayerUpKey,
      this.rightPlayerDownKey
    );
  },

  initKeyboard: function() {
    this.leftPlayerUpKey = game.input.keyboard.addKey(Phaser.Keyboard.W);
    this.leftPlayerDownKey = game.input.keyboard.addKey(Phaser.Keyboard.S);

    this.rightPlayerUpKey = game.input.keyboard.addKey(Phaser.Keyboard.UP);
    this.rightPlayerDownKey = game.input.keyboard.addKey(Phaser.Keyboard.DOWN);
  },

  initGraphics: function () {
    // this.backgroundGraphics = game.add.graphics(0, 0);
    // this.backgroundGraphics.lineStyle(2, 0xFFFFFF, 1);

    this.ballSprite = game.add.sprite(
      properties.ball.startX,
      game.world.centerY,
      graphicAssets.ball.name
    );
    this.ballSprite.anchor.set(0.5, 0.5);

    this.leftPlayerSprite = game.add.sprite(
      properties.leftPlayer.x,
      game.world.centerY,
      graphicAssets.player.name
    );
    this.leftPlayerSprite.anchor.set(0.5, 0.5);

    this.rightPlayerSprite = game.add.sprite(
      properties.rightPlayer.x,
      game.world.centerY,
      graphicAssets.player.name
    );
    this.rightPlayerSprite.anchor.set(0.5, 0.5);
  },

  initPhysics: function() {
    game.physics.startSystem(Phaser.Physics.ARCADE);
    game.physics.enable(this.ballSprite, Phaser.Physics.ARCADE);

    this.ballSprite.checkWorldBounds = true;
    this.ballSprite.body.collideWorldBounds = true;
    this.ballSprite.body.immovable = true;
    this.ballSprite.body.bounce.set(1);

    this.playersGroup = game.add.group();
    this.playersGroup.enableBody = true;
    this.playersGroup.physicsBodyType = Phaser.Physics.ARCADE;

    this.playersGroup.add(this.leftPlayerSprite);
    this.playersGroup.add(this.rightPlayerSprite);

    this.playersGroup.setAll('checkWorldBounds', true);
    this.playersGroup.setAll('body.collideWorldBounds', true);
    this.playersGroup.setAll('body.immovable', true);
  },

  updatePlayer: function(player, upKey, downKey) {
    if(upKey.isDown) {
      player.body.velocity.y = -properties.player.velocity;
    }
    else if(downKey.isDown) {
      player.body.velocity.y = properties.player.velocity;
    } else {
      player.body.velocity.y = 0;
    }
  },

  startGame: function () {
    game.input.onDown.remove(this.startGame, this);
    game.physics.arcade.velocityFromAngle(
      properties.ball.angle,
      properties.ball.speed,
      this.ballSprite.body.velocity
    );
  },
};

var game = new Phaser.Game(
  properties.board.width,
  properties.board.height,
  Phaser.AUTO,
  'gameDiv'
);

game.state.add('main', mainState);
game.state.start('main');
<|endoftext|>


<commit_before><commit_msg>pseudocode logic<commit_after>// Pseudocode:

// Storage:
// Nested array of Square objects.

// How do blocks spawn?
// For the first 4 moves, a 2 randomly spawns in an empty space.
// After that, a 2 or a 4 randomly spawns in an empty space.

// When does the game disallow a movement?
// No empty spaces in the direction of any occupied squares AND
// no squares that can combine in that direction.

// How do blocks merge?
// 2 blocks have the same number property AND are next to each other in the direction of movement.
// First check the squares at the destination of the direction of motion.
// When 2 blocks merge, move the other blocks in the direction accordingly.

// How do blocks slide?
// Move as far as possible in the direction, before hitting a boundary or another square.
<|endoftext|>


<commit_before>// The game is composed by VERTICAL_SPACES x HORIZONTAL_SPACES cells
// the bottom row is considered the VERTICAL_SPACES-1 nth row.
function Game(){
  this.grid             = [];
  this.currentTetromino = null;
  this.nextTetromino    = null;
  this.animating        = false;

  /*
   * currentX:
   *   This variable holds the current X position of
   *   the current tetromino, considering it's left
   *   most blocks
   */
  this.currentX = null;

  /*
   * currentY:
   *   This variable holds the current Y position of
   *   the current tetromino, considering it's bottom
   *   most block.
   */
  this.currentY = -1;

  this._initializeGrid();
}

Game.prototype = {
  _initializeGrid: function(){
    var i = 0;
    var j = 0;

    for (i = 0; i < Game.VERTICAL_SPACES; ++i){
      this.grid[i] = [];
      for(j = 0; j < Game.HORIZONTAL_SPACES; ++j){
        this.grid[i][j] = null;
      }
    }
  },

  moveDown: function(){
    if (!this.canMoveDown())
      return false;

    // All is fine! We can move the tetromino down
    // Easiest algorithm is just to remove any references to
    // the Tetromino and put it back in
    this._removeCurrentTetromino();

    // Put the tetromino back in
    this.currentY = this.currentY + 1;
    this._placeCurrentTetromino();

    return true;
  },

  moveLeft: function(){
    var i = 0,
        j = 0,
        w = 0,
        firstXBlock = 0,
        nextXBlock = 0

    var tetrominoHeight = this.currentTetromino.height();
    var tetrominoWidth  = this.currentTetromino.width();

    // Return as early as possible if block at edge of
    // grid
    if ( this.currentX === 0 )
      return false;

    // If block outside of grid on
    // top allow movement as long is still inside
    // the horizontal spaces
    if ( this.currentY < 0 && this.currentX > 0 ){
      this.currentX = this.currentX - 1;
      return true;
    }

    // Check first if blocks to the left are available
    for ( i = tetrominoHeight - 1, j = this.currentY; i >= 0 && j >= 0; --i, --j ){
      // Get first block filled in
      for ( w = 0; w < tetrominoWidth; ++w ){
        if(this.currentTetromino.grid[i][w] === 1 ){
          firstXBlock = w;
          break;
        }
      }

      nextXBlock = this.currentX + firstXBlock - 1;
      if ( this.grid[j][nextXBlock] !== null )
        return false;
    }

    // Clean block from the grid
    this._removeCurrentTetromino();

    // Put back in
    this.currentX = this.currentX - 1
    this._placeCurrentTetromino();

    return true;
  },

  moveRight: function(){
    var i = 0,
        j = 0,
        w = 0,
        lastXBlock = 0,
        nextXBlock = 0

    var tetrominoHeight = this.currentTetromino.height();
    var tetrominoWidth  = this.currentTetromino.width();

    // Return as early as possible if block at edge of
    // grid
    if ( tetrominoHeight <= this.currentY && this.currentX + tetrominoWidth - 1 === Game.HORIZONTAL_SPACES - 1)
      return false;

    // If block outside of grid on
    // top allow movement as long is still inside
    // the horizontal spaces
    if ( this.currentY < 0)
      if ( this.currentX + tetrominoWidth < Game.HORIZONTAL_SPACES ){
        this.currentX = this.currentX + 1;
        return true;
      }
      else
        return false;

    // Check first if blocks to the right are available
    for ( i = tetrominoHeight - 1, j = this.currentY; i >= 0 && j >= 0; --i, --j ){
      // Get last block filled in
      for ( w = 0; w < tetrominoWidth; ++w ){
        if(this.currentTetromino.grid[i][w] === 1 )
          lastXBlock = w;
      }

      nextXBlock = lastXBlock + this.currentX + 1;
      if ( this.grid[j][nextXBlock] !== null || nextXBlock === Game.HORIZONTAL_SPACES)
        return false;
    }

    // Clean block from the grid
    this._removeCurrentTetromino();

    // Put back in
    this.currentX = this.currentX + 1;
    this._placeCurrentTetromino();

    return true;
  },

  start: function(options){
    if ( options === undefined )
      options = {}

    this.currentTetromino = options.currentTetromino || Tetromino.random();
    this.nextTetromino    = options.nextTetromino    || Tetromino.random();

    // Decide what's the currentX position
    // based on the width of the Tetromino
    this.currentX = this._calculateCurrentX();
  },

  next: function(){
    this.currentTetromino = this.nextTetromino;
    this.nextTetromino    = Tetromino.random();
    this.currentY         = -1;
    this.currentX         = this._calculateCurrentX();
  },

  removeCompletedRows: function(){
    var i = this.grid.length - 1,
        j = 0;

    for ( ; i >= 0; --i ) {
      for (j = 0; j < this.grid[i].length; ++j ) {
        // If it's null we can skip this
        // row
        if ( this.grid[i][j] === null  )
          break
        // If last position is occupied
        // and it hasn't return from this
        // loop. this line is complete and
        // must be removed
        else if ( this.grid[i][j] !== null && j === this.grid[i].length - 1 ) {
          this.removeRow(i);

          // Save one full grid iteration
          // if row removed it 0
          if (i !== 0)
            this.removeCompletedRows();
        }
      }
    }
  },

  removeRow: function(row){
    var i = row

    for ( ; i >= 0; --i )
      if ( i == 0 )
        for ( var j = 0; j < this.grid[i].length; ++j)
          this.grid[i][j] = null;
      else
        this.grid[i] = _.clone(this.grid[i - 1]);

  },

  rotate: function(){
    var newGrid      = this.currentTetromino.rotateRight(false),
        currHeight   = this.currentTetromino.height(),
        currWidth    = this.currentTetromino.width(),
        futureHeight = newGrid.length,
        futureWidth  = newGrid[0].length,
        futureX      = null,
        futureY      = null;

    if ( futureHeight > currHeight )
      futureY = this.currentY + Math.floor(futureHeight/2);
    else if ( futureHeight < currHeight )
      futureY = this.currentY - Math.floor(currHeight/2);

    if ( futureWidth > currWidth )
      futureX = this.currentX - Math.floor(futureWidth/2);
    else if ( futureWidth < currWidth )
      futureX = this.currentX + Math.floor(currWidth/2);

    // if futureX is less than 0
    // means the tetromino would be
    // drawn outside of the left bound
    if ( futureX < 0 )
      return false;

    // if futureX plus futureWidth is
    // higher than Game.HORIZONTAL_SPACES means
    // the tetromino would be drawn outside
    // of the right bound
    if ( futureX + futureWidth > Game.HORIZONTAL_SPACES )
      return false;

    var w = futureHeight;
    for (var i = futureY; i >= 0 && w > 0; --i, --w){
      for (var j = futureX; j < futureX + futureWidth; ++j){

        if(newGrid[w - 1][j - futureX] != 1)
          continue;

        if(this.grid[i][j] != null && this.grid[i][j] != this.currentTetromino)
          return false;
      }
    }

    // Clean block from the grid
    this._removeCurrentTetromino();

    this.currentTetromino.rotateRight();
    this.currentX = futureX;
    this.currentY = futureY;

    this._placeCurrentTetromino();

    return true;
  },

  // helper methods
  isAnimating: function(animating){
     if( animating == undefined )
       return this.animating;

     this.animating = animating;
  },

  canMoveDown: function(){
    var i               = 0,
        j               = 0,
        tetrominoWidth  = this.currentTetromino.width()

    if (this.currentY == (Game.VERTICAL_SPACES - 1))
      return false;

    for(i = this.currentX; i < this.currentX + tetrominoWidth; ++i){
      for(j = this.currentY + 1; j >= 0; --j){

        // Edge case when outside
        // of the grid
        if(j - 1 > -1 && this.grid[j - 1][i] != this.currentTetromino )
          continue;
        else if(this.grid[j][i] == this.currentTetromino)
          break;
        else if(this.grid[j][i] != null)
          return false;
      }
    }

    return true;
  },

  // Private methods
  _removeCurrentTetromino: function(){
    // Search algorithm goes bottom-up
    var found = false;
    for(var i = this.currentY; i >= 0; --i){
      for(var j = 0; j < Game.HORIZONTAL_SPACES; ++j){

        if(this.grid[i][j] == this.currentTetromino){
          this.grid[i][j] = null;
          found = true;
        }
      }

      // When going up if one row has no occurrences
      // of a particular tetromino means the next up
      // can't have either. So we break earlier
      if(!found)
        break;
      else
        found = false;
    }
  },

  _placeCurrentTetromino: function(){
    var updateY         = this.currentY
    var tetrominoHeight = this.currentTetromino.height();
    var tetrominoWidth  = this.currentTetromino.width();

    for(var i = tetrominoHeight - 1; i >= 0; --i){
      for(var j = 0; j < tetrominoWidth; ++j){
        if(this.currentTetromino.grid[i][j] == 1)
          // currentY should be inclusive
          this.grid[updateY][this.currentX + j] = this.currentTetromino
      }
      --updateY;

      if ( updateY < 0 )
        break;

    }
    return true;
  },

  _calculateCurrentX: function(){
    if(this.currentTetromino.width() % 2 == 0)
      return (Game.HORIZONTAL_SPACES - this.currentTetromino.width())/2;
    else
      return Math.floor(Game.HORIZONTAL_SPACES / this.currentTetromino.width());
  }
}

Game.HORIZONTAL_SPACES = 10;
Game.VERTICAL_SPACES   = 20;
<commit_msg>Fixes rotate for the square block<commit_after>// The game is composed by VERTICAL_SPACES x HORIZONTAL_SPACES cells
// the bottom row is considered the VERTICAL_SPACES-1 nth row.
function Game(){
  this.grid             = [];
  this.currentTetromino = null;
  this.nextTetromino    = null;
  this.animating        = false;

  /*
   * currentX:
   *   This variable holds the current X position of
   *   the current tetromino, considering it's left
   *   most blocks
   */
  this.currentX = null;

  /*
   * currentY:
   *   This variable holds the current Y position of
   *   the current tetromino, considering it's bottom
   *   most block.
   */
  this.currentY = -1;

  this._initializeGrid();
}

Game.prototype = {
  _initializeGrid: function(){
    var i = 0;
    var j = 0;

    for (i = 0; i < Game.VERTICAL_SPACES; ++i){
      this.grid[i] = [];
      for(j = 0; j < Game.HORIZONTAL_SPACES; ++j){
        this.grid[i][j] = null;
      }
    }
  },

  moveDown: function(){
    if (!this.canMoveDown())
      return false;

    // All is fine! We can move the tetromino down
    // Easiest algorithm is just to remove any references to
    // the Tetromino and put it back in
    this._removeCurrentTetromino();

    // Put the tetromino back in
    this.currentY = this.currentY + 1;
    this._placeCurrentTetromino();

    return true;
  },

  moveLeft: function(){
    var i = 0,
        j = 0,
        w = 0,
        firstXBlock = 0,
        nextXBlock = 0

    var tetrominoHeight = this.currentTetromino.height();
    var tetrominoWidth  = this.currentTetromino.width();

    // Return as early as possible if block at edge of
    // grid
    if ( this.currentX === 0 )
      return false;

    // If block outside of grid on
    // top allow movement as long is still inside
    // the horizontal spaces
    if ( this.currentY < 0 && this.currentX > 0 ){
      this.currentX = this.currentX - 1;
      return true;
    }

    // Check first if blocks to the left are available
    for ( i = tetrominoHeight - 1, j = this.currentY; i >= 0 && j >= 0; --i, --j ){
      // Get first block filled in
      for ( w = 0; w < tetrominoWidth; ++w ){
        if(this.currentTetromino.grid[i][w] === 1 ){
          firstXBlock = w;
          break;
        }
      }

      nextXBlock = this.currentX + firstXBlock - 1;
      if ( this.grid[j][nextXBlock] !== null )
        return false;
    }

    // Clean block from the grid
    this._removeCurrentTetromino();

    // Put back in
    this.currentX = this.currentX - 1
    this._placeCurrentTetromino();

    return true;
  },

  moveRight: function(){
    var i = 0,
        j = 0,
        w = 0,
        lastXBlock = 0,
        nextXBlock = 0

    var tetrominoHeight = this.currentTetromino.height();
    var tetrominoWidth  = this.currentTetromino.width();

    // Return as early as possible if block at edge of
    // grid
    if ( tetrominoHeight <= this.currentY && this.currentX + tetrominoWidth - 1 === Game.HORIZONTAL_SPACES - 1)
      return false;

    // If block outside of grid on
    // top allow movement as long is still inside
    // the horizontal spaces
    if ( this.currentY < 0)
      if ( this.currentX + tetrominoWidth < Game.HORIZONTAL_SPACES ){
        this.currentX = this.currentX + 1;
        return true;
      }
      else
        return false;

    // Check first if blocks to the right are available
    for ( i = tetrominoHeight - 1, j = this.currentY; i >= 0 && j >= 0; --i, --j ){
      // Get last block filled in
      for ( w = 0; w < tetrominoWidth; ++w ){
        if(this.currentTetromino.grid[i][w] === 1 )
          lastXBlock = w;
      }

      nextXBlock = lastXBlock + this.currentX + 1;
      if ( this.grid[j][nextXBlock] !== null || nextXBlock === Game.HORIZONTAL_SPACES)
        return false;
    }

    // Clean block from the grid
    this._removeCurrentTetromino();

    // Put back in
    this.currentX = this.currentX + 1;
    this._placeCurrentTetromino();

    return true;
  },

  start: function(options){
    if ( options === undefined )
      options = {}

    this.currentTetromino = options.currentTetromino || Tetromino.random();
    this.nextTetromino    = options.nextTetromino    || Tetromino.random();

    // Decide what's the currentX position
    // based on the width of the Tetromino
    this.currentX = this._calculateCurrentX();
  },

  next: function(){
    this.currentTetromino = this.nextTetromino;
    this.nextTetromino    = Tetromino.random();
    this.currentY         = -1;
    this.currentX         = this._calculateCurrentX();
  },

  removeCompletedRows: function(){
    var i = this.grid.length - 1,
        j = 0;

    for ( ; i >= 0; --i ) {
      for (j = 0; j < this.grid[i].length; ++j ) {
        // If it's null we can skip this
        // row
        if ( this.grid[i][j] === null  )
          break
        // If last position is occupied
        // and it hasn't return from this
        // loop. this line is complete and
        // must be removed
        else if ( this.grid[i][j] !== null && j === this.grid[i].length - 1 ) {
          this.removeRow(i);

          // Save one full grid iteration
          // if row removed it 0
          if (i !== 0)
            this.removeCompletedRows();
        }
      }
    }
  },

  removeRow: function(row){
    var i = row

    for ( ; i >= 0; --i )
      if ( i == 0 )
        for ( var j = 0; j < this.grid[i].length; ++j)
          this.grid[i][j] = null;
      else
        this.grid[i] = _.clone(this.grid[i - 1]);

  },

  rotate: function(){
    var newGrid      = this.currentTetromino.rotateRight(false),
        currHeight   = this.currentTetromino.height(),
        currWidth    = this.currentTetromino.width(),
        futureHeight = newGrid.length,
        futureWidth  = newGrid[0].length,
        futureX      = null,
        futureY      = null;

    if ( futureHeight > currHeight )
      futureY = this.currentY + Math.floor(futureHeight/2);
    else if ( futureHeight < currHeight )
      futureY = this.currentY - Math.floor(currHeight/2);
    else
      futureY = this.currentY;

    if ( futureWidth > currWidth )
      futureX = this.currentX - Math.floor(futureWidth/2);
    else if ( futureWidth < currWidth )
      futureX = this.currentX + Math.floor(currWidth/2);
    else
      futureX = this.currentX;

    // if futureX is less than 0
    // means the tetromino would be
    // drawn outside of the left bound
    if ( futureX < 0 )
      return false;

    // if futureX plus futureWidth is
    // higher than Game.HORIZONTAL_SPACES means
    // the tetromino would be drawn outside
    // of the right bound
    if ( futureX + futureWidth > Game.HORIZONTAL_SPACES )
      return false;

    var w = futureHeight;
    for (var i = futureY; i >= 0 && w > 0; --i, --w){
      for (var j = futureX; j < futureX + futureWidth; ++j){

        if(newGrid[w - 1][j - futureX] !== 1)
          continue;

        if(this.grid[i][j] !== null && this.grid[i][j] !== this.currentTetromino)
          return false;
      }
    }

    // Clean block from the grid
    this._removeCurrentTetromino();

    this.currentTetromino.rotateRight();
    this.currentX = futureX;
    this.currentY = futureY;

    this._placeCurrentTetromino();

    return true;
  },

  // helper methods
  isAnimating: function(animating){
     if( animating == undefined )
       return this.animating;

     this.animating = animating;
  },

  canMoveDown: function(){
    var i               = 0,
        j               = 0,
        tetrominoWidth  = this.currentTetromino.width()

    if (this.currentY == (Game.VERTICAL_SPACES - 1))
      return false;

    for(i = this.currentX; i < this.currentX + tetrominoWidth; ++i){
      for(j = this.currentY + 1; j >= 0; --j){

        // Edge case when outside
        // of the grid
        if(j - 1 > -1 && this.grid[j - 1][i] != this.currentTetromino )
          continue;
        else if(this.grid[j][i] == this.currentTetromino)
          break;
        else if(this.grid[j][i] != null)
          return false;
      }
    }

    return true;
  },

  // Private methods
  _removeCurrentTetromino: function(){
    // Search algorithm goes bottom-up
    var found = false;
    for(var i = this.currentY; i >= 0; --i){
      for(var j = 0; j < Game.HORIZONTAL_SPACES; ++j){

        if(this.grid[i][j] == this.currentTetromino){
          this.grid[i][j] = null;
          found = true;
        }
      }

      // When going up if one row has no occurrences
      // of a particular tetromino means the next up
      // can't have either. So we break earlier
      if(!found)
        break;
      else
        found = false;
    }
  },

  _placeCurrentTetromino: function(){
    var updateY         = this.currentY
    var tetrominoHeight = this.currentTetromino.height();
    var tetrominoWidth  = this.currentTetromino.width();

    for(var i = tetrominoHeight - 1; i >= 0; --i){
      for(var j = 0; j < tetrominoWidth; ++j){
        if(this.currentTetromino.grid[i][j] == 1)
          // currentY should be inclusive
          this.grid[updateY][this.currentX + j] = this.currentTetromino
      }
      --updateY;

      if ( updateY < 0 )
        break;

    }
    return true;
  },

  _calculateCurrentX: function(){
    if(this.currentTetromino.width() % 2 == 0)
      return (Game.HORIZONTAL_SPACES - this.currentTetromino.width())/2;
    else
      return Math.floor(Game.HORIZONTAL_SPACES / this.currentTetromino.width());
  }
}

Game.HORIZONTAL_SPACES = 10;
Game.VERTICAL_SPACES   = 20;
<|endoftext|>