<commit_before>/**
 * Creates an instance of a block
 */
Block = function(blocks, blockType, id)
{
    var parametersRatio = 1.3;
    var defaultFont = 12;
    var defaultInputWidth = 100;
    var self = this;

    // Custom description
    this.description = null;

    // Width
    this.defaultWidth = blockType.size == 'small' ? 100 : 135;

    // History saved before move
    this.historySaved = false;

    // Id
    this.id = id;

    // Do I have focus ?
    this.hasFocus = false;

    // Division (object)
    this.div = null;

    // Is the user dragging ?
    this.drag = null;

    // Last scale
    this.lastScale = null;

    // Parameters
    this.parameters = null;
    this.parametersManager = null;

    // Position
    this.x = 0;
    this.y = 0;

    // I/Os cardinality
    this.ios = {};

    // Which IO has focus ?
    this.focusedIo = null;

    // Edges
    this.edges = {};

    // Can this block be used to break a loop ?
    this.isLoopable = function()
    {
        return blockType.loopable;
    };

    /**
     * Sets the block description to something custom
     */
    this.setDescription = function(description)
    {
        this.description = description;
        this.div.find('.description').html(description);
    };

    /**
     * Update the block parameters
     */
    this.updateParameters = function(parameters)
    {
        blocks.history.save();
        this.parameters = parameters;
    };

    /**
     * Parses the cardinality
     */
    this.parseCardinality = function(ioCard, isOutput)
    {
        var card = [0, 1];

        if (isOutput) {
            card = [0, '*'];
        }

        if (ioCard != undefined) {
            if (typeof(ioCard) != 'string') {
                card = [ioCard, ioCard];
            } else {
                tab = ioCard.split('-');
                if (tab.length == 1) {
                    card = [0, tab[0]];
                } else {
                    card = tab;
                }
            }
        }

        for (idx in card) {
            if (card[idx] != '*') {
                card[idx] = parseInt(card[idx]);
            }
        }

        return card;
    };

    /**
     * Parses a length
     */
    this.parseLength = function(length)
    {
        if (typeof(length) == 'number') {
            return length;
        }

        expression = length.split(/\./);

        if (expression.length == 2) {
            var key = expression[0];
            var operation = expression[1];

            if (operation == 'length') {
                return self.parametersManager.getParameterSize(key);
            }

            if (operation == 'value') {
                return parseInt(self.parameters[key]);
            }
        }
    };

    /**
     * Html entities on a string
     */
    this.htmlentities = function(str)
    {
        str = str.replace(/</, '<');
        str = str.replace(/>/, '>');
        return str;
    }

    /**
     * Set the infos of the block
     */
    this.setInfos = function(html)
    {
        self.div.find('.infos').html(html);
    };

    /**
     * Returns the render of the block
     */
    this.getHtml = function()
    {
	self.ios = {};
        var title = blockType.name + '#' + self.id + '';

        var parameters = self.parametersManager.fields;
        for (k in parameters) {
            var parameter = parameters[k];
            if (parameter.asTitle) {
                title = parameter.getHtml(self.parameters);
            }
        }

        html = '

';
        html += '
'+title+'

';
        html += '

';

        if (self.description) {
            html += '
' + self.description + '
';
        } else {
            if (blockType.description) {
                html += '
' + blockType.description + '
';
            } else {
                html += '
No description
';
            }
        }
        html += '

';
        html += '

';

        var parameters = self.parametersManager.fields;
        for (k in parameters) {
            var parameter = parameters[k];
            var parameterHtml = parameter.getHtml(self.parameters);
            var key = 'param_'+k;
            if (!parameter.asTitle && (!blocks.compactMode || (self.edges[key]!=undefined && self.edges[key].length>0))) {
                var card = self.parseCardinality(parameter.card, false)
                self.ios[key] = card;
                if (parameterHtml) {
                    html += '
';
                    if (card[1] == '*' || card[1] > 0) {
                        html += '

';
                    }
                    html += parameterHtml+'
';
                }
            }
        }

        // Handling inputs & outputs
        handle = function(key) {
            html += '
';

            for (k in blockType[key+'s']) {
                var isVariadic = false;
                var io = blockType[key+'s'][k];

                var size = 1;
                if (io.length != undefined) {
                    isVariadic = true;
                    size = self.parseLength(io.length);
                }

                for (x=0; x<size; x++) {
                    var ion = key + '_' + k;
                    var label = io.name.replace('#', x+1);

                    if (io.dynamicLabel) {
                        label = String(eval(io.dynamicLabel));
                    }

                    if (isVariadic) {
                        ion += '_' + x;
                    }

                    // Generating HTML
                    html += '

' + self.htmlentities(label) + '
';

                    // Setting cardinality
                    self.ios[ion] = self.parseCardinality(io.card, (key == 'output'));
                }
            }
                html += '
';
        };

        handle('input');
        handle('output');
	this.checkEdges();

        return html;
    };

    /**
     * Render the block
     */
    this.render = function()
    {
        this.lastScale = null;
        this.hasFocus = false;
        this.div.html(this.getHtml());
        this.initListeners();
        this.redraw();
    };

    /**
     * Creates and inject the div
     */
    this.create = function(div)
    {
        html = '

'

        div.append(html);
        this.div = div.find('#block' + this.id);

        if (this.parametersManager == undefined) {
            this.parametersManager = new ParametersManager(blockType, this);
	    if (this.parameters == null) {
                this.parameters = this.parametersManager.getDefaults();
	    }
        }

        this.render();
    };

    /**
     * Sets the position and the scale of the block
     */
    this.redraw = function(selected)
    {
        // Setting the position
        self.div.css('margin-left', blocks.center.x+self.x*blocks.scale+'px');
        self.div.css('margin-top', blocks.center.y+self.y*blocks.scale+'px');

        // Rescaling
        if (this.lastScale != blocks.scale) {
            self.div.css('font-size', Math.round(blocks.scale*defaultFont)+'px');
            self.div.css('width', Math.round(blocks.scale*self.defaultWidth)+'px');

            var size = Math.round(12*blocks.scale);
            self.div.find('.circle').css('width', size+'px');
            self.div.find('.circle').css('height', size+'px');
            self.div.find('.circle').css('background-size', size+'px '+size+'px');

            self.div.find('.inputs, .outputs').width(self.div.width()/2-10);

            self.cssParameters();
            this.lastScale = blocks.scale
        }

        // Changing the circle rendering
        for (k in self.ios) {
            var circle = self.div.find('.' + k + ' .circle');

            circle.removeClass('io_active');
            if (self.edges[k] != undefined && self.edges[k].length) {
                circle.addClass('io_active');
            }
        }

        // Updating the parameters manager div
        this.parametersManager.div = this.div.find('.parameters');

        // Is selected ?
        self.div.removeClass('block_selected');
        if (selected) {
            self.div.addClass('block_selected');
        }
    };

    /**
     * Sets the css for the inputs
     */
    this.cssParameters = function()
    {
        self.div.find('input').css('font-size', Math.round(blocks.scale*defaultFont)+'px');
        self.div.find('input').css('width', Math.round(blocks.scale*defaultInputWidth)+'px');
        self.div.find('.parameters').css('width', parametersRatio*Math.round(blocks.scale*self.defaultWidth)+'px');
    };

    /**
     * Init the function listeners
     */
    this.initListeners = function()
    {
        // Drag & drop the block
        self.div.find('.blockTitle').mousedown(function(event) {
            if (event.which == 1) {
                self.historySaved = false;
                self.drag = [blocks.mouseX/blocks.scale-self.x, blocks.mouseY/blocks.scale-self.y];
            }
        });

        // Handle focus
        self.div.hover(function() {
            self.hasFocus = true;
        }, function() {
            self.hasFocus = false;
        });

        // Handle focus on the I/Os
        self.div.find('.input, .output, .parameter').hover(function() {
            self.focusedIo = $(this).attr('rel');
        }, function() {
            self.focusedIo = null;
        });

        // Dragging
        $('html').mousemove(function(evt) {
            if (self.drag) {
                if (!self.historySaved) {
                    blocks.history.save();
                    self.historySaved = true;
                }
                self.x = (blocks.mouseX/blocks.scale-self.drag[0]);
                self.y = (blocks.mouseY/blocks.scale-self.drag[1]);
                blocks.redraw();
            }
        });

        // Drag the block
        $('html').mouseup(function() {
            self.drag = null;
        });

        // Draw a link
        self.div.find('.input, .output, .parameter').mousedown(function(event) {
            if (event.which == 1) {
                blocks.beginLink(self, $(this).attr('rel'));
                event.preventDefault();
            }
        });

        // Handle the parameters
        self.div.find('.gear').click(function() {
            self.parametersManager.toggle();
            self.cssParameters();
        });

        // Handle the deletion
        self.div.find('.delete').click(function() {
            blocks.removeBlock(blocks.getBlockId(self));
        });

        // Show the description
        self.div.find('.info').hover(function() {
            self.div.find('.description').show();
        }, function() {
            self.div.find('.description').hide();
        });
    };

    /**
     * Gets the link position for an input or output
     */
    this.linkPositionFor = function(io)
    {
        try {
            div = self.div.find('.' + io + ' .circle')

            var x = (div.offset().left-blocks.div.offset().left)+div.width()/2;
            var y = (div.offset().top-blocks.div.offset().top)+div.height()/2;
        } catch (error) {
            throw 'Unable to find link position for '+io+' ('+error+')';
        }

        return {x: x, y: y};
    };

    /**
     * Can the io be linked ?
     */
    this.canLink = function(ion)
    {
        var tab = [];
        if (this.edges[ion] != undefined) {
            tab = this.edges[ion];
        }

        var card = this.ios[ion];

        if (card[1] != '*') {
            return (tab.length < card[1]);
        }

        return true;
    };

    /**
     * Add an edge
     */
    this.addEdge = function(ion, edge)
    {
        var tab = [];
        if (this.edges[ion] != undefined) {
            tab = this.edges[ion];
        }

        tab.push(edge);
        this.edges[ion] = tab;
    };

    /**
     * Check that all edges reference a valid I/O
     */
    this.checkEdges = function()
    {
	var toDelete = [];
	for (ion in this.edges) {
	    var edges = this.edges[ion];

	    if (!(ion in this.ios)) {
		for (k in edges) {
		    toDelete.push(edges[k]);
		}

		delete this.edges[ion];
	    }
	}

	for (k in toDelete) {
	    blocks.removeEdge(blocks.getEdgeId(toDelete[k]));
	}

	if (toDelete.length) {
	    blocks.redraw();
	}
    };

    /**
     * Erase an edge
     */
    this.eraseEdge = function(ion, edge)
    {
        if (this.edges[ion] != undefined) {
            for (k in this.edges[ion]) {
                if (this.edges[ion][k] == edge) {
                    arrayRemove(this.edges[ion], k);
                    break;
                }
            }
        }
    };

    /**
     * Erase the block
     */
    this.erase = function()
    {
        self.div.remove();
    };

    /**
     * Find all successors of a block, and their successors
     */
    this.allSuccessors = function()
    {
        // Blocks already explored
        var explored = {};
        var exploreList = [this];
        var ids = [this.id];
        explored[this.id] = true;

        while (exploreList.length > 0) {
            var currentBlock = exploreList.pop();

            for (key in currentBlock.edges) {
                for (i in currentBlock.edges[key]) {
                    var edge = currentBlock.edges[key][i];
                    if (edge.isLoopable()) {
                        continue;
                    }
                    fromTo = edge.fromTo();

                    if (fromTo[0] == currentBlock) {
                        target = fromTo[1];

                        if (!(target.id in explored)) {
                            explored[target.id] = true;
                            exploreList.push(target);
                            ids.push(target.id);
                        }
                    }
                }
            }
        }

        return ids;
    };

    /**
     * Exports the block to JSON
     */
    this.exportData = function()
    {
	return {
	    id: this.id,
	    x: this.x,
	    y: this.y,
	    type: blockType.name,
            module: blockType.module,
	    parameters: this.parametersManager.exportData(this.parameters)
	};
    };
};

