<commit_before>define('app/views/monitoring', [
    'app/views/mistscreen',
    'text!app/templates/monitoring.html', 'ember'],
    /**
     *
     * Monitoring View
     *
     * @returns Class
     */
    function(MistScreen, monitoring_html) {
        return MistScreen.extend({

            template: Ember.Handlebars.compile(monitoring_html),

            cpuGraph: null,
            loadGraph: null,
            memGraph: null,
            viewRendered: false,
            //other Graphs to be added (TODO)

            init: function() {
                this._super();
                this.setUpGraphs();
            },

            // Check If Ember View Rendered
            didInsertElement: function(){
                console.log("- Ember Monitoring View Rendered");
                this.set('viewRendered',true);
                this._super();
            },

            // Check If Ember View Is Destroyed
            willDestroyElement: function(){
                console.log("- Ember Monitoring View Is Being Destroyed");
                window.clearInterval(window.monitoringInterval);
                this._super();
            },

            updateGraphs: function(data){
                    console.log("- We Have New Data At: " + (new Date()).toTimeString());

                    this.cpuGraph.updateData(data.cpu);
                    this.loadGraph.updateData(data.load);
                    this.memGraph.updateData(data.memory);
            },

            clickedCollapse: function(graph){

                $("#" + graph.id).hide(400);
                $("#" + graph.id + "Btn").show(400);
                // DEBUG Todo REMOVE IT
                console.log(graph);
                console.log("clickedCollapse");
            },

            clickedExpand: function(graph){

                $("#" + graph.id).show(400);
                $("#" + graph.id + "Btn").hide(400);
                // DEBUG Todo REMOVE IT
                console.log(graph);
                console.log("clickedCollapse");
            },

            // Graph Constructor
            setUpGraphs: function() {

                // Graph Constructor
                function Graph(divID,width,timeToDisplay){

                    var NUM_OF_LABELS = 6;
                    var STEP_SECONDS = 10;
                    var NUM_OF_MIN_MEASUREMENTS = 180;  // 30 Minutes
                    var NUM_OF_MAX_MEASUREMENTS = 8640; // 24 Hours

                    // Calculate Aspect Ratio Of Height
                    var fixedHeight = 160 / 1280 * width;
                    var margin = {top: 20, right: 0, bottom: 30, left: 0}; // TODO Fix Margin Based On Aspect Ratio

                    this.id = divID;
                    this.width = width;
                    this.height = (fixedHeight < 85 ? 85 : fixedHeight);
                    this.data = [];
                    this.timeDisplayed = timeToDisplay;
                    this.realDataIndex = -1;

                    // Distance of two values in graph (pixels), Important For Animation
                    this.valuesDistance = 0;
                    this.isAnimated = true;

                    // Calculate The step  of the time axis
                    this.secondsStep =  Math.floor((timeToDisplay.getHours()*60*60 +
                                        timeToDisplay.getMinutes()*60 +
                                        timeToDisplay.getSeconds() ) / NUM_OF_LABELS);

                    // Scale Functions will scale graph to defined width and height
                    var xScale = d3.time.scale().range([0, this.width - margin.left - margin.right]);
                    var yScale = d3.scale.linear().range([this.height - margin.top - margin.bottom, 0]);

                    // valueline is function that creates the main line based on data
                    var valueline = d3.svg.line()
                                    .x(function(d) {return xScale(d.time); })
                                    .y(function(d) {return yScale(d.value); });

                    // Create main graph, append svg and other elements
                    var d3svg =   d3.select("#"+this.id)
                                    .append('svg')
                                    .attr('width',this.width)
                                    .attr('height',this.height);

                    var d3GridX = d3.select("#"+this.id)
                                    .select('svg')
                                    .append("g")
                                    .attr("class", "grid-x")
                                    .attr("transform", "translate(0," + this.height + ")");

                    var d3GridY = d3.select("#"+this.id)
                                    .select('svg')
                                    .append("g")
                                    .attr("class", "grid-y");

                    var d3vLine = d3svg.append('g')
                                       .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
                                       .append('path');

                    var d3xAxis = d3svg.append('g')
                                  .attr('class','x-axis')
                                  .attr("transform", "translate(0," + (this.height - margin.bottom +2) + ")");

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

                    /**
                    * Method: updateData
                    * Gets Data From Controller, checks for overflow or less data received
                    * fixes them and then updates Graph
                    */
                    this.updateData = function(newData) {

                        if(this.data.length == 0)
                        {
                            info("-- Received First Data");

                            var dataBuffer = [];
                            var measurements_received = newData.length;
                            if(measurements_received < NUM_OF_MIN_MEASUREMENTS)
                            {
                                info("-- Measurement Are Less Than Expected, Filling With 0");
                                // Get First Measurement Time
                                var format = d3.time.format("%X");
                                var metricTime = format.parse(newData[0].time);
                                metricTime = new Date(metricTime.getTime() - STEP_SECONDS*1000);

                                // Fill Data With Zeros
                                for(var i= 0; i < (NUM_OF_MIN_MEASUREMENTS - measurements_received); i++)
                                {
                                    var zeroObject = {
                                        time: (metricTime.getHours() + ":" + metricTime.getMinutes() + ":" + metricTime.getSeconds()),
                                        value: 0
                                    }

                                    dataBuffer.push(zeroObject);
                                    metricTime = new Date(metricTime.getTime() - STEP_SECONDS*1000);
                                }
                                // Set Real Data Start Index
                                this.realDataIndex = dataBuffer.length;
                                dataBuffer.reverse();

                                // Join New Data With Zero Value Array
                                dataBuffer = dataBuffer.concat(newData);
                            }
                            else{

                                dataBuffer = newData;
                            }

                            // Fix Values, TypeCaste To Date And Number
                            var fixedData = [];
                            dataBuffer.forEach(function(d) {

                                var format    = d3.time.format("%X");
                                var tempObj   = {};
                                tempObj.time  = format.parse(d.time);
                                tempObj.value = +d.value;
                                fixedData.push(tempObj);
                            });

                            // Set Our Final Data
                            this.data = fixedData;
                        }
                        else{
                            info("-- Received Data Update, Num Of New Data: " + newData.length);

                            // Check If We Have Overflow , Clip Older Measurement
                            if(this.data.length + newData.length > NUM_OF_MAX_MEASUREMENTS)
                            {
                                info("-- Overflow, Max Data: " + NUM_OF_MAX_MEASUREMENTS + " , Current: " + (this.data.length + newData.length));

                                // Clip Old Data
                                var num_of_overflow_Objs = this.data.length + newData.length - NUM_OF_MAX_MEASUREMENTS;
                                this.data = this.data.slice(num_of_overflow_Objs);
                            }

                            // Fix Values, TypeCaste To Date And Number
                            var fixedData = [];
                            newData.forEach(function(d) {
                                var format = d3.time.format("%X");
                                var tempObj = {};
                                tempObj.time = format.parse(d.time);
                                tempObj.value = +d.value;
                                fixedData.push(tempObj);
                            });

                            // Set Our Final Data
                            this.data = this.data.concat(fixedData);
                            info("-- Updated Data Lenght: " + this.data.length);
                        }

                        this.updateView();
                    };

                    /**
                    * Method: updateView
                    * Updates graph by selecting data from data instance
                    * redraws value line, x-axis, labels and grid
                    */
                    this.updateView = function() {

                        console.log("-- Updating View")

                        var displayedData = [];
                        var num_of_displayed_measurements = (this.timeDisplayed.getHours()*60*60 +
                                                            this.timeDisplayed.getMinutes()*60   +
                                                            this.timeDisplayed.getSeconds()) / STEP_SECONDS;

                        // Get only data that will be displayed
                        if(this.data.length > num_of_displayed_measurements)
                        {
                            displayedData = this.data.slice(this.data.length - num_of_displayed_measurements);
                            console.log("--- After  Slice - Data Lenght: " + displayedData.length);
                        }
                        else
                        {
                            displayedData = this.data;
                        }

                        // Set Possible min/max x & y values
                        xScale.domain(d3.extent(displayedData , function(d) { return d.time;  }));
                        yScale.domain([0, d3.max(displayedData, function(d) { return d.value; })]);

                        if(this.isAnimated)
                        {
                            // For Animated Graph
                            // After we have our values we can calculate the distance and set new range
                            this.calcValueDistance();
                            xScale.range([-this.valuesDistance, this.width - margin.left - margin.right]);

                            // Update Animated Line
                            d3vLine.attr("transform", "translate(" + this.valuesDistance + ")")
                                   .attr("d", valueline(displayedData))
                                   .transition()
                                   .ease("linear")
                                   .duration(STEP_SECONDS*1000)
                                   .attr("transform", "translate(" + 0 + ")");
                        }
                        else {
                            xScale.range([0, this.width - margin.left - margin.right]);

                            // Update value line
                            d3vLine.attr("d", valueline(displayedData));
                        }

                        // Update xAxis - TODO Fix secondsStep
                        d3xAxis.call(d3.svg.axis()
                                           .scale(xScale)
                                           .orient("bottom")
                                           .ticks(d3.time.minutes, this.secondsStep/60) // TODO Fix SecondsStep
                                           .tickFormat(d3.time.format("%I:%M%p")))
                                           .selectAll("text")
                                           .style("text-anchor", "end")
                                           .attr('x','-10');

                        // Create Grid
                        d3GridX.call(d3.svg.axis()
                                           .scale(xScale)
                                           .orient("bottom")
                                           .ticks(5)
                                           .tickSize(-this.height, 0, 0)
                                           .tickFormat(""));

                        d3GridY.call(d3.svg.axis()
                                           .scale(yScale)
                                           .orient("left")
                                           .ticks(5)
                                           .tickSize(-this.width, 0, 0)
                                           .tickFormat(""));
                    };

                    /**
                    * Method: changeWidth
                    * Changes the width of svg element, sets new scale values
                    * and updates height to keep aspect ratio
                    */
                    this.changeWidth = function (width) {

                        // Create an aspect ratio
                        var newHeight = 160 / 1280 * width;

                        this.height = (newHeight < 85 ? 85 : newHeight);
                        this.width = width;

                        // Set new values to SVG element
                        d3svg.attr('width',this.width)
                             .attr('height',this.height);

                        // Update scale to new values
                        yScale = d3.scale.linear().range([this.height - margin.top - margin.bottom, 0]);
                        xScale = d3.time.scale().range([0, this.width - margin.left - margin.right]);

                        // Update x-axis based on new height
                        d3xAxis.attr("transform", "translate(0," + (this.height - margin.bottom) + ")");

                        // Update TODO update Grid

                        this.updateView();
                    };

                    /**
                    * Method: getLastMeasurementTime
                    * Returns null if there are no data
                    * else last measurements time as Date object
                    */
                    this.getLastMeasurementTime = function(){

                        if(this.data.length == 0)
                            return null;
                        else {
                            var lastObject = this.data[this.data.length-1];
                            return lastObject.time;
                        }
                    };

                    /**
                    * Method: calcValueDistance
                    * Calculates the distance between the last two points
                    * Important for animated graph
                    */
                    this.calcValueDistance = function() {

                        // Get last 2 data
                        if(this.data.length >= 2)
                        {
                            var xValueA = xScale(this.data[this.data.length-2].time);
                            var xValueB = xScale(this.data[this.data.length-1].time);
                            this.valuesDistance = xValueB - xValueA;
                        }
                    };

                }

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

                // Execuation Starts Here
                var machine = this.get('controller').get('model');
                console.log("- Machine Still Probing ?:" + machine.probing);
                console.log("- Machine Set To Probed ?:" + machine.probed);

                if(this.viewRendered && machine.hasMonitoring && !machine.probing && machine.probed){

                    var self = this;
                    var controller = Mist.monitoringController;

                    controller.initController(machine,this);

                    Em.run.next(function() {

                        // Re-Initialize Jquery Mobile Buttons, Hide Graph Buttons
                        $('.monitoring-button').button();
                        $('#add-rule-button').button();
                        $('#monitoring-dialog').popup();
                        $('#cpuGraphBtn').hide(0);
                        $('#loadGraphBtn').hide(0);
                        $('#memGraphBtn').hide(0);

                        console.log("- Asks For #cpuGraph");       // DEBUG TODO Remove It
                        var width = $('#cpuGraph').width();     // Get Current Width

                        // Create Graphs // TODO change tempDate
                        var tempDate = new Date();
                        tempDate.setHours(0,30,0);
                        self.cpuGraph = new Graph('cpuGraph',width,tempDate);
                        self.loadGraph = new Graph('loadGraph',width,tempDate);
                        self.memGraph = new Graph('memGraph',width,tempDate);

                        controller.setupDataRequest();

                        // Set Up Resolution Change Event
                        $(window).resize(function(){

                                    self.cpuGraph.changeWidth($('#cpuGraph').width());
                                    self.loadGraph.changeWidth($('#loadGraph').width());
                                    self.memGraph.changeWidth($('#memGraph').width());
                        })
                    });

                    Mist.rulesController.redrawRules();
                }
            }.observes('controller.model.hasMonitoring','controller.model.probing','viewRendered'),

        });
    }
);
<commit_msg>Added animated grid and x-axis, reduced bottom margin a little bit<commit_after>define('app/views/monitoring', [
    'app/views/mistscreen',
    'text!app/templates/monitoring.html', 'ember'],
    /**
     *
     * Monitoring View
     *
     * @returns Class
     */
    function(MistScreen, monitoring_html) {
        return MistScreen.extend({

            template: Ember.Handlebars.compile(monitoring_html),

            cpuGraph: null,
            loadGraph: null,
            memGraph: null,
            viewRendered: false,
            //other Graphs to be added (TODO)

            init: function() {
                this._super();
                this.setUpGraphs();
            },

            // Check If Ember View Rendered
            didInsertElement: function(){
                console.log("- Ember Monitoring View Rendered");
                this.set('viewRendered',true);
                this._super();
            },

            // Check If Ember View Is Destroyed
            willDestroyElement: function(){
                console.log("- Ember Monitoring View Is Being Destroyed");
                window.clearInterval(window.monitoringInterval);
                this._super();
            },

            updateGraphs: function(data){
                    console.log("- We Have New Data At: " + (new Date()).toTimeString());

                    this.cpuGraph.updateData(data.cpu);
                    this.loadGraph.updateData(data.load);
                    this.memGraph.updateData(data.memory);
            },

            clickedCollapse: function(graph){

                $("#" + graph.id).hide(400);
                $("#" + graph.id + "Btn").show(400);
                // DEBUG Todo REMOVE IT
                console.log(graph);
                console.log("clickedCollapse");
            },

            clickedExpand: function(graph){

                $("#" + graph.id).show(400);
                $("#" + graph.id + "Btn").hide(400);
                // DEBUG Todo REMOVE IT
                console.log(graph);
                console.log("clickedCollapse");
            },

            // Graph Constructor
            setUpGraphs: function() {

                // Graph Constructor
                function Graph(divID,width,timeToDisplay){

                    var NUM_OF_LABELS = 6;
                    var STEP_SECONDS = 10;
                    var NUM_OF_MIN_MEASUREMENTS = 180;  // 30 Minutes
                    var NUM_OF_MAX_MEASUREMENTS = 8640; // 24 Hours

                    // Calculate Aspect Ratio Of Height
                    var fixedHeight = 160 / 1280 * width;
                    var margin = {top: 20, right: 0, bottom: 24, left: 0}; // TODO Fix Margin Based On Aspect Ratio

                    this.id = divID;
                    this.width = width;
                    this.height = (fixedHeight < 85 ? 85 : fixedHeight);
                    this.data = [];
                    this.timeDisplayed = timeToDisplay;
                    this.realDataIndex = -1;

                    // Distance of two values in graph (pixels), Important For Animation
                    this.valuesDistance = 0;
                    this.isAnimated = true;

                    // Calculate The step  of the time axis
                    this.secondsStep =  Math.floor((timeToDisplay.getHours()*60*60 +
                                        timeToDisplay.getMinutes()*60 +
                                        timeToDisplay.getSeconds() ) / NUM_OF_LABELS);

                    // Scale Functions will scale graph to defined width and height
                    var xScale = d3.time.scale().range([0, this.width - margin.left - margin.right]);
                    var yScale = d3.scale.linear().range([this.height - margin.top - margin.bottom, 0]);

                    // valueline is function that creates the main line based on data
                    var valueline = d3.svg.line()
                                    .x(function(d) {return xScale(d.time); })
                                    .y(function(d) {return yScale(d.value); });

                    // Create main graph, append svg and other elements
                    var d3svg =   d3.select("#"+this.id)
                                    .append('svg')
                                    .attr('width',this.width)
                                    .attr('height',this.height);

                    var d3GridX = d3.select("#"+this.id)
                                    .select('svg')
                                    .append("g")
                                    .attr("class", "grid-x")
                                    .attr("transform", "translate(0," + this.height + ")");

                    var d3GridY = d3.select("#"+this.id)
                                    .select('svg')
                                    .append("g")
                                    .attr("class", "grid-y");

                    var d3vLine = d3svg.append('g')
                                       .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
                                       .append('path');

                    var d3xAxis = d3svg.append('g')
                                  .attr('class','x-axis')
                                  .attr("transform", "translate(0," + (this.height - margin.bottom +2) + ")");

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

                    /**
                    * Method: updateData
                    * Gets Data From Controller, checks for overflow or less data received
                    * fixes them and then updates Graph
                    */
                    this.updateData = function(newData) {

                        if(this.data.length == 0)
                        {
                            info("-- Received First Data");

                            var dataBuffer = [];
                            var measurements_received = newData.length;
                            if(measurements_received < NUM_OF_MIN_MEASUREMENTS)
                            {
                                info("-- Measurement Are Less Than Expected, Filling With 0");
                                // Get First Measurement Time
                                var format = d3.time.format("%X");
                                var metricTime = format.parse(newData[0].time);
                                metricTime = new Date(metricTime.getTime() - STEP_SECONDS*1000);

                                // Fill Data With Zeros
                                for(var i= 0; i < (NUM_OF_MIN_MEASUREMENTS - measurements_received); i++)
                                {
                                    var zeroObject = {
                                        time: (metricTime.getHours() + ":" + metricTime.getMinutes() + ":" + metricTime.getSeconds()),
                                        value: 0
                                    }

                                    dataBuffer.push(zeroObject);
                                    metricTime = new Date(metricTime.getTime() - STEP_SECONDS*1000);
                                }
                                // Set Real Data Start Index
                                this.realDataIndex = dataBuffer.length;
                                dataBuffer.reverse();

                                // Join New Data With Zero Value Array
                                dataBuffer = dataBuffer.concat(newData);
                            }
                            else{

                                dataBuffer = newData;
                            }

                            // Fix Values, TypeCaste To Date And Number
                            var fixedData = [];
                            dataBuffer.forEach(function(d) {

                                var format    = d3.time.format("%X");
                                var tempObj   = {};
                                tempObj.time  = format.parse(d.time);
                                tempObj.value = +d.value;
                                fixedData.push(tempObj);
                            });

                            // Set Our Final Data
                            this.data = fixedData;
                        }
                        else{
                            info("-- Received Data Update, Num Of New Data: " + newData.length);

                            // Check If We Have Overflow , Clip Older Measurement
                            if(this.data.length + newData.length > NUM_OF_MAX_MEASUREMENTS)
                            {
                                info("-- Overflow, Max Data: " + NUM_OF_MAX_MEASUREMENTS + " , Current: " + (this.data.length + newData.length));

                                // Clip Old Data
                                var num_of_overflow_Objs = this.data.length + newData.length - NUM_OF_MAX_MEASUREMENTS;
                                this.data = this.data.slice(num_of_overflow_Objs);
                            }

                            // Fix Values, TypeCaste To Date And Number
                            var fixedData = [];
                            newData.forEach(function(d) {
                                var format = d3.time.format("%X");
                                var tempObj = {};
                                tempObj.time = format.parse(d.time);
                                tempObj.value = +d.value;
                                fixedData.push(tempObj);
                            });

                            // Set Our Final Data
                            this.data = this.data.concat(fixedData);
                            info("-- Updated Data Lenght: " + this.data.length);
                        }

                        this.updateView();
                    };

                    /**
                    * Method: updateView
                    * Updates graph by selecting data from data instance
                    * redraws value line, x-axis, labels and grid
                    */
                    this.updateView = function() {

                        console.log("-- Updating View")

                        var displayedData = [];
                        var num_of_displayed_measurements = (this.timeDisplayed.getHours()*60*60 +
                                                            this.timeDisplayed.getMinutes()*60   +
                                                            this.timeDisplayed.getSeconds()) / STEP_SECONDS;

                        // Get only data that will be displayed
                        if(this.data.length > num_of_displayed_measurements)
                        {
                            displayedData = this.data.slice(this.data.length - num_of_displayed_measurements);
                            console.log("--- After  Slice - Data Lenght: " + displayedData.length);
                        }
                        else
                        {
                            displayedData = this.data;
                        }

                        // Set Possible min/max x & y values
                        xScale.domain(d3.extent(displayedData , function(d) { return d.time;  }));
                        yScale.domain([0, d3.max(displayedData, function(d) { return d.value; })]);

                        if(this.isAnimated)
                        {

                            var animationDuration = STEP_SECONDS*1000;
                            // For Animated Graph
                            // After we have our values we can calculate the distance and set new range
                            this.calcValueDistance();
                            xScale.range([-this.valuesDistance, this.width - margin.left - margin.right]);

                            // Update Animated Line
                            d3vLine.attr("transform", "translate(" + this.valuesDistance + ")")
                                   .attr("d", valueline(displayedData))
                                   .transition()
                                   .ease("linear")
                                   .duration(animationDuration)
                                   .attr("transform", "translate(" + 0 + ")");

                            // Update xAxis And Grid Offset- TODO Fix secondsStep
                            d3xAxis.call(d3.svg.axis()
                                           .scale(xScale)
                                           .orient("bottom")
                                           .ticks(d3.time.minutes, this.secondsStep/60) // TODO Fix SecondsStep
                                           .tickFormat(d3.time.format("%I:%M%p")))
                                           .selectAll("text")
                                           .style("text-anchor", "end")
                                           .attr('x','-10');

                            d3GridX.call(d3.svg.axis()
                                           .scale(xScale)
                                           .orient("bottom")
                                           .ticks(5)
                                           .tickSize(-this.height, 0, 0)
                                           .tickFormat(""));

                            d3GridY.call(d3.svg.axis()
                                           .scale(yScale)
                                           .orient("left")
                                           .ticks(5)
                                           .tickSize(-this.width, 0, 0)
                                           .tickFormat(""));

                            // Animate Axis And Grid
                            d3xAxis.attr("transform", "translate(" + this.valuesDistance + ","+ (this.height - margin.bottom +2) +")")
                                   .transition()
                                   .ease("linear")
                                   .duration(animationDuration)
                                   .attr("transform", "translate(0," + (this.height - margin.bottom +2) + ")");

                            d3GridX.attr("transform", "translate(" + this.valuesDistance + ","+ this.height +")")
                                   .transition()
                                   .ease("linear")
                                   .duration(animationDuration)
                                   .attr("transform", "translate(0," + this.height + ")");
                        }
                        else {
                            xScale.range([0, this.width - margin.left - margin.right]);

                            // Update value line
                            d3vLine.attr("d", valueline(displayedData));

                            // Update xAxis And grid - TODO Fix secondsStep
                            d3xAxis.call(d3.svg.axis()
                                           .scale(xScale)
                                           .orient("bottom")
                                           .ticks(d3.time.minutes, this.secondsStep/60) // TODO Fix SecondsStep
                                           .tickFormat(d3.time.format("%I:%M%p")))
                                           .selectAll("text")
                                           .style("text-anchor", "end")
                                           .attr('x','-10');

                            d3GridX.call(d3.svg.axis()
                                           .scale(xScale)
                                           .orient("bottom")
                                           .ticks(5)
                                           .tickSize(-this.height, 0, 0)
                                           .tickFormat(""));

                            d3GridY.call(d3.svg.axis()
                                           .scale(yScale)
                                           .orient("left")
                                           .ticks(5)
                                           .tickSize(-this.width, 0, 0)
                                           .tickFormat(""));
                        }
                    };

                    /**
                    * Method: changeWidth
                    * Changes the width of svg element, sets new scale values
                    * and updates height to keep aspect ratio
                    */
                    this.changeWidth = function (width) {

                        // Create an aspect ratio
                        var newHeight = 160 / 1280 * width;

                        this.height = (newHeight < 85 ? 85 : newHeight);
                        this.width = width;

                        // Set new values to SVG element
                        d3svg.attr('width',this.width)
                             .attr('height',this.height);

                        // Update scale to new values
                        yScale = d3.scale.linear().range([this.height - margin.top - margin.bottom, 0]);
                        xScale = d3.time.scale().range([0, this.width - margin.left - margin.right]);

                        // Update x-axis based on new height
                        d3xAxis.attr("transform", "translate(0," + (this.height - margin.bottom) + ")");

                        // Update TODO update Grid

                        this.updateView();
                    };

                    /**
                    * Method: getLastMeasurementTime
                    * Returns null if there are no data
                    * else last measurements time as Date object
                    */
                    this.getLastMeasurementTime = function(){

                        if(this.data.length == 0)
                            return null;
                        else {
                            var lastObject = this.data[this.data.length-1];
                            return lastObject.time;
                        }
                    };

                    /**
                    * Method: calcValueDistance
                    * Calculates the distance between the last two points
                    * Important for animated graph
                    */
                    this.calcValueDistance = function() {

                        // Get last 2 data
                        if(this.data.length >= 2)
                        {
                            var xValueA = xScale(this.data[this.data.length-2].time);
                            var xValueB = xScale(this.data[this.data.length-1].time);
                            this.valuesDistance = xValueB - xValueA;
                        }
                    };

                }

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

                // Execuation Starts Here
                var machine = this.get('controller').get('model');
                console.log("- Machine Still Probing ?:" + machine.probing);
                console.log("- Machine Set To Probed ?:" + machine.probed);

                if(this.viewRendered && machine.hasMonitoring && !machine.probing && machine.probed){

                    var self = this;
                    var controller = Mist.monitoringController;

                    controller.initController(machine,this);

                    Em.run.next(function() {

                        // Re-Initialize Jquery Mobile Buttons, Hide Graph Buttons
                        $('.monitoring-button').button();
                        $('#add-rule-button').button();
                        $('#monitoring-dialog').popup();
                        $('#cpuGraphBtn').hide(0);
                        $('#loadGraphBtn').hide(0);
                        $('#memGraphBtn').hide(0);

                        console.log("- Asks For #cpuGraph");       // DEBUG TODO Remove It
                        var width = $('#cpuGraph').width();     // Get Current Width

                        // Create Graphs // TODO change tempDate
                        var tempDate = new Date();
                        tempDate.setHours(0,30,0);
                        self.cpuGraph = new Graph('cpuGraph',width,tempDate);
                        self.loadGraph = new Graph('loadGraph',width,tempDate);
                        self.memGraph = new Graph('memGraph',width,tempDate);

                        controller.setupDataRequest();

                        // Set Up Resolution Change Event
                        $(window).resize(function(){

                                    self.cpuGraph.changeWidth($('#cpuGraph').width());
                                    self.loadGraph.changeWidth($('#loadGraph').width());
                                    self.memGraph.changeWidth($('#memGraph').width());
                        })
                    });

                    Mist.rulesController.redrawRules();
                }
            }.observes('controller.model.hasMonitoring','controller.model.probing','viewRendered'),

        });
    }
);
<|endoftext|>