/**
 * Imports a block from its data
 */
function BlockImport(blocks, data)
{
    for (t in blocks.blockTypes) {
	var blockType = blocks.blockTypes[t];
        var module = ('module' in data) ? data.module : null;
	if (blockType.name == data.type && blockType.module == module) {
	    var block = new Block(blocks, blockType, data.id);
	    block.x = data.x;
	    block.y = data.y;
	    block.parameters = ParametersImport(data.parameters);
	    return block;
	}
    }

    throw 'Unable to create a block of type ' + data.type;
};
<commit_msg>Fixing block title<commit_after>/**
 * Creates an instance of a block
 */
Block = function(blocks, blockType, id)
{
    var parametersRatio = 1.3;
    var defaultFont = 12;
    var defaultInputWidth = 100;
    var self = this;

    // Custom description
    this.description = null;

    // Width
    this.defaultWidth = blockType.size == 'small' ? 100 : 135;

    // History saved before move
    this.historySaved = false;

    // Id
    this.id = id;

    // Do I have focus ?
    this.hasFocus = false;

    // Division (object)
    this.div = null;

    // Is the user dragging ?
    this.drag = null;

    // Last scale
    this.lastScale = null;

    // Parameters
    this.parameters = null;
    this.parametersManager = null;

    // Position
    this.x = 0;
    this.y = 0;

    // I/Os cardinality
    this.ios = {};

    // Which IO has focus ?
    this.focusedIo = null;

    // Edges
    this.edges = {};

    // Can this block be used to break a loop ?
    this.isLoopable = function()
    {
        return blockType.loopable;
    };

    /**
     * Sets the block description to something custom
     */
    this.setDescription = function(description)
    {
        this.description = description;
        this.div.find('.description').html(description);
    };

    /**
     * Update the block parameters
     */
    this.updateParameters = function(parameters)
    {
        blocks.history.save();
        this.parameters = parameters;
    };

    /**
     * Parses the cardinality
     */
    this.parseCardinality = function(ioCard, isOutput)
    {
        var card = [0, 1];

        if (isOutput) {
            card = [0, '*'];
        }

        if (ioCard != undefined) {
            if (typeof(ioCard) != 'string') {
                card = [ioCard, ioCard];
            } else {
                tab = ioCard.split('-');
                if (tab.length == 1) {
                    card = [0, tab[0]];
                } else {
                    card = tab;
                }
            }
        }

        for (idx in card) {
            if (card[idx] != '*') {
                card[idx] = parseInt(card[idx]);
            }
        }

        return card;
    };

    /**
     * Parses a length
     */
    this.parseLength = function(length)
    {
        if (typeof(length) == 'number') {
            return length;
        }

        expression = length.split(/\./);

        if (expression.length == 2) {
            var key = expression[0];
            var operation = expression[1];

            if (operation == 'length') {
                return self.parametersManager.getParameterSize(key);
            }

            if (operation == 'value') {
                return parseInt(self.parameters[key]);
            }
        }
    };

    /**
     * Html entities on a string
     */
    this.htmlentities = function(str)
    {
        str = str.replace(/</, '<');
        str = str.replace(/>/, '>');
        return str;
    }

    /**
     * Set the infos of the block
     */
    this.setInfos = function(html)
    {
        self.div.find('.infos').html(html);
    };

    /**
     * Returns the render of the block
     */
    this.getHtml = function()
    {
	self.ios = {};
        var title = blockType.name + '#' + self.id + '';

        var parameters = self.parametersManager.fields;
        for (k in parameters) {
            var parameter = parameters[k];
            if (parameter.asTitle) {
                title = self.parameters[parameter.name];
            }
        }

        html = '

';
        html += '
'+title+'

';
        html += '

';

        if (self.description) {
            html += '
' + self.description + '
';
        } else {
            if (blockType.description) {
                html += '
' + blockType.description + '
';
            } else {
                html += '
No description
';
            }
        }
        html += '

';
        html += '

';

        var parameters = self.parametersManager.fields;
        for (k in parameters) {
            var parameter = parameters[k];
            var parameterHtml = parameter.getHtml(self.parameters);
            var key = 'param_'+k;
            if (!parameter.asTitle && (!blocks.compactMode || (self.edges[key]!=undefined && self.edges[key].length>0))) {
                var card = self.parseCardinality(parameter.card, false)
                self.ios[key] = card;
                if (parameterHtml) {
                    html += '
';
                    if (card[1] == '*' || card[1] > 0) {
                        html += '

';
                    }
                    html += parameterHtml+'
';
                }
            }
        }

        // Handling inputs & outputs
        handle = function(key) {
            html += '
';

            for (k in blockType[key+'s']) {
                var isVariadic = false;
                var io = blockType[key+'s'][k];

                var size = 1;
                if (io.length != undefined) {
                    isVariadic = true;
                    size = self.parseLength(io.length);
                }

                for (x=0; x<size; x++) {
                    var ion = key + '_' + k;
                    var label = io.name.replace('#', x+1);

                    if (io.dynamicLabel) {
                        label = String(eval(io.dynamicLabel));
                    }

                    if (isVariadic) {
                        ion += '_' + x;
                    }

                    // Generating HTML
                    html += '

' + self.htmlentities(label) + '
';

                    // Setting cardinality
                    self.ios[ion] = self.parseCardinality(io.card, (key == 'output'));
                }
            }
                html += '
';
        };

        handle('input');
        handle('output');
	this.checkEdges();

        return html;
    };

    /**
     * Render the block
     */
    this.render = function()
    {
        this.lastScale = null;
        this.hasFocus = false;
        this.div.html(this.getHtml());
        this.initListeners();
        this.redraw();
    };

    /**
     * Creates and inject the div
     */
    this.create = function(div)
    {
        html = '

'

        div.append(html);
        this.div = div.find('#block' + this.id);

        if (this.parametersManager == undefined) {
            this.parametersManager = new ParametersManager(blockType, this);
	    if (this.parameters == null) {
                this.parameters = this.parametersManager.getDefaults();
	    }
        }

        this.render();
    };

    /**
     * Sets the position and the scale of the block
     */
    this.redraw = function(selected)
    {
        // Setting the position
        self.div.css('margin-left', blocks.center.x+self.x*blocks.scale+'px');
        self.div.css('margin-top', blocks.center.y+self.y*blocks.scale+'px');

        // Rescaling
        if (this.lastScale != blocks.scale) {
            self.div.css('font-size', Math.round(blocks.scale*defaultFont)+'px');
            self.div.css('width', Math.round(blocks.scale*self.defaultWidth)+'px');

            var size = Math.round(12*blocks.scale);
            self.div.find('.circle').css('width', size+'px');
            self.div.find('.circle').css('height', size+'px');
            self.div.find('.circle').css('background-size', size+'px '+size+'px');

            self.div.find('.inputs, .outputs').width(self.div.width()/2-10);

            self.cssParameters();
            this.lastScale = blocks.scale
        }

        // Changing the circle rendering
        for (k in self.ios) {
            var circle = self.div.find('.' + k + ' .circle');

            circle.removeClass('io_active');
            if (self.edges[k] != undefined && self.edges[k].length) {
                circle.addClass('io_active');
            }
        }

        // Updating the parameters manager div
        this.parametersManager.div = this.div.find('.parameters');

        // Is selected ?
        self.div.removeClass('block_selected');
        if (selected) {
            self.div.addClass('block_selected');
        }
    };

    /**
     * Sets the css for the inputs
     */
    this.cssParameters = function()
    {
        self.div.find('input').css('font-size', Math.round(blocks.scale*defaultFont)+'px');
        self.div.find('input').css('width', Math.round(blocks.scale*defaultInputWidth)+'px');
        self.div.find('.parameters').css('width', parametersRatio*Math.round(blocks.scale*self.defaultWidth)+'px');
    };

    /**
     * Init the function listeners
     */
    this.initListeners = function()
    {
        // Drag & drop the block
        self.div.find('.blockTitle').mousedown(function(event) {
            if (event.which == 1) {
                self.historySaved = false;
                self.drag = [blocks.mouseX/blocks.scale-self.x, blocks.mouseY/blocks.scale-self.y];
            }
        });

        // Handle focus
        self.div.hover(function() {
            self.hasFocus = true;
        }, function() {
            self.hasFocus = false;
        });

        // Handle focus on the I/Os
        self.div.find('.input, .output, .parameter').hover(function() {
            self.focusedIo = $(this).attr('rel');
        }, function() {
            self.focusedIo = null;
        });

        // Dragging
        $('html').mousemove(function(evt) {
            if (self.drag) {
                if (!self.historySaved) {
                    blocks.history.save();
                    self.historySaved = true;
                }
                self.x = (blocks.mouseX/blocks.scale-self.drag[0]);
                self.y = (blocks.mouseY/blocks.scale-self.drag[1]);
                blocks.redraw();
            }
        });

        // Drag the block
        $('html').mouseup(function() {
            self.drag = null;
        });

        // Draw a link
        self.div.find('.input, .output, .parameter').mousedown(function(event) {
            if (event.which == 1) {
                blocks.beginLink(self, $(this).attr('rel'));
                event.preventDefault();
            }
        });

        // Handle the parameters
        self.div.find('.gear').click(function() {
            self.parametersManager.toggle();
            self.cssParameters();
        });

        // Handle the deletion
        self.div.find('.delete').click(function() {
            blocks.removeBlock(blocks.getBlockId(self));
        });

        // Show the description
        self.div.find('.info').hover(function() {
            self.div.find('.description').show();
        }, function() {
            self.div.find('.description').hide();
        });
    };

    /**
     * Gets the link position for an input or output
     */
    this.linkPositionFor = function(io)
    {
        try {
            div = self.div.find('.' + io + ' .circle')

            var x = (div.offset().left-blocks.div.offset().left)+div.width()/2;
            var y = (div.offset().top-blocks.div.offset().top)+div.height()/2;
        } catch (error) {
            throw 'Unable to find link position for '+io+' ('+error+')';
        }

        return {x: x, y: y};
    };

    /**
     * Can the io be linked ?
     */
    this.canLink = function(ion)
    {
        var tab = [];
        if (this.edges[ion] != undefined) {
            tab = this.edges[ion];
        }

        var card = this.ios[ion];

        if (card[1] != '*') {
            return (tab.length < card[1]);
        }

        return true;
    };

    /**
     * Add an edge
     */
    this.addEdge = function(ion, edge)
    {
        var tab = [];
        if (this.edges[ion] != undefined) {
            tab = this.edges[ion];
        }

        tab.push(edge);
        this.edges[ion] = tab;
    };

    /**
     * Check that all edges reference a valid I/O
     */
    this.checkEdges = function()
    {
	var toDelete = [];
	for (ion in this.edges) {
	    var edges = this.edges[ion];

	    if (!(ion in this.ios)) {
		for (k in edges) {
		    toDelete.push(edges[k]);
		}

		delete this.edges[ion];
	    }
	}

	for (k in toDelete) {
	    blocks.removeEdge(blocks.getEdgeId(toDelete[k]));
	}

	if (toDelete.length) {
	    blocks.redraw();
	}
    };

    /**
     * Erase an edge
     */
    this.eraseEdge = function(ion, edge)
    {
        if (this.edges[ion] != undefined) {
            for (k in this.edges[ion]) {
                if (this.edges[ion][k] == edge) {
                    arrayRemove(this.edges[ion], k);
                    break;
                }
            }
        }
    };

    /**
     * Erase the block
     */
    this.erase = function()
    {
        self.div.remove();
    };

    /**
     * Find all successors of a block, and their successors
     */
    this.allSuccessors = function()
    {
        // Blocks already explored
        var explored = {};
        var exploreList = [this];
        var ids = [this.id];
        explored[this.id] = true;

        while (exploreList.length > 0) {
            var currentBlock = exploreList.pop();

            for (key in currentBlock.edges) {
                for (i in currentBlock.edges[key]) {
                    var edge = currentBlock.edges[key][i];
                    if (edge.isLoopable()) {
                        continue;
                    }
                    fromTo = edge.fromTo();

                    if (fromTo[0] == currentBlock) {
                        target = fromTo[1];

                        if (!(target.id in explored)) {
                            explored[target.id] = true;
                            exploreList.push(target);
                            ids.push(target.id);
                        }
                    }
                }
            }
        }

        return ids;
    };

    /**
     * Exports the block to JSON
     */
    this.exportData = function()
    {
	return {
	    id: this.id,
	    x: this.x,
	    y: this.y,
	    type: blockType.name,
            module: blockType.module,
	    parameters: this.parametersManager.exportData(this.parameters)
	};
    };
};

/**
 * Imports a block from its data
 */
function BlockImport(blocks, data)
{
    for (t in blocks.blockTypes) {
	var blockType = blocks.blockTypes[t];
        var module = ('module' in data) ? data.module : null;
	if (blockType.name == data.type && blockType.module == module) {
	    var block = new Block(blocks, blockType, data.id);
	    block.x = data.x;
	    block.y = data.y;
	    block.parameters = ParametersImport(data.parameters);
	    return block;
	}
    }

    throw 'Unable to create a block of type ' + data.type;
};
<|endoftext|>


<commit_before>var moment = require('moment');

module.exports = function(main){
	var keys = main.options.mappings;
	var util = {};

	// parses a string and returns seconds ('1:35:30' -> 5730)
	// todo: move this to entry validation / scheme
	util.castDuration = function(duration){
		duration = duration.split(':');
		return moment.duration({hours:duration[0],minutes:duration[1],seconds:duration[2]}).asSeconds();
		//return parseInt(duration[0], 10) * 3600 + parseInt(duration[1], 10) * 60 + parseInt(duration[2], 10);
	};

	util.transformEntry = function(row, index){
		// is entry within timeframe?
		if(main.options.start.isAfter(row[keys.date]) || main.options.end.isBefore(row[keys.date])){
			return null;
		}

		// build proper model
		var entry = {
			custom: {}
		};

		entry.project = row[keys.project] || '';
		entry.task = row[keys.task] || '';

		// set date
		if(keys.date && row[keys.date]){
			entry.date = moment(row[keys.date], main.options.dateFormat);
		}else if(keys.start && row[keys.start]){
			entry.date = moment(row[keys.start], main.options.startFormat).startOf('day');
		}else{
			main.log('Entry (' + entry.project + ': ' + entry.task + ') does not have date or start field and is ignored.');
			return null;
		}

		// valid date?
		if(!entry.date.isValid()){
			main.log('Entry (' + entry.project + ': ' + entry.task + '): Date could not be parsed.');
			return null;
		}

		// set duration
		if(keys.duration && row[keys.duration]){
			entry.duration = util.castDuration(row[keys.duration]);
		}else if(keys.start && keys.end && row[keys.start] && row[keys.end]){
			var start = moment(row[keys.start], main.options.startFormat);
			var end = moment(row[keys.end], main.options.endFormat);
			entry.duration = end.diff(start, 'seconds');
		}else{
			main.log('Entry (' + entry.project + ': ' + entry.task + ') does not have duration or start/end field and is ignored.');
			return null;
		}

		// valid duration?
		if(entry.duration === 0){
			main.log('Entry (' + entry.project + ': ' + entry.task + '): Duration equals 0.');
			return null;
		}

		// custom fields
		if(typeof keys.custom === 'object' && keys.custom !== null){
			for (var prop in keys.custom) {
				if(keys.custom.hasOwnProperty(prop) && row[keys.custom[prop]]){
					entry.custom[prop] = row[keys.custom[prop]];
				}
			}
		}

		return entry;
	};

	return util;
};<commit_msg>move timeframe check<commit_after>var moment = require('moment');

module.exports = function(main){
	var keys = main.options.mappings;
	var util = {};

	// parses a string and returns seconds ('1:35:30' -> 5730)
	// todo: move this to entry validation / scheme
	util.castDuration = function(duration){
		duration = duration.split(':');
		return moment.duration({hours:duration[0],minutes:duration[1],seconds:duration[2]}).asSeconds();
		//return parseInt(duration[0], 10) * 3600 + parseInt(duration[1], 10) * 60 + parseInt(duration[2], 10);
	};

	// validate / parse entries from csv
	util.transformEntry = function(row, index){
		// build proper model
		var entry = {
			custom: {}
		};

		entry.project = row[keys.project] || '';
		entry.task = row[keys.task] || '';

		// set date
		if(keys.date && row[keys.date]){
			entry.date = moment(row[keys.date], main.options.dateFormat);
		}else if(keys.start && row[keys.start]){
			entry.date = moment(row[keys.start], main.options.startFormat).startOf('day');
		}else{
			main.log('Entry (' + entry.project + ': ' + entry.task + '): Does not have date or start field and is ignored');
			return null;
		}

		// valid date?
		if(!entry.date.isValid()){
			main.log('Entry (' + entry.project + ': ' + entry.task + '): Date could not be parsed');
			return null;
		}

		// is entry within timeframe?
		if(main.options.start.isAfter(entry.date) || main.options.end.isBefore(entry.date)){
			main.log('Entry (' + entry.project + ': ' + entry.task + '): Is not within timeframe');
			return null;
		}

		// set duration
		if(keys.duration && row[keys.duration]){
			entry.duration = util.castDuration(row[keys.duration]);
		}else if(keys.start && keys.end && row[keys.start] && row[keys.end]){
			var start = moment(row[keys.start], main.options.startFormat);
			var end = moment(row[keys.end], main.options.endFormat);
			entry.duration = end.diff(start, 'seconds');
		}else{
			main.log('Entry (' + entry.project + ': ' + entry.task + ') does not have duration or start/end field and is ignored');
			return null;
		}

		// valid duration?
		if(entry.duration === 0){
			main.log('Entry (' + entry.project + ': ' + entry.task + '): Duration equals 0');
			return null;
		}

		// custom fields
		if(typeof keys.custom === 'object' && keys.custom !== null){
			for (var prop in keys.custom) {
				if(keys.custom.hasOwnProperty(prop) && row[keys.custom[prop]]){
					entry.custom[prop] = row[keys.custom[prop]];
				}
			}
		}

		return entry;
	};

	return util;
};<|endoftext|>


<commit_before>
import React from "react";
import PropTypes from "prop-types";

import LineSeries from "./LineSeries";
import AreaOnlySeries from "./AreaOnlySeries";
import { strokeDashTypes } from "../utils";

function AreaSeries(props) {
	const { yAccessor, baseAt } = props;
	const {
		className,
		opacity,
		stroke,
		strokeWidth,
		strokeOpacity,
		strokeDasharray,
		fill,
		interpolation,
		style,
		canvasClip,
	} = props;

	return (
		<g className={className}>
			<LineSeries
				yAccessor={yAccessor}
				stroke={stroke}
				strokeWidth={strokeWidth}
				strokeOpacity={strokeOpacity}
				strokeDasharray={strokeDasharray}
				interpolation={interpolation}
				style={style}
				canvasClip={canvasClip}
				fill="none"
				hoverHighlight={false}
			/>
			<AreaOnlySeries
				yAccessor={yAccessor}
				interpolation={interpolation}
				base={baseAt}
				fill={fill}
				opacity={opacity}
				style={style}
				canvasClip={canvasClip}
				stroke="none"
			/>
		</g>
	);
}

AreaSeries.propTypes = {
	stroke: PropTypes.string,
	strokeWidth: PropTypes.number,
	fill: PropTypes.string.isRequired,
	strokeOpacity: PropTypes.number.isRequired,
	opacity: PropTypes.number.isRequired,
	className: PropTypes.string,
	yAccessor: PropTypes.func.isRequired,
	baseAt: PropTypes.func,
	interpolation: PropTypes.func,
	canvasClip: PropTypes.func,
	style: PropTypes.object,
	strokeDasharray: PropTypes.oneOf(strokeDashTypes),
};

AreaSeries.defaultProps = {
	stroke: "#4682B4",
	strokeWidth: 1,
	strokeOpacity: 1,
	strokeDasharray: "Solid",
	opacity: 0.5,
	fill: "#4682B4",
	className: "react-stockcharts-area",
};

export default AreaSeries;
<commit_msg>Move line on top of area<commit_after>
import React from "react";
import PropTypes from "prop-types";

import LineSeries from "./LineSeries";
import AreaOnlySeries from "./AreaOnlySeries";
import { strokeDashTypes } from "../utils";

function AreaSeries(props) {
	const { yAccessor, baseAt } = props;
	const {
		className,
		opacity,
		stroke,
		strokeWidth,
		strokeOpacity,
		strokeDasharray,
		fill,
		interpolation,
		style,
		canvasClip,
	} = props;

	return (
		<g className={className}>
			<AreaOnlySeries
				yAccessor={yAccessor}
				interpolation={interpolation}
				base={baseAt}
				fill={fill}
				opacity={opacity}
				style={style}
				canvasClip={canvasClip}
				stroke="none"
			/>
			<LineSeries
				yAccessor={yAccessor}
				stroke={stroke}
				strokeWidth={strokeWidth}
				strokeOpacity={strokeOpacity}
				strokeDasharray={strokeDasharray}
				interpolation={interpolation}
				style={style}
				canvasClip={canvasClip}
				fill="none"
				hoverHighlight={false}
			/>
		</g>
	);
}

AreaSeries.propTypes = {
	stroke: PropTypes.string,
	strokeWidth: PropTypes.number,
	fill: PropTypes.string.isRequired,
	strokeOpacity: PropTypes.number.isRequired,
	opacity: PropTypes.number.isRequired,
	className: PropTypes.string,
	yAccessor: PropTypes.func.isRequired,
	baseAt: PropTypes.func,
	interpolation: PropTypes.func,
	canvasClip: PropTypes.func,
	style: PropTypes.object,
	strokeDasharray: PropTypes.oneOf(strokeDashTypes),
};

AreaSeries.defaultProps = {
	stroke: "#4682B4",
	strokeWidth: 1,
	strokeOpacity: 1,
	strokeDasharray: "Solid",
	opacity: 0.5,
	fill: "#4682B4",
	className: "react-stockcharts-area",
};