<commit_before>module.exports =  {
  is: "project",
  name: "Repository",
  workspace: "logitud.repository",
  "files=": { is: 'group', elements: [
      { is: 'group', name: 'app', path: 'app/src/', elements: [
        { is: 'file', name: 'main.ts', tags: ['tsc'] },
        { is: 'file', name: 'index.html', tags: ['copy'] },
        { is: 'file', name: 'styles.css', tags: ['copy'] },
        { is: 'file', name: 'systemjs.config.js', tags: ['copy'] },
        { is: 'file', name: 'images/*.png', tags: ['copy'] },
      ]},
      { is: 'group', name: 'server', path: 'server/src/', elements: [
        { is: 'file', name: 'server.ts', tags: ['tsc']},
      ]},
      { is: 'group', name: 'shared', path: 'shared/src/', elements: [
        { is: 'file', name: 'interfaces.md', tags: ['interface']},
      ]},
  ]},
  "targets=": { is: 'group',
    "repository app=":  {
      is: 'target',
      environments: ["=::openms.aspects.angular::"],
      components: ["=::openms.aspects.angular.dev::"],
      files: ["=files:app ? tsc", "=files:shared ? tsc"],
      copyFiles: [{ is: "group", elements: ["=files:app ? copy"], dest: "", expand: true }],
      interfaces: [{ is: "group", elements: ['=files:shared ? interface'] }],
      npmPackage: { is: "component",
        dependencies: { is: "component",
          "@openmicrostep/async": "^0.1.1",
        },
      },
    },
    "repository server=":  {
      is: 'target',
      environments: ["=::openms.aspects.node::"],
      components: ["=::openms.aspects.node.dev::", "=::aspects obi::"],
      targets: ["aspects obi"],
      files: ["=files:server ? tsc", "=files:shared ? tsc"],
      interfaces: [{ is: "group", elements: ['=files:shared ? interface'] }],
      npmPackage: { is: "component",
        dependencies: { is: "component",
          "sqlite3": "^3.1.8",
          "pg": "^7.3.0",
          "mongodb": "^2.2.31",
          "request": "^2.83.0",
          "request-promise-native": "^1.0.5",
          "express-session": "^1.15.6",
          "connect-mongo": "^1.3.2",
          "ldapjs": "^1.0.1",
          "@openmicrostep/msbuildsystem.shared": "^0.6.3",
          "source-map-support": "^0.4.11",
          "uuid": "^3.1.0",
        },
        devDependencies: { is: "component",
          "@types/ldapjs": "^1.0.0",
          "@types/express-session": "^1.15.3",
          "@types/request-promise-native": "^1.0.8",
          "@types/uuid": "^3.4.3",
        },
      },
    }
  }
};
<commit_msg>project: fix make.js<commit_after>module.exports =  {
  is: "project",
  name: "Repository",
  workspace: "logitud.repository",
  "files=": { is: 'group', elements: [
      { is: 'group', name: 'app', path: 'app/src/', elements: [
        { is: 'file', name: 'main.ts', tags: ['tsc'] },
        { is: 'file', name: 'index.html', tags: ['copy'] },
        { is: 'file', name: 'styles.css', tags: ['copy'] },
        { is: 'file', name: 'systemjs.config.js', tags: ['copy'] },
        { is: 'file', name: 'images/*.png', tags: ['copy'] },
      ]},
      { is: 'group', name: 'server', path: 'server/src/', elements: [
        { is: 'file', name: 'server.ts', tags: ['tsc']},
      ]},
      { is: 'group', name: 'shared', path: 'shared/src/', elements: [
        { is: 'file', name: 'interfaces.md', tags: ['interface']},
      ]},
  ]},
  "targets=": { is: 'group',
    "repository app=":  {
      is: 'target',
      environments: ["=::openms.aspects.angular::"],
      components: ["={::openms.aspects.angular.dev::} - targets"],
      files: ["=files:app ? tsc", "=files:shared ? tsc"],
      copyFiles: [{ is: "group", elements: ["=files:app ? copy"], dest: "", expand: true }],
      interfaces: [{ is: "group", elements: ['=files:shared ? interface'] }],
      npmPackage: { is: "component",
        dependencies: { is: "component",
          "@openmicrostep/async": "^0.1.1",
        },
      },
    },
    "repository server=":  {
      is: 'target',
      environments: ["=::openms.aspects.node::"],
      components: ["=::openms.aspects.node.dev::"],
      files: ["=files:server ? tsc", "=files:shared ? tsc"],
      interfaces: [{ is: "group", elements: ['=files:shared ? interface'] }],
      npmPackage: { is: "component",
        dependencies: { is: "component",
          "sqlite3": "^3.1.8",
          "pg": "^7.3.0",
          "mongodb": "^2.2.31",
          "request": "^2.83.0",
          "request-promise-native": "^1.0.5",
          "express-session": "^1.15.6",
          "connect-mongo": "^1.3.2",
          "ldapjs": "^1.0.1",
          "@openmicrostep/msbuildsystem.shared": "^0.6.3",
          "source-map-support": "^0.4.11",
          "uuid": "^3.1.0",
        },
        devDependencies: { is: "component",
          "@types/ldapjs": "^1.0.0",
          "@types/express-session": "^1.15.3",
          "@types/request-promise-native": "^1.0.8",
          "@types/uuid": "^3.4.3",
        },
      },
    }
  }
};
<|endoftext|>