export default AreaSeries;
<|endoftext|>


<commit_before>define(["@loader", "module", "can/view/stache/intermediate_and_imports"], function(loader, module, getIntermediateAndImports){

  var main;

  var isNode = typeof process === "object" &&
    {}.toString.call(process) === "[object process]";

  if(!isNode) {
    steal.done().then(setup);
  }

  function setup(){
    loader.import(loader.main).then(function(r){
      main = r;
      liveReload();
    });
  }

  function liveReload(){
    if(!loader.has("live-reload")) {
      return;
    }

    loader.import("live-reload", { name: module.id }).then(function(reload){
			loader.normalize(loader.main).then(function(mainName){
				reload(function(){
					main.rerender();
				});

				reload(mainName, function(r){
					main = r;
				});
			});
    });
  }

	var start = function(){
		var state = this.state = new this.viewModel;
		can.route.map(state);
		can.route.ready();
		this.rerender();
	},
	rerender = function(){
		var body = document.body;
		can.view.renderAsync(this.render, this.state).then(function(result){
			can.each(can.makeArray(body.childNodes), function(el){
				if(el.tagName && el.tagName.toLowerCase() !== "script") {
					can.remove(el);
				}
			});
			can.appendChild(body, result.fragment);
		});
	},
	renderNode = function(url){
		var state = new this.viewModel;
		state.attr(can.route.deparam(url));
		var doc = new document.constructor;

		var renderPromise = can.view.renderAsync(this.render, state, {}, doc)
		.then(function(result){
			var html = doc.body.innerHTML;

			triggerInBody("removed");
			doc.documentElement.removeChild(doc.body);

			return {
				html: html,
				data: result.data
			};
		});

		function triggerInBody(event) {
			// Do cleanup here.
			function traverse(el){
				var cur = el.firstChild;
				while(cur) {
					can.trigger(cur, event);
					traverse(cur);
					cur = cur.nextSibling;
				}
			}

			var cur = doc.body.firstChild;
			while(cur) {
				traverse(cur);
				can.trigger(cur, event);
				cur = cur.nextSibling;
			}
		}

		return renderPromise;
	};

	function translate(load){
		var intermediateAndImports = getIntermediateAndImports(load.source);

		var ases = intermediateAndImports.ases;
		var imports = intermediateAndImports.imports;
		var args = [];
		can.each(ases, function(from, name){
			// Move the as to the front of the array.
			imports.splice(imports.indexOf(from), 1);
			imports.unshift(from);
			args.unshift(name);
		});
		imports.unshift("can/view/stache/stache");
		args.unshift("stache");

		return "define("+JSON.stringify(intermediateAndImports.imports)+",function(" +
			args.join(", ") + "){\n" +
			"var __export = {\n" +
			"\trender: stache(" + JSON.stringify(intermediateAndImports.intermediate) + "),\n" +
			"\tstart: " + start.toString() + ",\n" +
			"\trerender: " + rerender.toString() + ",\n" +
			"\trenderNode: " + renderNode.toString() + ",\n" +
			can.map(ases, function(from, name){
				return "\t" + name + ": " + name +"['default'] || " + name;
			}).join(",\n") +
			"\n};\n\n" +
			"if(typeof steal !== 'undefined' && !(typeof process === 'object' && {}.toString.call(process) === '[object process]')) steal.done().then(function() { __export.start(); });\n" +
			"return __export;\n" +
		"});";
	}

  return {
    translate: translate
  };
});
<commit_msg>Removing the autorender System plugin<commit_after><|endoftext|>


<commit_before>import {Map} from "immutable";
import get from "lodash.get";
import {Content} from "native-base";
import {isEmpty, isNil, values} from "ramda";
import React, {Component, PropTypes} from "react";
import IPropTypes from "react-immutable-proptypes";
import {Dimensions, StyleSheet, TouchableOpacity, View} from "react-native";
import Accordion from "react-native-collapsible/Accordion";
import {connect} from "react-redux";
import {bindActionCreators} from "redux";

import {saveQuestionnaireAnswers} from "../actions/questionnaire";
import Icon from "../components/iwapp-icons";
import QuestionnaireProgress from "../components/questionnaire-progress";
import Text from "../components/text-lato";
import * as colors from "../lib/colors";
import FaIcons from "react-native-vector-icons/FontAwesome";
import {heightWithoutHeader} from "../lib/const";

const styles = StyleSheet.create({
    answer: {
        padding: 5,
        flexDirection: "row"
    },
    lastAnswer: {
        borderBottomWidth: 1,
        borderBottomColor: colors.borderAccordion
    },
    header: {
        borderTopWidth: 1,
        borderStyle: "solid",
        borderTopColor: colors.borderAccordion,
        flexDirection: "row",
        alignItems: "center",
        justifyContent: "space-between",
        paddingHorizontal: 18,
        paddingVertical: 5
    },
    lastHeader: {
        borderBottomWidth: 1,
        borderBottomColor: colors.borderAccordion
    },
    questionnaireProgressContainer: {
        marginBottom: 1,
        alignItems: "center"
    },
    questionStatus: {
        borderRadius: 100,
        height: 8,
        width: 8,
        marginRight: 10,
        alignSelf: "center"
    },
    questionTextWrp: {
        justifyContent: "center",
        minHeight: 40
    },
    questionText: {
        fontSize: 13,
        color: colors.textGrey
    },
    answerStatus: {
        borderRadius: 100,
        height: 22,
        width: 22,
        borderWidth: 1,
        borderColor: colors.borderAccordion,
        marginHorizontal: 5,
        alignItems: "center"
    },
    answerStatusText: {
        color: colors.questionnaireAnswerStatusText,
        top: 1,
        fontWeight: "bold"
    },
    answerWrp: {
        marginBottom: 5
    },
    textAnswer: {
        alignSelf: "center",
        fontSize: 13,
        color: colors.questionnaireAnswerStatusText,
    }
});

class Questionnaire extends Component {

    static propTypes = {
        asteroid: PropTypes.object.isRequired,
        collections: IPropTypes.map,
        saveQuestionnaireAnswers: PropTypes.func.isRequired,
        selectedQuestionnaire: PropTypes.shape({
            key: PropTypes.string.isRequired
        }).isRequired,
        sessionId: PropTypes.string.isRequired,
        site: PropTypes.object.isRequired,
        userId: PropTypes.string.isRequired
    }

    static defaultProps: {
        site: {}
    }

    constructor (props) {
        super(props);
        this.state = {
            answers: []
        };
    }

    componentWillMount () {
        this.setAnswersState(this.props);
    }

    componentDidMount () {
        this.subscribeToAnswers();
        this.subscribeToQuestions();
    }

    componentWillReceiveProps (nextProps) {
        this.setAnswersState(nextProps);
    }

    setAnswersState (props) {
        const questionnaire = this.getQuestionnaire(props.collections);
        const answersCollection = this.getAnswersFromCollection(props.collections).toJS();
        const answers = this.setAnswers(answersCollection.answers, questionnaire);
        this.setState({answers});
    }

    subscribeToQuestions () {
        const category = this.getQuestionnaireCategory();
        this.props.asteroid.subscribe("questions", {type: "questionnaire", category});
    }

    subscribeToAnswers () {
        const category = this.getQuestionnaireCategory();
        this.props.asteroid.subscribe("answers", {
            siteId: this.props.site._id,
            category,
            type: "questionnaire"
        });
    }

    getQuestionnaire (collections) {
        if (collections.get("questions")) {
            const questionnaire = collections.get("questions").find(questions =>
                questions.get("type") === "questionnaire" &&
                questions.get("category") === this.getQuestionnaireCategory()
            ) || Map();
            return questionnaire.toJS();
        }
        return {};
    }

    getQuestionnaireCategory () {
        return get(this.props, "selectedQuestionnaire.key");
    }

    getAnswersId (collections) {
        const {type, category} = this.getQuestionnaire(collections);
        return `${type}-${category}-${this.props.site._id}`;
    }

    getAnswersFromCollection (collections) {
        return collections.getIn(["answers", this.getAnswersId(collections)]) || Map();
    }

    getAnswerFromState (questionId) {
        return this.state.answers.find(answer => questionId === answer.id);
    }

    setAnswers (answers = [], questionnaire, selectedAnswer, questionIndex) {
        if (!questionnaire.questions) {
            return [];
        }
        return questionnaire.questions.map((question, index) => {
            const selectedAnswerInCollection = answers.find(answer => question.id === answer.id) || {};
            if (isEmpty(selectedAnswerInCollection) && isNil(selectedAnswer)) {
                return {};
            }
            if (index !== questionIndex) {
                return (
                    this.getAnswerFromState(question.id) ||
                    selectedAnswerInCollection
                );
            }
            return {
                id: question.id,
                timestamp: new Date().toISOString(),
                answer: selectedAnswer,
                question: {
                    text: question.text
                }
            };
        });
    }

    isLastSection (questionIndex) {
        return questionIndex === get(this.getQuestionnaire(this.props.collections), "questions.length") - 1;
    }

    isAlreadyAnswered (questionId) {
        const answer = this.getAnswerFromState(questionId);
        return answer && answer.id === questionId;
    }

    isActiveAnswer (option, optionIndex, questionId) {
        const answer = this.getAnswerFromState(questionId);
        return (
            answer &&
            answer.id === questionId &&
            answer.answer === option
        );
    }

    onSaveQuestionnaireAnswers ({option, questionIndex}) {
        const questionnaire = this.getQuestionnaire(this.props.collections);
        const collectionsAnswer = this.getAnswersFromCollection(this.props.collections).get("answers");
        const answers = this.setAnswers(
            collectionsAnswer ? collectionsAnswer.toJS() : [],
            questionnaire,
            option,
            questionIndex
        );

        this.setState({answers});
        this.props.saveQuestionnaireAnswers(
            answers,
            questionIndex,
            this.props.userId,
            this.props.site._id,
            questionnaire,
            this.props.sessionId
        );
    }

    updateProgress (selectedQuestionnaire, questionnaire) {
        const totalQuestions = get(questionnaire, "questions.length");
        var totalAnswers = 0;
        values(questionnaire.questions).forEach(value => {
            this.isAlreadyAnswered(value.id) ? totalAnswers++ : null;
        });
        var percentage = totalAnswers/totalQuestions;
        const questionnaireItem = {
            ...selectedQuestionnaire,
            value: percentage || 0,
            totalAnswers,
            text: ((percentage || 0) * 100).toFixed() + "% completato"
        };
        return questionnaireItem;
    }

    renderQuestionStatus (question) {
        return (
            <View
                style={[styles.questionStatus, {
                    backgroundColor: (
                        this.isAlreadyAnswered(question.id) ?
                        colors.greenStatus :
                        colors.redStatus
                    )
                }]}
            />
        );
    }

    renderHeader (question, questionIndex, isActive) {
        const {width} = Dimensions.get("window");
        return (
            <View
                key={questionIndex}
                style={[styles.header, this.isLastSection(questionIndex) ? styles.lastHeader : {}]}
            >
                <View style={{flexDirection: "row"}}>
                    {this.renderQuestionStatus(question, questionIndex)}
                    <View style={[styles.questionTextWrp, {width: width * 0.75}]}>
                        <Text style={styles.questionText}>{question.text}</Text>
                    </View>
                </View>
                <FaIcons
                    color={colors.iconArrow}
                    name={isActive ? "angle-up" : "angle-down"}
                    size={26}
                />
            </View>
        );
    }