<commit_before>import { Make } from '../../af/util/make.js';
import Binding from '../Binding.js';
import { parseExpression } from '../Parser.js';
import { bindNode } from '../Bind.js';

let TemplateRepeatBinding = {

    name: 'bind-repeat',

    /**
     * @type {WeakMap}
     */
    itemNodeList : null,

    /**
     * @type {WeakMap}
     */
    itemScopeList : null,

    /**
     * @type {Node}
     */
    template : null,

    /**
     * @type {Node}
     */
    marker : null,

    /**
     * @type {Array}
     */
    modelBackup : null,

    /**
     * @constructs
     * @extends {Binding}
     * @return {void}
     */
    _make({ node: template, text, scopeInfo }) {
        Binding._make.apply(this);

        const marker = document.createComment(`repeat ${template.id} with ${text}`);

        this.originalNodeValue = text;
        this.template = template;
        this.marker = marker;

        this.itemNodeList = new Map();
        this.itemScopeList = new Map();
        this.modelBackup = [];

        console.log('replace template with marker');
        template.parentNode.replaceChild(marker, template);

        scopeInfo.bindings.push(this);
    },

    /**
     * renders one model item to the DOM
     *
     * @param  {*} model      [description]
     * @param  {ScopePrototype} scope      [description]
     * @param  {string} itemName   [description]
     * @param  {DocumentFragment} fragment   [description]
     * @param  {Node} polyParent [description]
     * @param  {Object} item       [description]
     * @param  {number} index      [description]
     * @return {void}            [description]
     */
    renderItem(model, scope, itemName, fragment, item, index) {
        let node = null;

        if (this.itemNodeList.has(item)) {
            node = this.itemNodeList.get(item);
            let childScope = this.itemScopeList.get(item);

            childScope.$first = index === 0;
            childScope.$last = model.length -1 === index;
            childScope.$index = index;

            childScope.update();
        } else {
            let childScope = Make({
                $first : index === 0,
                $last : model.length-1 === index,
                $index : index,
                __parentScope__ : scope,
            }, scope).get();

            childScope[itemName] = item;

            node = document.importNode(this.template.content, true).firstElementChild;
            bindNode(node, childScope, true);

            this.itemNodeList.set(item, node);
            this.itemScopeList.set(item, childScope);
        }

        fragment.appendChild(node);
    },

    update(scope) {
        let [itemName, link, expression] = this.originalNodeValue.split(' ');
        let model = parseExpression(expression, scope);
        let dirty = false;

        if (link !== 'in') {
            console.console.error('DataBinding: invalide expression', this.originalNodeValue);
            return;
        }

        if (!Array.isArray(model)) {
            console.warn('A repeat binding can only consume arrays!', model);
            model = [];
        }

        dirty = this.modelBackup.length !== model.length;

        if (!dirty) {
            for (let i = 0; i < model.length; i++) {
                if (model[i] !== this.modelBackup[i]) {
                    dirty = true;
                    break;
                }
            }
        }

        if (dirty) {
            // clean out items that where removed.
            this.modelBackup.forEach(item => {
                if (model.indexOf(item) < 0) {
                    const node = this.itemNodeList.get(item);

                    // if the node doesn't exist something went totally wrong... but it happens :/
                    if (node) {
                        this.marker.parentNode.removeChild(node);
                    }

                    this.itemScopeList.delete(item);
                    this.itemNodeList.delete(item);
                }
            });

            this.modelBackup = model.slice();

            if (window.Polymer) {
                window.Polymer.dom.flush();
            }

            /** @type {DocumentFragment} */
            const fragment = document.createDocumentFragment();

            model.forEach(this.renderItem.bind(this, model, scope, itemName, fragment));

            if (this.marker.nextElementSibling) {
                this.marker.parentNode.insertBefore(fragment, this.marker.nextElementSibling);
            } else {
                this.marker.parentNode.appendChild(fragment);
            }
        }
    },

    destory() {
        let count = this.modelBackup.reduce((prev, item) => {
            let [scopes, bindings] = prev;
            let scope = this.itemScopeList.get(item);
            let [scopes_add, bindings_add] = scope.__destroy__(true);

            return [scopes + scopes_add, bindings + bindings_add];
        }, [0, 0]);

        this.itemScopeList = null;
        this.itemNodeList = null;

        return count;
    },

    __proto__: Binding,
};

BindingRegistry.register(TemplateRepeatBinding);

export default TemplateRepeatBinding;
<commit_msg>create a fast render mode to improve render time for large lists<commit_after>import { Make } from '../../af/util/make.js';
import { bindNode } from '../Bind.js';
import BindingApi from '../BindingApi';

const { parseExpression } = BindingApi().parser;

const TemplateRepeatBinding = {

    name: 'bind-repeat',

    /**
     * @type {WeakMap}
     */
    itemNodeList : null,

    /**
     * @type {WeakMap}
     */
    itemScopeList : null,

    /**
     * @type {Node}
     */
    template : null,

    /**
     * @type {Node}
     */
    marker : null,

    /**
     * @type {Array}
     */
    modelBackup : null,

    model: null,

    /**
     * @type {Boolean}
     */
    fast: false,

    /**
     * @constructs
     * @extends {Binding}
     * @return {void}
     */
    _make({ node: template, text, parameter }) {
        super._make();

        const marker = document.createComment(`repeat ${template.id} with ${text}`);

        this.originalNodeValue = text;
        this.template = template;
        this.marker = marker;

        if (parameter === 'fast') {
            this.itemNodeList = [];
            this.itemScopeList = [];
            this.fast = true;
        } else {
            this.itemNodeList = new Map();
            this.itemScopeList = new Map();
        }

        this.modelBackup = [];

        console.log('replace template with marker');
        template.parentNode.replaceChild(marker, template);

        BindingApi(this).attachBinding(TemplateRepeatBinding);
    },

    /**
     * renders one model item to the DOM
     *
     * @param  {ScopePrototype} scope      [description]
     * @param  {string} itemName   [description]
     * @param  {number} index      [description]
     * @return {void}            [description]
     */
    renderItemFast(scope, itemName, index) {
        let childScope = Make({
            $first : index === 0,
            $last : this.model.length-1 === index,
            $index : index,
            __parentScope__ : scope,
        }, scope).get();

        Object.defineProperty(childScope, itemName, {
            get:() => {
                return this.model[index];
            }
        });

        const node = document.importNode(this.template.content, true).firstElementChild;
        bindNode(node, childScope, true);

        this.itemNodeList[index] = node;
        this.itemScopeList[index] = childScope;

        return node;
    },

    /**
     * renders one model item to the DOM
     *
     * @param  {*} model      [description]
     * @param  {ScopePrototype} scope      [description]
     * @param  {string} itemName   [description]
     * @param  {DocumentFragment} fragment   [description]
     * @param  {Node} polyParent [description]
     * @param  {Object} item       [description]
     * @param  {number} index      [description]
     * @return {void}            [description]
     */
    renderItemStable(model, scope, itemName, fragment, item, index) {
        let node = null;

        if (this.itemNodeList.has(item)) {
            node = this.itemNodeList.get(item);
            let childScope = this.itemScopeList.get(item);

            childScope.$first = index === 0;
            childScope.$last = model.length -1 === index;
            childScope.$index = index;

            childScope.update();
        } else {
            let childScope = Make({
                $first : index === 0,
                $last : model.length-1 === index,
                $index : index,
                __parentScope__ : scope,
            }, scope).get();

            childScope[itemName] = item;

            node = document.importNode(this.template.content, true).firstElementChild;
            bindNode(node, childScope, true);

            this.itemNodeList.set(item, node);
            this.itemScopeList.set(item, childScope);
        }

        fragment.appendChild(node);
    },

    updateFast(scope) {
        const [itemName, link, expression] = this.originalNodeValue.split(' ');

        this.model = parseExpression(expression, scope);

        if (link !== 'in') {
            console.console.error('DataBinding: invalide expression', this.originalNodeValue);
            return;
        }

        if (!Array.isArray(this.model)) {
            console.warn('A repeat binding can only consume arrays!', this.model);
            this.model = [];
        }

        while (this.itemNodeList.length > this.model.length) {
            const node = this.itemNodeList.pop();
            const scope = this.itemScopeList.pop();

            node.parentNode.removeChild(node);
            scope.__destroy__();
        }

        while (this.itemNodeList.length < this.model.length) {
            const marker = this.itemNodeList.length ? this.itemNodeList[this.itemNodeList.length-1] : this.marker;

            const node = this.renderItemFast(scope, itemName, this.itemNodeList.length);

            if (marker.nextElementSibling) {
                marker.parentNode.insertBefore(node, marker.nextElementSibling);
            } else {
                marker.parentNode.appendChild(node);
            }
        }
    },

    updateStable(scope) {
        let [itemName, link, expression] = this.originalNodeValue.split(' ');
        let model = parseExpression(expression, scope);
        let dirty = false;

        if (link !== 'in') {
            console.console.error('DataBinding: invalide expression', this.originalNodeValue);
            return;
        }

        if (!Array.isArray(model)) {
            console.warn('A repeat binding can only consume arrays!', model);
            model = [];
        }

        dirty = this.modelBackup.length !== model.length;

        if (!dirty) {
            for (let i = 0; i < model.length; i++) {
                if (model[i] !== this.modelBackup[i]) {
                    dirty = true;
                    break;
                }
            }
        }

        if (dirty) {
            // clean out items that where removed.
            this.modelBackup.forEach(item => {
                if (model.indexOf(item) < 0) {
                    const node = this.itemNodeList.get(item);

                    // if the node doesn't exist something went totally wrong... but it happens :/
                    if (node) {
                        this.marker.parentNode.removeChild(node);
                    }

                    this.itemScopeList.delete(item);
                    this.itemNodeList.delete(item);
                }
            });

            this.modelBackup = model.slice();

            if (window.Polymer) {
                window.Polymer.dom.flush();
            }

            /** @type {DocumentFragment} */
            const fragment = document.createDocumentFragment();

            model.forEach(this.renderItemStable.bind(this, model, scope, itemName, fragment));

            if (this.marker.nextElementSibling) {
                this.marker.parentNode.insertBefore(fragment, this.marker.nextElementSibling);
            } else {
                this.marker.parentNode.appendChild(fragment);
            }
        }
    },

    update(scope) {
        if (this.fast) {
            return this.updateFast(scope);
        } else {
            return this.updateStable(scope);
        }
    },

    destory() {
        let count = this.modelBackup.reduce((prev, item) => {
            let [scopes, bindings] = prev;
            let scope = this.itemScopeList.get(item);
            let [scopes_add, bindings_add] = scope.__destroy__(true);

            return [scopes + scopes_add, bindings + bindings_add];
        }, [0, 0]);

        this.itemScopeList = null;
        this.itemNodeList = null;

        return count;
    },

    __proto__: BindingApi().Binding,
};