    renderAnswerStatus (option, optionIndex, questionId) {
        return this.isActiveAnswer(option, optionIndex, questionId) ? (
            <View style={[styles.answerStatus, {borderColor: colors.greenStatus}]}>
                <Icon
                    color={colors.greenStatus}
                    name="iw-check"
                    size={16}
                    style={{backgroundColor: colors.transparent, top: 2}}
                />
            </View>
        ) :
        (<View style={styles.answerStatus}>
            <Text style={styles.answerStatusText}>{optionIndex + 1}</Text>
        </View>);
    }

    renderAnswer ({option, optionIndex, questionIndex, question}) {
        const {width} = Dimensions.get("window");
        const optionsLength = question.options.length;
        return (
            <View key={optionIndex} style={[
                (optionIndex === optionsLength - 1 && this.isLastSection(questionIndex)) ? styles.lastAnswer : {}
            ]}
            >
                <TouchableOpacity onPress={() => this.onSaveQuestionnaireAnswers(...arguments)} style={styles.answerWrp}>
                    <View style={styles.answer}>
                        {this.renderAnswerStatus(option, optionIndex, question.id)}
                        <Text style={[styles.textAnswer, {width: width * 0.85}]}>{option}</Text>
                    </View>
                </TouchableOpacity>
            </View>
        );
    }

    renderContent (question, questionIndex) {
        return question.options.map((option, optionIndex) =>
            ::this.renderAnswer({option, optionIndex, question, questionIndex})
        );
    }

    renderAccordion (questionnaire) {
        return !isEmpty(questionnaire) ? (
            <Accordion
                renderContent={::this.renderContent}
                renderHeader={::this.renderHeader}
                sections={questionnaire.questions}
                underlayColor={colors.transparent}
            />
        ) : null;
    }

    render () {
        const {height} = Dimensions.get("window");
        const questionnaire = this.getQuestionnaire(this.props.collections);
        const selectedQuestionnaire = this.updateProgress(this.props.selectedQuestionnaire, questionnaire);
        // height: height * .875
        return (
            <View style={{height: heightWithoutHeader(height * 1.038)}}>
                <Content>
                    <View style={styles.questionnaireProgressContainer}>
                        <QuestionnaireProgress questionnaire={selectedQuestionnaire} />
                    </View>
                    <View>
                        {this.renderAccordion(questionnaire)}
                    </View>
                </Content>
            </View>
        );
    }
}

function mapStateToProps (state) {
    return {
        collections: state.collections,
        sessionId: state.sessionId,
        site: state.site,
        userId: state.userId
    };
}
function mapDispatchToProps (dispatch) {
    return {
        saveQuestionnaireAnswers: bindActionCreators(saveQuestionnaireAnswers, dispatch)
    };
}
export default connect(mapStateToProps, mapDispatchToProps)(Questionnaire);
<commit_msg>fix optimistic update bug on questionnaire.<commit_after>import {Map} from "immutable";
import get from "lodash.get";
import {Content} from "native-base";
import {isEmpty, isNil, values} from "ramda";
import React, {Component, PropTypes} from "react";
import IPropTypes from "react-immutable-proptypes";
import {Dimensions, StyleSheet, TouchableOpacity, View} from "react-native";
import Accordion from "react-native-collapsible/Accordion";
import {connect} from "react-redux";
import {bindActionCreators} from "redux";

import {saveQuestionnaireAnswers} from "../actions/questionnaire";
import Icon from "../components/iwapp-icons";
import QuestionnaireProgress from "../components/questionnaire-progress";
import Text from "../components/text-lato";
import * as colors from "../lib/colors";
import FaIcons from "react-native-vector-icons/FontAwesome";
import {heightWithoutHeader} from "../lib/const";

const styles = StyleSheet.create({
    answer: {
        padding: 5,
        flexDirection: "row"
    },
    lastAnswer: {
        borderBottomWidth: 1,
        borderBottomColor: colors.borderAccordion
    },
    header: {
        borderTopWidth: 1,
        borderStyle: "solid",
        borderTopColor: colors.borderAccordion,
        flexDirection: "row",
        alignItems: "center",
        justifyContent: "space-between",
        paddingHorizontal: 18,
        paddingVertical: 5
    },
    lastHeader: {
        borderBottomWidth: 1,
        borderBottomColor: colors.borderAccordion
    },
    questionnaireProgressContainer: {
        marginBottom: 1,
        alignItems: "center"
    },
    questionStatus: {
        borderRadius: 100,
        height: 8,
        width: 8,
        marginRight: 10,
        alignSelf: "center"
    },
    questionTextWrp: {
        justifyContent: "center",
        minHeight: 40
    },
    questionText: {
        fontSize: 13,
        color: colors.textGrey
    },
    answerStatus: {
        borderRadius: 100,
        height: 22,
        width: 22,
        borderWidth: 1,
        borderColor: colors.borderAccordion,
        marginHorizontal: 5,
        alignItems: "center"
    },
    answerStatusText: {
        color: colors.questionnaireAnswerStatusText,
        top: 1,
        fontWeight: "bold"
    },
    answerWrp: {
        marginBottom: 5
    },
    textAnswer: {
        alignSelf: "center",
        fontSize: 13,
        color: colors.questionnaireAnswerStatusText,
    }
});

class Questionnaire extends Component {

    static propTypes = {
        asteroid: PropTypes.object.isRequired,
        collections: IPropTypes.map,
        saveQuestionnaireAnswers: PropTypes.func.isRequired,
        selectedQuestionnaire: PropTypes.shape({
            key: PropTypes.string.isRequired
        }).isRequired,
        sessionId: PropTypes.string.isRequired,
        site: PropTypes.object.isRequired,
        userId: PropTypes.string.isRequired
    }

    static defaultProps: {
        site: {}
    }

    constructor (props) {
        super(props);
        this.state = {
            answers: []
        };
    }

    componentWillMount () {
        this.setAnswersState(this.props);
    }

    componentDidMount () {
        this.subscribeToAnswers();
        this.subscribeToQuestions();
    }

    setAnswersState (props) {
        const questionnaire = this.getQuestionnaire(props.collections);
        const answersCollection = this.getAnswersFromCollection(props.collections).toJS();
        const answers = this.setAnswers(answersCollection.answers, questionnaire);
        this.setState({answers});
    }

    subscribeToQuestions () {
        const category = this.getQuestionnaireCategory();
        this.props.asteroid.subscribe("questions", {type: "questionnaire", category});
    }

    subscribeToAnswers () {
        const category = this.getQuestionnaireCategory();
        this.props.asteroid.subscribe("answers", {
            siteId: this.props.site._id,
            category,
            type: "questionnaire"
        });
    }

    getQuestionnaire (collections) {
        if (collections.get("questions")) {
            const questionnaire = collections.get("questions").find(questions =>
                questions.get("type") === "questionnaire" &&
                questions.get("category") === this.getQuestionnaireCategory()
            ) || Map();
            return questionnaire.toJS();
        }
        return {};
    }

    getQuestionnaireCategory () {
        return get(this.props, "selectedQuestionnaire.key");
    }

    getAnswersId (collections) {
        const {type, category} = this.getQuestionnaire(collections);
        return `${type}-${category}-${this.props.site._id}`;
    }

    getAnswersFromCollection (collections) {
        return collections.getIn(["answers", this.getAnswersId(collections)]) || Map();
    }

    getAnswerFromState (questionId) {
        return this.state.answers.find(answer => questionId === answer.id);
    }

    setAnswers (answers = [], questionnaire, selectedAnswer, questionIndex) {
        if (!questionnaire.questions) {
            return [];
        }
        return questionnaire.questions.map((question, index) => {
            const selectedAnswerInCollection = answers.find(answer => question.id === answer.id) || {};
            if (isEmpty(selectedAnswerInCollection) && isNil(selectedAnswer)) {
                return {};
            }
            if (index !== questionIndex) {
                return (
                    this.getAnswerFromState(question.id) ||
                    selectedAnswerInCollection
                );
            }
            return {
                id: question.id,
                timestamp: new Date().toISOString(),
                answer: selectedAnswer,
                question: {
                    text: question.text
                }
            };
        });
    }

    isLastSection (questionIndex) {
        return questionIndex === get(this.getQuestionnaire(this.props.collections), "questions.length") - 1;
    }

    isAlreadyAnswered (questionId) {
        const answer = this.getAnswerFromState(questionId);
        return answer && answer.id === questionId;
    }

    isActiveAnswer (option, optionIndex, questionId) {
        const answer = this.getAnswerFromState(questionId);
        return (
            answer &&
            answer.id === questionId &&
            answer.answer === option
        );
    }

    onSaveQuestionnaireAnswers ({option, questionIndex}) {
        const questionnaire = this.getQuestionnaire(this.props.collections);
        const collectionsAnswer = this.getAnswersFromCollection(this.props.collections).get("answers");
        const answers = this.setAnswers(
            collectionsAnswer ? collectionsAnswer.toJS() : [],
            questionnaire,
            option,
            questionIndex
        );

        this.setState({answers});
        this.props.saveQuestionnaireAnswers(
            answers,
            questionIndex,
            this.props.userId,
            this.props.site._id,
            questionnaire,
            this.props.sessionId
        );
    }

    updateProgress (selectedQuestionnaire, questionnaire) {
        const totalQuestions = get(questionnaire, "questions.length");
        var totalAnswers = 0;
        values(questionnaire.questions).forEach(value => {
            this.isAlreadyAnswered(value.id) ? totalAnswers++ : null;
        });
        var percentage = totalAnswers/totalQuestions;
        const questionnaireItem = {
            ...selectedQuestionnaire,
            value: percentage || 0,
            totalAnswers,
            text: ((percentage || 0) * 100).toFixed() + "% completato"
        };
        return questionnaireItem;
    }

    renderQuestionStatus (question) {
        return (
            <View
                style={[styles.questionStatus, {
                    backgroundColor: (
                        this.isAlreadyAnswered(question.id) ?
                        colors.greenStatus :
                        colors.redStatus
                    )
                }]}
            />
        );
    }

    renderHeader (question, questionIndex, isActive) {
        const {width} = Dimensions.get("window");
        return (
            <View
                key={questionIndex}
                style={[styles.header, this.isLastSection(questionIndex) ? styles.lastHeader : {}]}
            >
                <View style={{flexDirection: "row"}}>
                    {this.renderQuestionStatus(question, questionIndex)}
                    <View style={[styles.questionTextWrp, {width: width * 0.75}]}>
                        <Text style={styles.questionText}>{question.text}</Text>
                    </View>
                </View>
                <FaIcons
                    color={colors.iconArrow}
                    name={isActive ? "angle-up" : "angle-down"}
                    size={26}
                />
            </View>
        );
    }

    renderAnswerStatus (option, optionIndex, questionId) {
        return this.isActiveAnswer(option, optionIndex, questionId) ? (
            <View style={[styles.answerStatus, {borderColor: colors.greenStatus}]}>
                <Icon
                    color={colors.greenStatus}
                    name="iw-check"
                    size={16}
                    style={{backgroundColor: colors.transparent, top: 2}}
                />
            </View>
        ) :
        (<View style={styles.answerStatus}>
            <Text style={styles.answerStatusText}>{optionIndex + 1}</Text>
        </View>);
    }