BindingApi().registerBinding(TemplateRepeatBinding);

export default TemplateRepeatBinding;
<|endoftext|>


<commit_before>
var expect from 'chai').expect;
let sinon from 'sinon/pkg/sinon.js');
import React from 'react';
var ReactDOM from 'react-dom');
var KeenViz from '../../../../../lib/js/app/components/explorer/visualization/keen_viz.js');
var TestUtils from 'react-addons-test-utils');
var ExplorerUtils from '../../../../../lib/js/app/utils/ExplorerUtils');
var TestHelpers from '../../../../support/TestHelpers');
var $R from 'rquery')(_, React, ReactDOM, TestUtils);

describe('components/explorer/visualization/keen_viz', () => {

  beforeEach(() => {
    this.model = TestHelpers.createExplorerModel();
    this.dataviz = TestHelpers.createDataviz();
    this.exportToCsv = () => { return this; }
    this.component = TestUtils.renderIntoDocument(<KeenViz model={this.model} dataviz={this.dataviz} exportToCsv={this.exportToCsv}/>);
    this.exportToCsvStub = sinon.stub(this, 'exportToCsv');
  });

  describe('export to csv button', () => {
    it('export to csv button is not shown when chart type is different than table', () => {
    	assert.lengthOf($R(this.component).find('[role="export-table"]').components, 0);
    });
    it('export to csv button is shown when char type table is selected', () => {
      this.model.metadata.visualization.chart_type = 'table';
      this.component = TestUtils.renderIntoDocument(<KeenViz model={this.model} dataviz={this.dataviz} exportToCsv={this.exportToCsv}/>);
      assert.lengthOf($R(this.component).find('[role="export-table"]').components, 1);
    });
    it('export to csv button calls export to csv function', () => {
      this.model.metadata.visualization.chart_type = 'table';
      this.component = TestUtils.renderIntoDocument(<KeenViz model={this.model} dataviz={this.dataviz} exportToCsv={this.exportToCsv}/>);
      TestUtils.Simulate.click($R(this.component).find('[role="export-table"]').components[0]);
      sinon.assert.called(this.exportToCsvStub);
    });
  });
});
<commit_msg>fix test keen viz<commit_after>
import React from 'react';
import ReactDOM from 'react-dom';
import KeenViz from '../../../../../lib/js/app/components/explorer/visualization/keen_viz.js';
import TestUtils from 'react-addons-test-utils';
import ExplorerUtils from '../../../../../lib/js/app/utils/ExplorerUtils';
import TestHelpers from '../../../../support/TestHelpers';
import rquery from 'rquery';

const $R = rquery(_, React, ReactDOM, TestUtils);

describe('components/explorer/visualization/keen_viz', () => {
  let model;
  let dataviz;
  let exportToCsv;
  let exportToCsvStub;
  let component;

  beforeEach(() => {
    model = TestHelpers.createExplorerModel();
    dataviz = TestHelpers.createDataviz();
    exportToCsv = jest.fn();
    component = TestUtils.renderIntoDocument(<KeenViz model={model} dataviz={dataviz} exportToCsv={exportToCsv}/>);
    exportToCsvStub = jest.fn();
  });

  describe('export to csv button', () => {
    it('export to csv button is not shown when chart type is different than table', () => {
    	expect($R(component).find('[role="export-table"]').components).toHaveLength(0);
    });
    it('export to csv button is shown when char type table is selected', () => {
      model.metadata.visualization.chart_type = 'table';
      component = TestUtils.renderIntoDocument(<KeenViz model={model} dataviz={dataviz} exportToCsv={exportToCsv}/>);
      expect($R(component).find('[role="export-table"]').components).toHaveLength(1);
    });
    it('export to csv button calls export to csv function', () => {
      model.metadata.visualization.chart_type = 'table';
      component = TestUtils.renderIntoDocument(<KeenViz model={model} dataviz={dataviz} exportToCsv={exportToCsv}/>);
      TestUtils.Simulate.click($R(component).find('[role="export-table"]').components[0]);
      expect(exportToCsv).toBeCalled();
    });
  });
});
<|endoftext|>


<commit_before>// # module: Dollphie.utils.pretty-printer
//
// An implementation of Wadler's Pretty Printer
// (http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)

// -- Dependencies -----------------------------------------------------
var { Base }  = require('adt-simple');
var { curry } = require('core.lambda');

// -- Data structures --------------------------------------------------
// Internal representation of a document
union DOC {
  NIL,
  CONCAT(DOC, DOC),
  NEST(Number, DOC),
  TEXT(String),
  LINE,
  UNION(DOC, DOC)
} deriving (Base)

// Exported representation of a document
union Doc {
  Nil,
  Text(String, Doc),
  Line(Number, Doc)
} deriving (Base)

// -- Helpers ----------------------------------------------------------
// @type: Int, String → String
function repeat(n, s) {
  return Array(n + 1).join(s)
}

// @type: DOC → DOC
function flatten {
  NIL            => NIL,
  CONCAT(a, b)   => CONCAT(flatten(a), flatten(b)),
  NEST(depth, a) => NEST(depth, flatten(a)),
  TEXT(s)        => TEXT(s),
  LINE           => TEXT(""),
  UNION(a, b)    => flatten(a),
  a              => (function(){ throw new Error("No match: " + a) })();
}

// @type: Int, Int, DOC → DOC
function best(width, indentation, doc) {
  return go(width, indentation, [[0, doc]]);

  // @type: Int, Int, (Int, DOC) → Doc
  function go(w, k, x) {
    return match x {
      []                         => Nil,
      [[i, NIL], ...xs]          => go(w, k, xs),
      [[i, CONCAT(x, y)], ...xs] => go(w, k, [[i, x], [i, y]] +++ xs),
      [[i, NEST(j, x)], ...xs]   => go(w, k, [[i + j, x]] +++ xs),
      [[i, TEXT(s)], ...xs]      => Text(s, go(w, k + s.length, xs)),
      [[i, LINE], ...xs]         => Line(i, go(w, i, xs)),
      [[i, UNION(x, y)], ...xs]  => better(w, k, go(w, k, [[i, x]] +++ xs),
                                                 λ[go(w, k, [[i, y]] +++ xs)])
    }
  }

  // @type: Int, Int, Doc, (Unit → Doc) → Doc
  function better(w, k, x, y) {
    return fits(w - k, x)? x : y()
  }

  // @type: Int, Doc → Boolean
  function fits {
    (w, x) if w < 0 => false,
    (w, Nil)        => true,
    (w, Text(s, x)) => fits(w - s.length, x),
    (w, Line(i, x)) => true
  }
}

// @type: DOC, DOC → DOC
function horizontalConcat(x, y) {
  return concat(concat(x, text(" ")), y)
}

// @type: DOC, DOC → DOC
function verticalConcat(x, y) {
  return concat(concat(x, line()), y)
}

// @type: String → Array(String)
function words(s) {
  return s.split(/\s+/)
}

// -- Combinators ------------------------------------------------------
// @type: Unit → DOC
function nil() {
  return NIL
}

// @type: DOC → DOC → DOC
function concat(a, b) {
  return CONCAT(a, b)
}

// @type: Int → DOC → DOC
function nest(depth, a) {
  return NEST(depth, a)
}

// @type: String → DOC
function text(s) {
  return TEXT(s)
}

// @type: Unit → DOC
function line() {
  return LINE
}

// @type: DOC → DOC
function group(a) {
  return UNION(flatten(a), a)
}

// @type: Doc → String
function layout {
  Nil        => "",
  Text(s, a) => s + layout(a),
  Line(i, a) => '\n' + repeat(i, ' ') + layout(a)
}

// @type: Int → DOC → String
function pretty(width, doc) {
  return layout(best(width, 0, doc))
}

// @type: (DOC, DOC → DOC) → Array(DOC) → DOC
function foldDoc {
  (f, [])         => nil(),
  (f, [x])        => x,
  (f, [x, ...xs]) => f(x, foldDoc(f, xs))
}

// @type: Array(DOC) → DOC
function spread(xs) {
  return foldDoc(horizontalConcat, xs)
}

// @type: Array(DOC) → DOC
function stack(xs) {
  return foldDoc(verticalConcat, xs)
}

// @type: Int → DOC → DOC → DOC → DOC
function bracket(indent, left, x, right) {
  return group(
    spread(
      text(l),
      nest(indent, concat(line(), x)),
      line(),
      text(r)
    )
  )
}

// @type: DOC → DOC → DOC
function join(x, y) {
  return concat(concat(x, UNION(text(" "), line())), y)
}

// @type: Array(DOC) → DOC
function fillWords(s) {
  return foldDoc(join, words(s).map(text))
}