    renderAnswer ({option, optionIndex, questionIndex, question}) {
        const {width} = Dimensions.get("window");
        const optionsLength = question.options.length;
        return (
            <View key={optionIndex} style={[
                (optionIndex === optionsLength - 1 && this.isLastSection(questionIndex)) ? styles.lastAnswer : {}
            ]}
            >
                <TouchableOpacity onPress={() => this.onSaveQuestionnaireAnswers(...arguments)} style={styles.answerWrp}>
                    <View style={styles.answer}>
                        {this.renderAnswerStatus(option, optionIndex, question.id)}
                        <Text style={[styles.textAnswer, {width: width * 0.85}]}>{option}</Text>
                    </View>
                </TouchableOpacity>
            </View>
        );
    }

    renderContent (question, questionIndex) {
        return question.options.map((option, optionIndex) =>
            ::this.renderAnswer({option, optionIndex, question, questionIndex})
        );
    }

    renderAccordion (questionnaire) {
        return !isEmpty(questionnaire) ? (
            <Accordion
                renderContent={::this.renderContent}
                renderHeader={::this.renderHeader}
                sections={questionnaire.questions}
                underlayColor={colors.transparent}
            />
        ) : null;
    }

    render () {
        const {height} = Dimensions.get("window");
        const questionnaire = this.getQuestionnaire(this.props.collections);
        const selectedQuestionnaire = this.updateProgress(this.props.selectedQuestionnaire, questionnaire);
        // height: height * .875
        return (
            <View style={{height: heightWithoutHeader(height * 1.038)}}>
                <Content>
                    <View style={styles.questionnaireProgressContainer}>
                        <QuestionnaireProgress questionnaire={selectedQuestionnaire} />
                    </View>
                    <View>
                        {this.renderAccordion(questionnaire)}
                    </View>
                </Content>
            </View>
        );
    }
}

function mapStateToProps (state) {
    return {
        collections: state.collections,
        sessionId: state.sessionId,
        site: state.site,
        userId: state.userId
    };
}
function mapDispatchToProps (dispatch) {
    return {
        saveQuestionnaireAnswers: bindActionCreators(saveQuestionnaireAnswers, dispatch)
    };
}
export default connect(mapStateToProps, mapDispatchToProps)(Questionnaire);
<|endoftext|>


<commit_before>'use strict';

var isArray    = Array.isArray
  , push       = Array.prototype.push
  , copy       = require('es5-ext/lib/Array/prototype/copy')
  , remove     = require('es5-ext/lib/Array/prototype/remove')
  , compact    = require('es5-ext/lib/Object/compact')
  , forEach    = require('es5-ext/lib/Object/for-each')
  , isObject   = require('es5-ext/lib/Object/is-object')
  , some       = require('es5-ext/lib/Object/some')
  , startsWith = require('es5-ext/lib/String/prototype/starts-with')
  , dirname    = require('path').dirname
  , sep        = require('next/lib/path/sep')
  , getMap     = require('./get-map')

  , reader, calculate, comparePaths, read;

comparePaths = function (a, b) {
	return (a > b) ? 1 : -1;
};

calculate = function (filename, map) {
	var options = {}, paths = {};
	some(map.map, function (map, path) {
		if (path > filename) {
			return true;
		}
		forEach(map, function (map, path) {
			if (!isObject(map)) {
				return;
			}
			if (!startsWith.call(filename, path + sep) && (filename !== path)) {
				return;
			}
			if (paths[path]) {
				paths[path].push(map);
			} else {
				paths[path] = [map];
			}
		});
	}, null, comparePaths);

	forEach(paths, function (data) {
		data.forEach(function (map) {
			forEach(map, function (value, name) {
				var list;
				if (isArray(value)) {
					// list
					if (value.hasOwnProperty('mod')) {
						list = options[name];
						value.forEach(function (mod) {
							switch (mod.action) {
								case 'add':
								if (list) {
									push.apply(list, mod.value);
									list.sort();
								} else {
									list = copy.call(mod.value);
								}
								break;
								case 'remove':
								if (list) {
									remove.apply(list, mod.value);
								}
							}
						});
						if (!list) {
							return;
						}
						value = list;
					} else {
						value = copy.call(value);
					}
				}
				options[name] = value;
			});
		});
	}, null, comparePaths);

	return compact(options);
};

reader = function (filename, mapPromise, watch) {
	var promise, current;
	if (watch) {
		mapPromise.on('change', function (map) {
			var nopts = calculate(filename, map);
			if (!isCopy(current, nopts, Infinity)) {
				current = nopts;
				promise.emit('change', current);
			}
		});
	}
	return (promise = mapPromise(function (map) {
		return (current = calculate(filename, map));
	}));
};

read = module.exports = function (filename, watch) {
	return reader(filename, getMap(dirname(filename), watch), watch);
};
read.reader = reader;
<commit_msg>Require missing dependency<commit_after>'use strict';

var isArray    = Array.isArray
  , push       = Array.prototype.push
  , copy       = require('es5-ext/lib/Array/prototype/copy')
  , remove     = require('es5-ext/lib/Array/prototype/remove')
  , compact    = require('es5-ext/lib/Object/compact')
  , forEach    = require('es5-ext/lib/Object/for-each')
  , isCopy     = require('es5-ext/lib/Object/is-copy')
  , isObject   = require('es5-ext/lib/Object/is-object')
  , some       = require('es5-ext/lib/Object/some')
  , startsWith = require('es5-ext/lib/String/prototype/starts-with')
  , dirname    = require('path').dirname
  , sep        = require('next/lib/path/sep')
  , getMap     = require('./get-map')

  , reader, calculate, comparePaths, read;

comparePaths = function (a, b) {
	return (a > b) ? 1 : -1;
};

calculate = function (filename, map) {
	var options = {}, paths = {};
	some(map.map, function (map, path) {
		if (path > filename) {
			return true;
		}
		forEach(map, function (map, path) {
			if (!isObject(map)) {
				return;
			}
			if (!startsWith.call(filename, path + sep) && (filename !== path)) {
				return;
			}
			if (paths[path]) {
				paths[path].push(map);
			} else {
				paths[path] = [map];
			}
		});
	}, null, comparePaths);

	forEach(paths, function (data) {
		data.forEach(function (map) {
			forEach(map, function (value, name) {
				var list;
				if (isArray(value)) {
					// list
					if (value.hasOwnProperty('mod')) {
						list = options[name];
						value.forEach(function (mod) {
							switch (mod.action) {
								case 'add':
								if (list) {
									push.apply(list, mod.value);
									list.sort();
								} else {
									list = copy.call(mod.value);
								}
								break;
								case 'remove':
								if (list) {
									remove.apply(list, mod.value);
								}
							}
						});
						if (!list) {
							return;
						}
						value = list;
					} else {
						value = copy.call(value);
					}
				}
				options[name] = value;
			});
		});
	}, null, comparePaths);

	return compact(options);
};

reader = function (filename, mapPromise, watch) {
	var promise, current;
	if (watch) {
		mapPromise.on('change', function (map) {
			var nopts = calculate(filename, map);
			if (!isCopy(current, nopts, Infinity)) {
				current = nopts;
				promise.emit('change', current);
			}
		});
	}
	return (promise = mapPromise(function (map) {
		return (current = calculate(filename, map));
	}));
};

read = module.exports = function (filename, watch) {
	return reader(filename, getMap(dirname(filename), watch), watch);
};
read.reader = reader;
<|endoftext|>


<commit_before>/**
 * KumbiaPHP web & app Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://wiki.kumbiaphp.com/Licencia
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@kumbiaphp.com so we can send you a copy immediately.
 *
 * Plugin para jQuery que incluye los callbacks basicos para los Helpers
 *
 * @copyright  Copyright (c) 2005-2009 Kumbia Team (http://www.kumbiaphp.com)
 * @license    http://wiki.kumbiaphp.com/Licencia     New BSD License
 */

(function($) {
    /**
     * Objeto KumbiaPHP
     *
     **/
    $.KumbiaPHP = {
        /**
         * Muestra mensaje de confirmacion
         *
         * @param Object event
         **/
        cConfirm: function(event) {
            if(!confirm(this.title)) {
                event.preventDefault();
            }
        },
        /**
         * Aplica un efecto a un elemento
         *
         * @param String fx
         **/
        cFx: function(fx) {
            return function(event) {
                event.preventDefault();
                (($(this.rel))[fx])();
            }
        },
        /**
         * Carga con AJAX
         *
         * @param Object event
         **/
        cRemote: function(event) {
            event.preventDefault();
            $(this.rel).load(this.href);
        },
        /**
         * Carga con AJAX y Confirmacion
         *
         * @param Object event
         **/
        cRemoteConfirm: function(event) {
            event.preventDefault();
            if(confirm(this.title)) {
                $(this.rel).load(this.href);
            }
        },
        /**
         * Metodo para utilizar con map de jQuery, el cual genera calendarios jsCalendar
         *
         **/
        calendar: function() {
            var tigger = $('#' + this.id + '_tigger').get(0);
            Calendar.setup({ inputField: this.id, ifFormat: tigger.alt, daFormat: tigger.alt, button: tigger.id});
        },

        /**
		 * Enviar formularios de manera asincronica, via POST
		 * Y los carga en un contenedor
		 **/
		cFRemote: function(e){
			e.preventDefault();
			self = $(this);
			var url = self.attr('action');
			var div = self.attr('rel');
			$.post(url, self.serialize(), function(data, status){
				$('#'+div).html(data);
			});
		},

        /**
         * Carga con AJAX al cambiar select
         *
         * @param Object event
         **/
        cUpdaterSelect: function(event) {
            var action = $('meta[name=js.' + this.id + '.action]').attr("content");
			var update = $('meta[name=js.' + this.id + '.update]').attr("content");
            $('#' + update).load(action + this.value);
        },

        /**
         * Enlaza a las clases por defecto
         *
         **/
        bind : function() {
            $("a.js-confirm").live('click', this.cConfirm);
            $("a.js-remote").live('click', this.cRemote);
            $("a.js-remote-confirm").live('click', this.cRemoteConfirm);
            $("a.js-show").live('click', this.cFx('show'));
            $("a.js-hide").live('click', this.cFx('hide'));
            $("a.js-toggle").live('click', this.cFx('toggle'));
            $("a.js-fade-in").live('click', this.cFx('fadeIn'));
            $("a.js-fade-out").live('click', this.cFx('fadeOut'));

            $(".js-ajax").ajaxComplete(function (event, XMLHttpRequest, ajaxOptions) {
                $.KumbiaPHP.bindNoLive(this);
            });

            // enlaza los que no funcionan con live
            this.bindNoLive(document);
        },
        /**
         * Aquellos que no se enlazan automaticamente con live
         *
         * @param DomObject parent
         **/
        bindNoLive : function(parent) {
            // lista desplegable que actualiza con ajax
            $("select.js-remote", parent).bind('change', this.cUpdaterSelect);

            // calendario
            $("input.js-calendar", parent).map(this.calendar);

            // formulario ajax
            $("form.js-remote", parent).bind('submit', this.cFRemote);
        }
    }

     // Enlaza a las clases por defecto
    $(function(){ $.KumbiaPHP.bind(); });
})(jQuery);<commit_msg>Con el fin de reutilizar las clases css del plugin de jquery, se cambio  js-ajax por js-remote, quedando las capas que serviran de contenedores  de peticiones ajax de la siguiente forma:<commit_after>/**
 * KumbiaPHP web & app Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://wiki.kumbiaphp.com/Licencia
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@kumbiaphp.com so we can send you a copy immediately.
 *
 * Plugin para jQuery que incluye los callbacks basicos para los Helpers
 *
 * @copyright  Copyright (c) 2005-2009 Kumbia Team (http://www.kumbiaphp.com)
 * @license    http://wiki.kumbiaphp.com/Licencia     New BSD License
 */