// @type: Array(DOC) → DOC
function fill {
  []            => nil(),
  [x]           => x,
  [x, y, ...zs] => UNION(horizontalConcat(flatten(x), fill([flatten(y)] +++ zs)),
                         verticalConcat(x, fill([y] +++ zs)))
}

// -- Exports ----------------------------------------------------------
module.exports = {
  nil       : nil,
  concat    : curry(2, concat),
  nest      : curry(2, nest),
  text      : text,
  line      : line,
  group     : group,
  pretty    : curry(2, pretty),
  foldDoc   : curry(2, foldDoc),
  spread    : spread,
  stack     : stack,
  bracket   : curry(4, bracket),
  join      : curry(2, join),
  fillWords : fillWords,
  fill      : fill
}
<commit_msg>Fixes flatten and bracket<commit_after>// # module: Dollphie.utils.pretty-printer
//
// An implementation of Wadler's Pretty Printer
// (http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)

// -- Dependencies -----------------------------------------------------
var { Base }  = require('adt-simple');
var { curry } = require('core.lambda');

// -- Data structures --------------------------------------------------
// Internal representation of a document
union DOC {
  NIL,
  CONCAT(DOC, DOC),
  NEST(Number, DOC),
  TEXT(String),
  LINE,
  UNION(DOC, DOC)
} deriving (Base)

// Exported representation of a document
union Doc {
  Nil,
  Text(String, Doc),
  Line(Number, Doc)
} deriving (Base)

// -- Helpers ----------------------------------------------------------
// @type: Int, String → String
function repeat(n, s) {
  return Array(n + 1).join(s)
}

// @type: DOC → DOC
function flatten {
  NIL            => NIL,
  CONCAT(a, b)   => CONCAT(flatten(a), flatten(b)),
  NEST(depth, a) => NEST(depth, flatten(a)),
  TEXT(s)        => TEXT(s),
  LINE           => TEXT(" "),
  UNION(a, b)    => flatten(a),
  a              => (function(){ throw new Error("No match: " + a) })();
}

// @type: Int, Int, DOC → DOC
function best(width, indentation, doc) {
  return go(width, indentation, [[0, doc]]);

  // @type: Int, Int, (Int, DOC) → Doc
  function go(w, k, x) {
    return match x {
      []                         => Nil,
      [[i, NIL], ...xs]          => go(w, k, xs),
      [[i, CONCAT(x, y)], ...xs] => go(w, k, [[i, x], [i, y]] +++ xs),
      [[i, NEST(j, x)], ...xs]   => go(w, k, [[i + j, x]] +++ xs),
      [[i, TEXT(s)], ...xs]      => Text(s, go(w, k + s.length, xs)),
      [[i, LINE], ...xs]         => Line(i, go(w, i, xs)),
      [[i, UNION(x, y)], ...xs]  => better(w, k, go(w, k, [[i, x]] +++ xs),
                                                 λ[go(w, k, [[i, y]] +++ xs)])
    }
  }

  // @type: Int, Int, Doc, (Unit → Doc) → Doc
  function better(w, k, x, y) {
    return fits(w - k, x)? x : y()
  }

  // @type: Int, Doc → Boolean
  function fits {
    (w, x) if w < 0 => false,
    (w, Nil)        => true,
    (w, Text(s, x)) => fits(w - s.length, x),
    (w, Line(i, x)) => true
  }
}

// @type: DOC, DOC → DOC
function horizontalConcat(x, y) {
  return concat(concat(x, text(" ")), y)
}

// @type: DOC, DOC → DOC
function verticalConcat(x, y) {
  return concat(concat(x, line()), y)
}

// @type: String → Array(String)
function words(s) {
  return s.split(/\s+/)
}

// -- Combinators ------------------------------------------------------
// @type: Unit → DOC
function nil() {
  return NIL
}

// @type: DOC → DOC → DOC
function concat(a, b) {
  return CONCAT(a, b)
}

// @type: Int → DOC → DOC
function nest(depth, a) {
  return NEST(depth, a)
}

// @type: String → DOC
function text(s) {
  return TEXT(s)
}

// @type: Unit → DOC
function line() {
  return LINE
}

// @type: DOC → DOC
function group(a) {
  return UNION(flatten(a), a)
}

// @type: Doc → String
function layout {
  Nil        => "",
  Text(s, a) => s + layout(a),
  Line(i, a) => '\n' + repeat(i, ' ') + layout(a)
}

// @type: Int → DOC → String
function pretty(width, doc) {
  return layout(best(width, 0, doc))
}

// @type: (DOC, DOC → DOC) → Array(DOC) → DOC
function foldDoc {
  (f, [])         => nil(),
  (f, [x])        => x,
  (f, [x, ...xs]) => f(x, foldDoc(f, xs))
}

// @type: Array(DOC) → DOC
function spread(xs) {
  return foldDoc(horizontalConcat, xs)
}

// @type: Array(DOC) → DOC
function stack(xs) {
  return foldDoc(verticalConcat, xs)
}

// @type: Int → DOC → DOC → DOC → DOC
function bracket(indent, left, x, right) {
  return group(
    spread([
      text(left),
      nest(indent, concat(line(), x)),
      line(),
      text(right)
    ])
  )
}

// @type: DOC → DOC → DOC
function join(x, y) {
  return concat(concat(x, UNION(text(" "), line())), y)
}

// @type: Array(DOC) → DOC
function fillWords(s) {
  return foldDoc(join, words(s).map(text))
}

// @type: Array(DOC) → DOC
function fill {
  []            => nil(),
  [x]           => x,
  [x, y, ...zs] => UNION(horizontalConcat(flatten(x), fill([flatten(y)] +++ zs)),
                         verticalConcat(x, fill([y] +++ zs)))
}

// -- Exports ----------------------------------------------------------
module.exports = {
  nil       : nil,
  concat    : curry(2, concat),
  nest      : curry(2, nest),
  text      : text,
  line      : line,
  group     : group,
  pretty    : curry(2, pretty),
  foldDoc   : curry(2, foldDoc),
  spread    : spread,
  stack     : stack,
  bracket   : curry(4, bracket),
  join      : curry(2, join),
  fillWords : fillWords,
  fill      : fill
}
<|endoftext|>


<commit_before>var step = require('step');
var assert = require('assert');
var _ = require('underscore');
var NamedMapsCacheEntry = require('../cache/model/named_maps_entry');

var cors = require('../middleware/cors');
var userMiddleware = require('../middleware/user');
var allowQueryParams = require('../middleware/allow-query-params');
var vectorError = require('../middleware/vector-error');

function NamedMapsController(prepareContext, namedMapProviderCache, tileBackend, previewBackend,
                             surrogateKeysCache, tablesExtentApi, metadataBackend) {
    this.namedMapProviderCache = namedMapProviderCache;
    this.tileBackend = tileBackend;
    this.previewBackend = previewBackend;
    this.surrogateKeysCache = surrogateKeysCache;
    this.tablesExtentApi = tablesExtentApi;
    this.metadataBackend = metadataBackend;
    this.prepareContext = prepareContext;
}

module.exports = NamedMapsController;

NamedMapsController.prototype.register = function(app) {
    app.get(
        app.base_url_templated + '/:template_id/:layer/:z/:x/:y.(:format)',
        cors(),
        userMiddleware,
        this.prepareContext,
        this.getNamedMapProvider(),
        this.tile.bind(this),
        vectorError()
    );

    app.get(
        app.base_url_mapconfig + '/static/named/:template_id/:width/:height.:format',
        cors(),
        userMiddleware,
        allowQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']),
        this.prepareContext,
        this.getNamedMapProvider(),
        this.staticMap.bind(this)
    );
};

NamedMapsController.prototype.getNamedMapProvider = function () {
    return function getNamedMapProviderMiddleware (req, res, next) {
        const { user } = res.locals;
        const { config, auth_token } = req.query;
        const { template_id } = req.params;

        this.namedMapProviderCache.get(user, template_id, config, auth_token, res.locals, (err, namedMapProvider) => {
            if (err) {
                return next(err);
            }

            res.locals.namedMapProvider = namedMapProvider;

            next();
        });
    }.bind(this);
};

NamedMapsController.prototype.sendResponse = function(req, res, body, headers, namedMapProvider) {
    this.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(res.locals.user, namedMapProvider.getTemplateName()));
    res.set('Content-Type', headers['content-type'] || headers['Content-Type'] || 'image/png');
    res.set('Cache-Control', 'public,max-age=7200,must-revalidate');

    var self = this;

    step(
        function getAffectedTablesAndLastUpdatedTime() {
            namedMapProvider.getAffectedTablesAndLastUpdatedTime(this);
        },
        function sendResponse(err, result) {
            req.profiler.done('affectedTables');
            if (err) {
                global.logger.log('ERROR generating cache channel: ' + err);
            }
            if (!result || !!result.tables) {
                // we increase cache control as we can invalidate it
                res.set('Cache-Control', 'public,max-age=31536000');

                var lastModifiedDate;
                if (Number.isFinite(result.lastUpdatedTime)) {
                    lastModifiedDate = new Date(result.getLastUpdatedAt());
                } else {
                    lastModifiedDate = new Date();
                }
                res.set('Last-Modified', lastModifiedDate.toUTCString());

                res.set('X-Cache-Channel', result.getCacheChannel());
                if (result.tables.length > 0) {
                    self.surrogateKeysCache.tag(res, result);
                }
            }
            res.status(200);
            res.send(body);
        }
    );
};

NamedMapsController.prototype.tile = function(req, res, next) {
    var self = this;

    const { namedMapProvider } = res.locals;

    step(
        function getTile() {
            self.tileBackend.getTile(namedMapProvider, req.params, this);
        },
        function handleImage(err, tile, headers, stats) {
            req.profiler.add(stats);

            if (err) {
                err.label = 'NAMED_MAP_TILE';
                next(err);
            } else {
                self.sendResponse(req, res, tile, headers, namedMapProvider);
            }
        }
    );
};

NamedMapsController.prototype.staticMap = function(req, res, next) {
    var self = this;

    var cdbUser = res.locals.user;

    var format = req.params.format === 'jpg' ? 'jpeg' : 'png';
    // We force always the tile to be generated using PNG because
    // is the only format we support by now
    res.locals.format = 'png';
    res.locals.layer = res.locals.layer || 'all';

    const { namedMapProvider } = res.locals;
    step(
        function prepareLayerVisibility() {
            self.prepareLayerFilterFromPreviewLayers(cdbUser, req, res.locals, namedMapProvider, this);
        },
        function prepareImageOptions(err) {
            assert.ifError(err);
            self.getStaticImageOptions(cdbUser, res.locals, namedMapProvider, this);
        },
        function getImage(err, imageOpts) {
            assert.ifError(err);

            var width = +req.params.width;
            var height = +req.params.height;

            if (!_.isUndefined(imageOpts.zoom) && imageOpts.center) {
                self.previewBackend.getImage(
                    namedMapProvider, format, width, height, imageOpts.zoom, imageOpts.center, this);
            } else {
                self.previewBackend.getImage(
                    namedMapProvider, format, width, height, imageOpts.bounds, this);
            }
        },
        function incrementMapViews(err, image, headers, stats) {
            assert.ifError(err);

            var next = this;
            namedMapProvider.getMapConfig(function(mapConfigErr, mapConfig) {
                self.metadataBackend.incMapviewCount(cdbUser, mapConfig.obj().stat_tag, function(sErr) {
                    if (err) {
                        global.logger.log("ERROR: failed to increment mapview count for user '%s': %s", cdbUser, sErr);
                    }
                    next(err, image, headers, stats);
                });
            });
        },
        function handleImage(err, image, headers, stats) {
            req.profiler.done('render-' + format);
            req.profiler.add(stats || {});

            if (err) {
                err.label = 'STATIC_VIZ_MAP';
                next(err);
            } else {
                self.sendResponse(req, res, image, headers, namedMapProvider);
            }
        }
    );
};

NamedMapsController.prototype.prepareLayerFilterFromPreviewLayers = function (
    user,
    req,
    params,
    namedMapProvider,
    callback
) {
    var self = this;
    namedMapProvider.getTemplate(function (err, template) {
        if (err) {
            return callback(err);
        }

        if (!template || !template.view || !template.view.preview_layers) {
           return callback();
        }

        var previewLayers = template.view.preview_layers;
        var layerVisibilityFilter = [];

        template.layergroup.layers.forEach(function (layer, index) {
            if (previewLayers[''+index] !== false && previewLayers[layer.id] !== false) {
                layerVisibilityFilter.push(''+index);
            }
        });

        if (!layerVisibilityFilter.length) {
            return callback();
        }

        // overwrites 'all' default filter
        params.layer = layerVisibilityFilter.join(',');

        // recreates the provider
        self.namedMapProviderCache.get(
            user,
            req.params.template_id,
            req.query.config,
            req.query.auth_token,
            params,
            callback
        );
    });
};

var DEFAULT_ZOOM_CENTER = {
    zoom: 1,
    center: {
        lng: 0,
        lat: 0
    }
};

function numMapper(n) {
    return +n;
}

NamedMapsController.prototype.getStaticImageOptions = function(cdbUser, params, namedMapProvider, callback) {
    var self = this;

    if ([params.zoom, params.lon, params.lat].map(numMapper).every(Number.isFinite)) {
        return callback(null, {
            zoom: params.zoom,
            center: {
                lng: params.lon,
                lat: params.lat
            }
        });
    }

    if (params.bbox) {
        var bbox = params.bbox.split(',').map(numMapper);
        if (bbox.length === 4 && bbox.every(Number.isFinite)) {
            return callback(null, {
                bounds: {
                    west: bbox[0],
                    south: bbox[1],
                    east: bbox[2],
                    north: bbox[3]
                }
            });
        }
    }

    step(
        function getTemplate() {
            namedMapProvider.getTemplate(this);
        },
        function handleTemplateView(err, template) {
            assert.ifError(err);

            if (template.view) {
                var zoomCenter = templateZoomCenter(template.view);
                if (zoomCenter) {
                    if (Number.isFinite(+params.zoom)) {
                        zoomCenter.zoom = +params.zoom;
                    }
                    return zoomCenter;
                }

                var bounds = templateBounds(template.view);
                if (bounds) {
                    return bounds;
                }
            }

            return false;
        },
        function estimateBoundsIfNoImageOpts(err, imageOpts) {
            if (imageOpts) {
                return imageOpts;
            }

            var next = this;
            namedMapProvider.getAffectedTablesAndLastUpdatedTime(function(err, affectedTablesAndLastUpdate) {
                if (err) {
                    return next(null);
                }

                var affectedTables = affectedTablesAndLastUpdate.tables || [];

                if (affectedTables.length === 0) {
                    return next(null);
                }

                self.tablesExtentApi.getBounds(cdbUser, affectedTables, function(err, result) {
                    return next(null, result);
                });
            });

        },
        function returnCallback(err, imageOpts) {
            return callback(err, imageOpts || DEFAULT_ZOOM_CENTER);
        }
    );
};

function templateZoomCenter(view) {
    if (!_.isUndefined(view.zoom) && view.center) {
        return {
            zoom: view.zoom,
            center: view.center
        };
    }
    return false;
}

function templateBounds(view) {
    if (view.bounds) {
        var hasAllBounds = _.every(['west', 'south', 'east', 'north'], function(prop) {
            return Number.isFinite(view.bounds[prop]);
        });
        if (hasAllBounds) {
            return {
                bounds: {
                    west: view.bounds.west,
                    south: view.bounds.south,
                    east: view.bounds.east,
                    north: view.bounds.north
                }
            };
        } else {
            return false;
        }
    }
    return false;
}
<commit_msg>Do not use step<commit_after>var step = require('step');
var assert = require('assert');
var _ = require('underscore');
var NamedMapsCacheEntry = require('../cache/model/named_maps_entry');

var cors = require('../middleware/cors');
var userMiddleware = require('../middleware/user');
var allowQueryParams = require('../middleware/allow-query-params');
var vectorError = require('../middleware/vector-error');

function NamedMapsController(prepareContext, namedMapProviderCache, tileBackend, previewBackend,
                             surrogateKeysCache, tablesExtentApi, metadataBackend) {
    this.namedMapProviderCache = namedMapProviderCache;
    this.tileBackend = tileBackend;
    this.previewBackend = previewBackend;
    this.surrogateKeysCache = surrogateKeysCache;
    this.tablesExtentApi = tablesExtentApi;
    this.metadataBackend = metadataBackend;
    this.prepareContext = prepareContext;
}

module.exports = NamedMapsController;

NamedMapsController.prototype.register = function(app) {
    app.get(
        app.base_url_templated + '/:template_id/:layer/:z/:x/:y.(:format)',
        cors(),
        userMiddleware,
        this.prepareContext,
        this.getNamedMapProvider(),
        this.tile.bind(this),
        vectorError()
    );

    app.get(
        app.base_url_mapconfig + '/static/named/:template_id/:width/:height.:format',
        cors(),
        userMiddleware,
        allowQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']),
        this.prepareContext,
        this.getNamedMapProvider(),
        this.staticMap.bind(this)
    );
};

NamedMapsController.prototype.getNamedMapProvider = function () {
    return function getNamedMapProviderMiddleware (req, res, next) {
        const { user } = res.locals;
        const { config, auth_token } = req.query;
        const { template_id } = req.params;

        this.namedMapProviderCache.get(user, template_id, config, auth_token, res.locals, (err, namedMapProvider) => {
            if (err) {
                return next(err);
            }

            res.locals.namedMapProvider = namedMapProvider;

            next();
        });
    }.bind(this);
};

NamedMapsController.prototype.sendResponse = function(req, res, body, headers, namedMapProvider) {
    this.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(res.locals.user, namedMapProvider.getTemplateName()));
    res.set('Content-Type', headers['content-type'] || headers['Content-Type'] || 'image/png');
    res.set('Cache-Control', 'public,max-age=7200,must-revalidate');

    var self = this;

    step(
        function getAffectedTablesAndLastUpdatedTime() {
            namedMapProvider.getAffectedTablesAndLastUpdatedTime(this);
        },
        function sendResponse(err, result) {
            req.profiler.done('affectedTables');
            if (err) {
                global.logger.log('ERROR generating cache channel: ' + err);
            }
            if (!result || !!result.tables) {
                // we increase cache control as we can invalidate it
                res.set('Cache-Control', 'public,max-age=31536000');

                var lastModifiedDate;
                if (Number.isFinite(result.lastUpdatedTime)) {
                    lastModifiedDate = new Date(result.getLastUpdatedAt());
                } else {
                    lastModifiedDate = new Date();
                }
                res.set('Last-Modified', lastModifiedDate.toUTCString());

                res.set('X-Cache-Channel', result.getCacheChannel());
                if (result.tables.length > 0) {
                    self.surrogateKeysCache.tag(res, result);
                }
            }
            res.status(200);
            res.send(body);
        }
    );
};

NamedMapsController.prototype.tile = function(req, res, next) {
    const { namedMapProvider } = res.locals;

    this.tileBackend.getTile(namedMapProvider, req.params, (err, tile, headers, stats) => {
        req.profiler.add(stats);

        if (err) {
            err.label = 'NAMED_MAP_TILE';
            return next(err);
        }

        this.sendResponse(req, res, tile, headers, namedMapProvider);
    });
};

NamedMapsController.prototype.staticMap = function(req, res, next) {
    var self = this;

    var cdbUser = res.locals.user;

    var format = req.params.format === 'jpg' ? 'jpeg' : 'png';
    // We force always the tile to be generated using PNG because
    // is the only format we support by now
    res.locals.format = 'png';
    res.locals.layer = res.locals.layer || 'all';

    const { namedMapProvider } = res.locals;
    step(
        function prepareLayerVisibility() {
            self.prepareLayerFilterFromPreviewLayers(cdbUser, req, res.locals, namedMapProvider, this);
        },
        function prepareImageOptions(err) {
            assert.ifError(err);
            self.getStaticImageOptions(cdbUser, res.locals, namedMapProvider, this);
        },
        function getImage(err, imageOpts) {
            assert.ifError(err);

            var width = +req.params.width;
            var height = +req.params.height;

            if (!_.isUndefined(imageOpts.zoom) && imageOpts.center) {
                self.previewBackend.getImage(
                    namedMapProvider, format, width, height, imageOpts.zoom, imageOpts.center, this);
            } else {
                self.previewBackend.getImage(
                    namedMapProvider, format, width, height, imageOpts.bounds, this);
            }
        },
        function incrementMapViews(err, image, headers, stats) {
            assert.ifError(err);

            var next = this;
            namedMapProvider.getMapConfig(function(mapConfigErr, mapConfig) {
                self.metadataBackend.incMapviewCount(cdbUser, mapConfig.obj().stat_tag, function(sErr) {
                    if (err) {
                        global.logger.log("ERROR: failed to increment mapview count for user '%s': %s", cdbUser, sErr);
                    }
                    next(err, image, headers, stats);
                });
            });
        },
        function handleImage(err, image, headers, stats) {
            req.profiler.done('render-' + format);
            req.profiler.add(stats || {});

            if (err) {
                err.label = 'STATIC_VIZ_MAP';
                next(err);
            } else {
                self.sendResponse(req, res, image, headers, namedMapProvider);
            }
        }
    );
};