(function($) {
    /**
     * Objeto KumbiaPHP
     *
     **/
    $.KumbiaPHP = {
        /**
         * Muestra mensaje de confirmacion
         *
         * @param Object event
         **/
        cConfirm: function(event) {
            if(!confirm(this.title)) {
                event.preventDefault();
            }
        },
        /**
         * Aplica un efecto a un elemento
         *
         * @param String fx
         **/
        cFx: function(fx) {
            return function(event) {
                event.preventDefault();
                (($(this.rel))[fx])();
            }
        },
        /**
         * Carga con AJAX
         *
         * @param Object event
         **/
        cRemote: function(event) {
            event.preventDefault();
            $(this.rel).load(this.href);
        },
        /**
         * Carga con AJAX y Confirmacion
         *
         * @param Object event
         **/
        cRemoteConfirm: function(event) {
            event.preventDefault();
            if(confirm(this.title)) {
                $(this.rel).load(this.href);
            }
        },
        /**
         * Metodo para utilizar con map de jQuery, el cual genera calendarios jsCalendar
         *
         **/
        calendar: function() {
            var tigger = $('#' + this.id + '_tigger').get(0);
            Calendar.setup({ inputField: this.id, ifFormat: tigger.alt, daFormat: tigger.alt, button: tigger.id});
        },

        /**
		 * Enviar formularios de manera asincronica, via POST
		 * Y los carga en un contenedor
		 **/
		cFRemote: function(e){
			e.preventDefault();
			self = $(this);
			var url = self.attr('action');
			var div = self.attr('rel');
			$.post(url, self.serialize(), function(data, status){
				$('#'+div).html(data);
			});
		},

        /**
         * Carga con AJAX al cambiar select
         *
         * @param Object event
         **/
        cUpdaterSelect: function(event) {
            var action = $('meta[name=js.' + this.id + '.action]').attr("content");
			var update = $('meta[name=js.' + this.id + '.update]').attr("content");
            $('#' + update).load(action + this.value);
        },

        /**
         * Enlaza a las clases por defecto
         *
         **/
        bind : function() {
            $("a.js-confirm").live('click', this.cConfirm);
            $("a.js-remote").live('click', this.cRemote);
            $("a.js-remote-confirm").live('click', this.cRemoteConfirm);
            $("a.js-show").live('click', this.cFx('show'));
            $("a.js-hide").live('click', this.cFx('hide'));
            $("a.js-toggle").live('click', this.cFx('toggle'));
            $("a.js-fade-in").live('click', this.cFx('fadeIn'));
            $("a.js-fade-out").live('click', this.cFx('fadeOut'));

            $("div.js-remote").ajaxComplete(function (event, XMLHttpRequest, ajaxOptions) {
                $.KumbiaPHP.bindNoLive(this);
            });

            // enlaza los que no funcionan con live
            this.bindNoLive(document);
        },
        /**
         * Aquellos que no se enlazan automaticamente con live
         *
         * @param DomObject parent
         **/
        bindNoLive : function(parent) {
            // lista desplegable que actualiza con ajax
            $("select.js-remote", parent).bind('change', this.cUpdaterSelect);

            // calendario
            $("input.js-calendar", parent).map(this.calendar);

            // formulario ajax
            $("form.js-remote", parent).bind('submit', this.cFRemote);
        }
    }

     // Enlaza a las clases por defecto
    $(function(){ $.KumbiaPHP.bind(); });
})(jQuery);<|endoftext|>


<commit_before>import { isNull, isArray } from "lodash"
import { Client } from "memjs"
import config from "config"
import { error } from "./loggers"

const { NODE_ENV } = process.env
const { CACHE_LIFETIME_IN_SECONDS } = config

const createClient = () => {
  if (NODE_ENV === "test") {
    const store = {}
    return {
      store,
      get: (key, cb) => cb(null, store[key]),
      set: (key, data) => (store[key] = data),
    }
  }
  return Client.create(null, {
    expires: CACHE_LIFETIME_IN_SECONDS,
  })
}

export const client = createClient()

export default {
  get: key => {
    return new Promise((resolve, reject) => {
      if (isNull(client)) return reject(new Error("Cache client is `null`"))

      client.get(key, (err, data) => {
        if (err) return reject(err)
        if (data) return resolve(JSON.parse(data))
        reject(new Error("cache#get did not return `data`"))
      })
    })
  },

  set: (key, data) => {
    if (isNull(client)) return false

    const timestamp = new Date().getTime()
    if (isArray(data)) {
      data.map(datum => (datum.cached = timestamp)) // eslint-disable-line no-param-reassign
    } else {
      data.cached = timestamp // eslint-disable-line no-param-reassign
    }

    return client.set(key, JSON.stringify(data), err => {
      if (err) error(err)
    })
  },

  delete: key =>
    new Promise((resolve, reject) =>
      client.delete(key, (err, response) => {
        if (err) return reject(err)
        resolve(response)
      })
    ),
}
<commit_msg>[memjs] Fix arguments deprecation warning.<commit_after>import { isNull, isArray } from "lodash"
import { Client } from "memjs"
import config from "config"
import { error } from "./loggers"

const { NODE_ENV } = process.env
const { CACHE_LIFETIME_IN_SECONDS } = config

const createClient = () => {
  if (NODE_ENV === "test") {
    const store = {}
    return {
      store,
      get: (key, cb) => cb(null, store[key]),
      set: (key, data) => (store[key] = data),
    }
  }
  return Client.create()
}

export const client = createClient()

export default {
  get: key => {
    return new Promise((resolve, reject) => {
      if (isNull(client)) return reject(new Error("Cache client is `null`"))

      client.get(key, (err, data) => {
        if (err) return reject(err)
        if (data) return resolve(JSON.parse(data))
        reject(new Error("cache#get did not return `data`"))
      })
    })
  },

  set: (key, data) => {
    if (isNull(client)) return false

    const timestamp = new Date().getTime()
    if (isArray(data)) {
      data.map(datum => (datum.cached = timestamp)) // eslint-disable-line no-param-reassign
    } else {
      data.cached = timestamp // eslint-disable-line no-param-reassign
    }

    return client.set(key, JSON.stringify(data), { expires: CACHE_LIFETIME_IN_SECONDS }, err => {
      if (err) error(err)
    })
  },

  delete: key =>
    new Promise((resolve, reject) =>
      client.delete(key, (err, response) => {
        if (err) return reject(err)
        resolve(response)
      })
    ),
}
<|endoftext|>


<commit_before>var updateQueryStats = function(element){
    //fragment=-1 means getting all the data
    var shuffleUrl = templates.urls.sentData({
            myria: myriaConnection,
            query: queryPlan.queryId,
            fragment: -1
        });

    d3.csv(shuffleUrl, function(d) {
            d.numTuples = +d.numTuples;
            return d;
        }, function (data) {
            $(element.node()).empty();
            var div = element.append("div")
                .attr("class", "query-stats");
            var h = div.append("h4")
                .text("Query stats:");
            var totalTuple = data.reduce(function(a,b){
                return a + b.numTuples;
            }, 0);
            var items = "";
            items += templates.defItem({key: "Running time:", value: customFullTimeFormat(queryPlan.elapsedNanos, false)});
            items += templates.defItem({key: "# shuffled tuples:", value: Intl.NumberFormat().format(totalTuple)});
            var dl = templates.defList({items: items});
            $(".query-stats").append(dl);
        });
};<commit_msg>Use aggregated data in stats cause it’s smaller and we can use the cached version<commit_after>var updateQueryStats = function(element) {
    var shuffleUrl = templates.urls.aggregatedSentData({
            myria: myriaConnection,
            query: queryPlan.queryId
        });

    d3.csv(shuffleUrl, function(d) {
            d.numTuples = +d.numTuples;
            return d;
        }, function (data) {
            $(element.node()).empty();
            var div = element.append("div")
                .attr("class", "query-stats");
            var h = div.append("h4")
                .text("Query stats:");
            var totalTuple = data.reduce(function(a,b){
                return a + b.numTuples;
            }, 0);
            var items = "";
            items += templates.defItem({key: "Running time:", value: customFullTimeFormat(queryPlan.elapsedNanos, false)});
            items += templates.defItem({key: "# shuffled tuples:", value: Intl.NumberFormat().format(totalTuple)});
            var dl = templates.defList({items: items});
            $(".query-stats").append(dl);
        });
};<|endoftext|>


<commit_before>export default function assertString(input) {
  const isString = (typeof input === 'string' || input instanceof String);

  if (!isString) {
    let invalidType;
    if (input === null) {
      invalidType = 'null';
    } else {
      invalidType = typeof input;
      if (invalidType === 'object' && input.constructor) {
        invalidType = input.constructor.name;
      } else {
        invalidType = `a ${invalidType}`;
      }
    }
    let message = 'This library (validator.js) validates strings only, ';
    message += `instead it received ${invalidType}.`;
    throw new TypeError(message);
  }
}
<commit_msg>Update assertString.js<commit_after>export default function assertString(input) {
  const isString = (typeof input === 'string' || input instanceof String);

  if (!isString) {
    let invalidType;
    if (input === null) {
      invalidType = 'null';
    } else {
      invalidType = typeof input;
      if (invalidType === 'object' && input.constructor && input.constructor.hasOwnProperty('name')) {
        invalidType = input.constructor.name;
      } else {
        invalidType = `a ${invalidType}`;
      }
    }
    let message = 'This library (validator.js) validates strings only, ';
    message += `instead it received ${invalidType}.`;
    throw new TypeError(message);
  }
}
<|endoftext|>


<commit_before>
// TODO: export to external module
//       when i do, add option to not fully traverse into a directory,
//       and option to not traverse into symlinks
//
// TODO: limit max parallellism
// TODO: UNIT TESTS!!! ROBUST CONTRACTS W.R.T. INPUT AND OUTPUT

// tries to match globs with a target destination, so things like
// {src: ['packages/*/src'], dest: ['dist']} are possible.
//
// eventually, something like
//
// {src: ['packages/*/src/*'], dest: ['packages/*/lib']}
//
// should be possible.
//
// For now, something like `scripts/_get-test-directories.sh` can be used for this multi src-dest purpose.

import path from "path";

import glob from "glob";
import globCommon from "glob/common";
const {Glob} = glob;
import isGlob from "is-glob";
import forEach from "lodash/forEach";
import map from "lodash/map";
import isArray from "lodash/isArray";

import asyncBlock from "./_async-block";

function ownProp (obj, field) {
  return Object.prototype.hasOwnProperty.call(obj, field)
}

export default function walk(args: Object, eachCb, cb) {
  return new Walker(args, eachCb, cb).walk(args);
}