NamedMapsController.prototype.prepareLayerFilterFromPreviewLayers = function (
    user,
    req,
    params,
    namedMapProvider,
    callback
) {
    var self = this;
    namedMapProvider.getTemplate(function (err, template) {
        if (err) {
            return callback(err);
        }

        if (!template || !template.view || !template.view.preview_layers) {
           return callback();
        }

        var previewLayers = template.view.preview_layers;
        var layerVisibilityFilter = [];

        template.layergroup.layers.forEach(function (layer, index) {
            if (previewLayers[''+index] !== false && previewLayers[layer.id] !== false) {
                layerVisibilityFilter.push(''+index);
            }
        });

        if (!layerVisibilityFilter.length) {
            return callback();
        }

        // overwrites 'all' default filter
        params.layer = layerVisibilityFilter.join(',');

        // recreates the provider
        self.namedMapProviderCache.get(
            user,
            req.params.template_id,
            req.query.config,
            req.query.auth_token,
            params,
            callback
        );
    });
};

var DEFAULT_ZOOM_CENTER = {
    zoom: 1,
    center: {
        lng: 0,
        lat: 0
    }
};

function numMapper(n) {
    return +n;
}

NamedMapsController.prototype.getStaticImageOptions = function(cdbUser, params, namedMapProvider, callback) {
    var self = this;

    if ([params.zoom, params.lon, params.lat].map(numMapper).every(Number.isFinite)) {
        return callback(null, {
            zoom: params.zoom,
            center: {
                lng: params.lon,
                lat: params.lat
            }
        });
    }

    if (params.bbox) {
        var bbox = params.bbox.split(',').map(numMapper);
        if (bbox.length === 4 && bbox.every(Number.isFinite)) {
            return callback(null, {
                bounds: {
                    west: bbox[0],
                    south: bbox[1],
                    east: bbox[2],
                    north: bbox[3]
                }
            });
        }
    }

    step(
        function getTemplate() {
            namedMapProvider.getTemplate(this);
        },
        function handleTemplateView(err, template) {
            assert.ifError(err);

            if (template.view) {
                var zoomCenter = templateZoomCenter(template.view);
                if (zoomCenter) {
                    if (Number.isFinite(+params.zoom)) {
                        zoomCenter.zoom = +params.zoom;
                    }
                    return zoomCenter;
                }

                var bounds = templateBounds(template.view);
                if (bounds) {
                    return bounds;
                }
            }

            return false;
        },
        function estimateBoundsIfNoImageOpts(err, imageOpts) {
            if (imageOpts) {
                return imageOpts;
            }

            var next = this;
            namedMapProvider.getAffectedTablesAndLastUpdatedTime(function(err, affectedTablesAndLastUpdate) {
                if (err) {
                    return next(null);
                }

                var affectedTables = affectedTablesAndLastUpdate.tables || [];

                if (affectedTables.length === 0) {
                    return next(null);
                }

                self.tablesExtentApi.getBounds(cdbUser, affectedTables, function(err, result) {
                    return next(null, result);
                });
            });

        },
        function returnCallback(err, imageOpts) {
            return callback(err, imageOpts || DEFAULT_ZOOM_CENTER);
        }
    );
};

function templateZoomCenter(view) {
    if (!_.isUndefined(view.zoom) && view.center) {
        return {
            zoom: view.zoom,
            center: view.center
        };
    }
    return false;
}

function templateBounds(view) {
    if (view.bounds) {
        var hasAllBounds = _.every(['west', 'south', 'east', 'north'], function(prop) {
            return Number.isFinite(view.bounds[prop]);
        });
        if (hasAllBounds) {
            return {
                bounds: {
                    west: view.bounds.west,
                    south: view.bounds.south,
                    east: view.bounds.east,
                    north: view.bounds.north
                }
            };
        } else {
            return false;
        }
    }
    return false;
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 ncph Authors
// This file is free software: You can redistribute it and/or modify it under
// the terms of the GNU AGPLv3 or later. See COPYING for details.

// Implementation of the Fischer-Yates-Durstenfeld O(n) shuffle algorithm. This
// variant is non-destructive (does not modify the input array), and takes the
// random number generator as a parameter, for deterministic operation.
//
//   https://en.wikipedia.org/wiki/Fisher-Yates_shuffle
//
function shuffle(orig, rng) {
  arr = orig.slice(0);
  for (i = arr.length-1; i > 0; i--) {
    var j = Math.floor(rng.gen() * (i+1));
    var tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
  }
  return arr;
}

// Implementation of a simple linear congruential generator, using the
// parameters from Teukolsky-Vetterling-Flannery's Numerical Recipes.
//
//   https://en.wikipedia.org/wiki/Linear_congruential_generator
//
function Lcg32(seed) {
  this.state = seed;
  this.advance(); this.advance(); this.advance();
}
Lcg32.prototype.advance = function() {
  this.state = (this.state * 1664525 + 1013904223) % 0xFFFFFFFF;
}
Lcg32.prototype.gen = function() {
  this.advance();
  return this.state / 0xFFFFFFFF;
}

var clips = [
  "wicker-notthebees",
  "zandalee-inevitable",
  "firebirds-kiss",
  "driveangry-disrobe",
  "therock-gottago",
];

var clipDuration = 5000;

// Get the clip for a given Unix timestamp (deterministic).
//
// This works breaking the timestamp into blocks (the number of full sequences
// since Epoch) and index. The block is used as a seed to generate permutations.
function getClip(ts) {
  var total = Math.floor(ts / clipDuration);
  var block = Math.floor(total / clips.length);
  var index = total % clips.length;
  var permuted = shuffle(clips, new Lcg32(block));
  return permuted[index];
}

var vid = document.getElementById('vid');
var vidCounter = 0;

function changeVid() {
  vid.src = "clips/" + getClip(Date.now()) + ".mp4";
  vid.play();
  ++vidCounter;
}

window.onload = function() {
  window.setInterval(changeVid, clipDuration);
  changeVid();
};
<commit_msg>js: Run fixjsstyle.<commit_after>// Copyright (C) 2016 ncph Authors
// This file is free software: You can redistribute it and/or modify it under
// the terms of the GNU AGPLv3 or later. See COPYING for details.

// Implementation of the Fischer-Yates-Durstenfeld O(n) shuffle algorithm. This
// variant is non-destructive (does not modify the input array), and takes the
// random number generator as a parameter, for deterministic operation.
//
//   https://en.wikipedia.org/wiki/Fisher-Yates_shuffle
//
function shuffle(orig, rng) {
  arr = orig.slice(0);
  for (i = arr.length - 1; i > 0; i--) {
    var j = Math.floor(rng.gen() * (i + 1));
    var tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
  }
  return arr;
}

// Implementation of a simple linear congruential generator, using the
// parameters from Teukolsky-Vetterling-Flannery's Numerical Recipes.
//
//   https://en.wikipedia.org/wiki/Linear_congruential_generator
//
function Lcg32(seed) {
  this.state = seed;
  this.advance(); this.advance(); this.advance();
}
Lcg32.prototype.advance = function() {
  this.state = (this.state * 1664525 + 1013904223) % 0xFFFFFFFF;
};
Lcg32.prototype.gen = function() {
  this.advance();
  return this.state / 0xFFFFFFFF;
};

var clips = [
  'wicker-notthebees',
  'zandalee-inevitable',
  'firebirds-kiss',
  'driveangry-disrobe',
  'therock-gottago',
];

var clipDuration = 5000;

// Get the clip for a given Unix timestamp (deterministic).
//
// This works breaking the timestamp into blocks (the number of full sequences
// since Epoch) and index. The block is used as a seed to generate permutations.
function getClip(ts) {
  var total = Math.floor(ts / clipDuration);
  var block = Math.floor(total / clips.length);
  var index = total % clips.length;
  var permuted = shuffle(clips, new Lcg32(block));
  return permuted[index];
}

var vid = document.getElementById('vid');
var vidCounter = 0;

function changeVid() {
  vid.src = 'clips/' + getClip(Date.now()) + '.mp4';
  vid.play();
  ++vidCounter;
}

window.onload = function() {
  window.setInterval(changeVid, clipDuration);
  changeVid();
};
<|endoftext|>


<commit_before>var express = require('express');
var app = express();

var http = require('http').Server(app);
var io = require('socket.io')(http);

app.use(express.static('public'));

app.get('/', function(req, res){
  res.sendfile('river.html');
});

app.get('/canoa0', function(req, res){
  res.sendfile('river0.html');
});

app.get('/canoa1', function(req, res){
  res.sendfile('river1.html');
});

app.get('/remo0', function(req, res){
  res.sendfile('remo0.html');
});

app.get('/remo1', function(req, res){
  res.sendfile('remo1.html');
});

io.on('connection', function(socket){
  socket.on('acc0', function(msg){
    io.emit('acc0', msg);
  });
    socket.on('acc1', function(msg){
    io.emit('acc1', msg);
  });
    socket.on('click0', function(msg){
    io.emit('click0', msg);
  });
    socket.on('click1', function(msg){
    io.emit('click1', msg);
  });
});

http.listen(process.env.PORT || 3000, function(){
  console.log(`Listening on ${ process.env.PORT }`);
});

<commit_msg>change<commit_after>var express = require('express');
var app = express();

var http = require('http').Server(app);
var io = require('socket.io')(http);

app.use(express.static('public'));

app.get('/', function(req, res){
  res.sendfile('river.html');
});

app.get('/canoa0', function(req, res){
  res.sendfile('river0.html');
});

app.get('/canoa1', function(req, res){
  res.sendfile('river1.html');
});

app.get('/remo0', function(req, res){
  res.sendfile('remo0.html');
});

app.get('/remo1', function(req, res){
  res.sendfile('remo1.html');
});

io.on('connection', function(socket){
  socket.on('acc0', function(msg){
    io.emit('acc0', msg);
  });
    socket.on('acc1', function(msg){
    io.emit('acc1', msg);
  });
    socket.on('click0', function(msg){
    io.emit('click0', msg);
  });
    socket.on('click1', function(msg){
    io.emit('click1', msg);
  });
});

http.listen(process.env.PORT || 3000, function(){
  console.log('Listening on ${ process.env.PORT }');
});

<|endoftext|>