// it's a zombie! run!
export class Walker {
  constructor(...args) {
    this.cb = args.pop();
    this.eachCb = args.pop();
    const opts = args[0] || {};

    this.destExt = opts.destExt;

    this._asyncBlock = asyncBlock((err) => {
      if (err) {
        if (!this.aborted) this.abort();
        return (0, this.cb)(err);
      }
      return (0, this.cb)();
    });
    this.retain = this._asyncBlock.retain;
    this.release = this._asyncBlock.release;

    // cwd & root logic borrowed from glob, used in makeAbs
    this.changedCwd = false;
    const cwd = process.cwd();
    if (!ownProp(opts, "cwd"))
      this.cwd = cwd;
    else {
      this.cwd = path.resolve(opts.cwd);
      this.changedCwd = this.cwd !== cwd;
    }

    this.root = opts.root ? path.resolve(opts.root) : path.resolve(this.cwd, "/");
    if (process.platform === "win32") this.root = this.root.replace(/\\/g, "/")

    this._pending = 0;

    this.nextGlobId = 0;
    this.pendingGlobs = Object.create(null);

    this.aborted = false;
    this.seen = Object.create(null);

    // Shared cache between different glob invokations
    this.realpathCache = Object.create(null);
    this.globCache = Object.create(null);
    this.statCache = Object.create(null);
    this.symlinks = Object.create(null);
  }

  walk({src: globs, dest: dests}) {
    if (this._pending > 0) throw new Error("Not reentrant");

    const hasDest = !!dests;
    const oneDest = hasDest && dests.length === 1;
    // assert oneDest || dests.length === globs.length
    for (let i = 0, len = globs.length; i < len; i++) {
      const globPat = globs[i];
      const dest = hasDest && (oneDest ? dests[0] : dests[i]);
      if (isGlob(globPat)) {
        this.walkGlob(globPat, dest);
      } else {
        this.walkPathDirect(globPat, dest);
      }
    }
    return this;
  }

  _globOpts(opts = {}) {
    opts.realpathCache = this.realpathCache;
    opts.cache = this.globCache;
    opts.statCache = this.statCache;
    opts.symlinks = this.symlinks;
    return opts;
  }

  walkGlob(globPat, dest) {
    // TODO: match dest according to globbing
    const dests = dest ? [dest] : false;

    const globberId = this.nextGlobId++;
    // stat all paths, since we'll be walking into found directories
    this.retain();
    this.pendingGlobs[globberId] = new Glob(globPat, this._globOpts({stat: true}), (err, matches) => {
      if (this.aborted) return;
      if (err) return this.abort(err);

      this.pendingGlobs[globberId] = undefined;
      this.walkPaths(matches, dests, globPat.slice(-1) !== "*");
      this.release();
    });
  }

  walkPaths(paths, dests, direct) {
    const hasDest = !!dests;
    const oneDest = hasDest && dests.length === 1;
    // assert oneDest || dests.length === paths.length
    for (let i = 0, len = paths.length; i < len; i++) {
      const src = paths[i]
      const dest = hasDest && (oneDest ? path.join(dests[0], path.basename(src)) : dests[i]);
      this.walkPath(src, dest, direct);
    }
  }

  _makeAbs(f) {
    return globCommon.makeAbs(this, f);
  }

  walkPathDirect(src, dest) {
    // perform a glob just to populate the stat cache
    const globberId = this.nextGlobId++;
    this.retain();
    this.pendingGlobs[globberId] = new Glob(src, this._globOpts({stat: true}), (err) => {
      if (this.aborted) return;
      if (err) return this.abort(err);

      this.pendingGlobs[globberId] = undefined;
      this.walkPath(src, dest, true);
      this.release();
    });
  }

  walkPath(src, dest, direct = false) {
    const cacheKey = this._makeAbs(src);
    const seen = this.seen[cacheKey];
    if (seen) return;
    this.seen[cacheKey] = true;

    const cacheItem = this.globCache[cacheKey];

    if (cacheItem === "DIR" || isArray(cacheItem)) {
      const globberId = this.nextGlobId++;
      this.retain();
      this.pendingGlobs[globberId] = new Glob(src + "/**", this._globOpts(), (err, matches) => {
        if (this.aborted) return;
        if (err) return this.abort(err);

        this.pendingGlobs[globberId] = undefined;

        if (direct) {
          const dests = map(matches, (match) => path.join(dest, this.removeLeadingParentDirs(match)));

          this.walkPaths(matches, dests);
        } else {
          const srcPathLen = src.length;
          const dests = map(matches, (match) => dest + match.slice(srcPathLen));

          this.walkPaths(matches, dests);
        }
        this.release();
      });
    } else {
      (0, this.eachCb)(src, this.replaceExt(dest));
    }
  }

  abort(err) {
    this.aborted = true;
    forEach(this.pendingGlobs, (globber) => { globber && globber.abort(); });
    this.pendingGlobs = Object.create(null);
    this._asyncBlock.error(err);
  }

  replaceExt(dest) {
    if (!this.destExt) return dest;
    const p = path.parse(dest);
    p.base = path.basename(p.base, p.ext) + this.destExt;
    return path.format(p);
  }

  removeLeadingParentDirs(dest) {
    const normDest = path.normalize(dest);
    const i = normDest.lastIndexOf("../");
    return i >= 0 ? normDest.slice(i + 3) : dest;
  }
}
<commit_msg>chore: remove addressed comment<commit_after>
// TODO: export to external module
//       when i do, add option to not fully traverse into a directory,
//       and option to not traverse into symlinks
//
// TODO: UNIT TESTS!!! ROBUST CONTRACTS W.R.T. INPUT AND OUTPUT

// tries to match globs with a target destination, so things like
// {src: ['packages/*/src'], dest: ['dist']} are possible.
//
// eventually, something like
//
// {src: ['packages/*/src/*'], dest: ['packages/*/lib']}
//
// should be possible.
//
// For now, something like `scripts/_get-test-directories.sh` can be used for this multi src-dest purpose.

import path from "path";

import glob from "glob";
import globCommon from "glob/common";
const {Glob} = glob;
import isGlob from "is-glob";
import forEach from "lodash/forEach";
import map from "lodash/map";
import isArray from "lodash/isArray";

import asyncBlock from "./_async-block";

function ownProp (obj, field) {
  return Object.prototype.hasOwnProperty.call(obj, field)
}

export default function walk(args: Object, eachCb, cb) {
  return new Walker(args, eachCb, cb).walk(args);
}

// it's a zombie! run!
export class Walker {
  constructor(...args) {
    this.cb = args.pop();
    this.eachCb = args.pop();
    const opts = args[0] || {};

    this.destExt = opts.destExt;

    this._asyncBlock = asyncBlock((err) => {
      if (err) {
        if (!this.aborted) this.abort();
        return (0, this.cb)(err);
      }
      return (0, this.cb)();
    });
    this.retain = this._asyncBlock.retain;
    this.release = this._asyncBlock.release;

    // cwd & root logic borrowed from glob, used in makeAbs
    this.changedCwd = false;
    const cwd = process.cwd();
    if (!ownProp(opts, "cwd"))
      this.cwd = cwd;
    else {
      this.cwd = path.resolve(opts.cwd);
      this.changedCwd = this.cwd !== cwd;
    }

    this.root = opts.root ? path.resolve(opts.root) : path.resolve(this.cwd, "/");
    if (process.platform === "win32") this.root = this.root.replace(/\\/g, "/")

    this._pending = 0;

    this.nextGlobId = 0;
    this.pendingGlobs = Object.create(null);

    this.aborted = false;
    this.seen = Object.create(null);

    // Shared cache between different glob invokations
    this.realpathCache = Object.create(null);
    this.globCache = Object.create(null);
    this.statCache = Object.create(null);
    this.symlinks = Object.create(null);
  }

  walk({src: globs, dest: dests}) {
    if (this._pending > 0) throw new Error("Not reentrant");

    const hasDest = !!dests;
    const oneDest = hasDest && dests.length === 1;
    // assert oneDest || dests.length === globs.length
    for (let i = 0, len = globs.length; i < len; i++) {
      const globPat = globs[i];
      const dest = hasDest && (oneDest ? dests[0] : dests[i]);
      if (isGlob(globPat)) {
        this.walkGlob(globPat, dest);
      } else {
        this.walkPathDirect(globPat, dest);
      }
    }
    return this;
  }

  _globOpts(opts = {}) {
    opts.realpathCache = this.realpathCache;
    opts.cache = this.globCache;
    opts.statCache = this.statCache;
    opts.symlinks = this.symlinks;
    return opts;
  }

  walkGlob(globPat, dest) {
    // TODO: match dest according to globbing
    const dests = dest ? [dest] : false;

    const globberId = this.nextGlobId++;
    // stat all paths, since we'll be walking into found directories
    this.retain();
    this.pendingGlobs[globberId] = new Glob(globPat, this._globOpts({stat: true}), (err, matches) => {
      if (this.aborted) return;
      if (err) return this.abort(err);

      this.pendingGlobs[globberId] = undefined;
      this.walkPaths(matches, dests, globPat.slice(-1) !== "*");
      this.release();
    });
  }

  walkPaths(paths, dests, direct) {
    const hasDest = !!dests;
    const oneDest = hasDest && dests.length === 1;
    // assert oneDest || dests.length === paths.length
    for (let i = 0, len = paths.length; i < len; i++) {
      const src = paths[i]
      const dest = hasDest && (oneDest ? path.join(dests[0], path.basename(src)) : dests[i]);
      this.walkPath(src, dest, direct);
    }
  }

  _makeAbs(f) {
    return globCommon.makeAbs(this, f);
  }

  walkPathDirect(src, dest) {
    // perform a glob just to populate the stat cache
    const globberId = this.nextGlobId++;
    this.retain();
    this.pendingGlobs[globberId] = new Glob(src, this._globOpts({stat: true}), (err) => {
      if (this.aborted) return;
      if (err) return this.abort(err);

      this.pendingGlobs[globberId] = undefined;
      this.walkPath(src, dest, true);
      this.release();
    });
  }

  walkPath(src, dest, direct = false) {
    const cacheKey = this._makeAbs(src);
    const seen = this.seen[cacheKey];
    if (seen) return;
    this.seen[cacheKey] = true;

    const cacheItem = this.globCache[cacheKey];

    if (cacheItem === "DIR" || isArray(cacheItem)) {
      const globberId = this.nextGlobId++;
      this.retain();
      this.pendingGlobs[globberId] = new Glob(src + "/**", this._globOpts(), (err, matches) => {
        if (this.aborted) return;
        if (err) return this.abort(err);

        this.pendingGlobs[globberId] = undefined;

        if (direct) {
          const dests = map(matches, (match) => path.join(dest, this.removeLeadingParentDirs(match)));

          this.walkPaths(matches, dests);
        } else {
          const srcPathLen = src.length;
          const dests = map(matches, (match) => dest + match.slice(srcPathLen));

          this.walkPaths(matches, dests);
        }
        this.release();
      });
    } else {
      (0, this.eachCb)(src, this.replaceExt(dest));
    }
  }

  abort(err) {
    this.aborted = true;
    forEach(this.pendingGlobs, (globber) => { globber && globber.abort(); });
    this.pendingGlobs = Object.create(null);
    this._asyncBlock.error(err);
  }

  replaceExt(dest) {
    if (!this.destExt) return dest;
    const p = path.parse(dest);
    p.base = path.basename(p.base, p.ext) + this.destExt;
    return path.format(p);
  }

  removeLeadingParentDirs(dest) {
    const normDest = path.normalize(dest);
    const i = normDest.lastIndexOf("../");
    return i >= 0 ? normDest.slice(i + 3) : dest;
  }
}
<|endoftext|>