<commit_before>import {
    CREATE_BLOCK,
    CREATE_BLOCK_WITH_CONNECTION,
    CREATE_CONNECTION,
    CREATE_PIPELINE,
    CREATE_PIPELINE_FROM_JSON,
    DELETE_NODE,
    DUMP_JSON,
    INSPECT_NODE,
    LINK_INPUT,
    LINK_OUTPUT,
    NODE_CHANGED,
    CONNECTION_ERROR,
    RESET_NODE,
    RESET_PIPELINE,
    RUN_ERROR,
    RUN_NODE,
    RUN_PIPELINE,
    SET_CURRENT_PIPELINE,
    SET_BLOCK_OPTIONS,
    SET_NODE_TITLE,
    UPDATE_GRAPH,
    UPDATE_NODES,
    UPDATE_NODES_AND_GRAPH,
    setCurrentPipeline,
    nodeChanged
} from './actions';

export const memoryMiddleware = env => store => {
    // Create root pipeline
    const rootPipeline = env.newPipeline();
    rootPipeline.setTitle('ROOT');
    let currentPipeline = rootPipeline;

    // Create dummy node on the pipeline
    const ten = rootPipeline.newNode('number', {value: 10});

    const pIn = env.newPipeline();
    const five = pIn.newNode('number', {value: 5});
    const prod = pIn.newNode('product');
    pIn.connect(five, prod);
    pIn.linkInput(prod.input(), 'number');
    pIn.linkOutput(prod.output(), 'result');

    rootPipeline.addNode(pIn);
    rootPipeline.connect(ten, pIn.input('number'));

    let ignoreEvents = false;
    const setIgnoreEvents = () => ignoreEvents = true;
    const unsetIgnoreEvents = () => ignoreEvents = false;

    // Listen to status changes in the pipeline to dispatch actions
    rootPipeline.on('child-change', (node) => {
        store.dispatch(nodeChanged(node));
        if (ignoreEvents) return;
        store.dispatch(createUpdateNodesAction(rootPipeline));
    });

    rootPipeline.on('deep-child-change', node => {
        store.dispatch(nodeChanged(node));
        if (ignoreEvents) return;
        store.dispatch(createUpdateNodesAction(rootPipeline));
    });

    rootPipeline.on('change', () => {
        store.dispatch(nodeChanged(rootPipeline));
        if (ignoreEvents) return;
        store.dispatch(createUpdateNodesAction(rootPipeline));
    });

    rootPipeline.on('runError', function (err) {
        console.log(err); // eslint-disable-line no-console
        store.dispatch({
            type: RUN_ERROR,
            payload: {
                message: err.message
            }
        });
    });

    // Update on initialization
    store.dispatch(createUpdateNodesAction(rootPipeline));
    store.dispatch(createUpdateGraphAction(rootPipeline));
    store.dispatch(setCurrentPipeline(rootPipeline.toJSON()));

    return next => action => {
        if (action.type.startsWith('@@larissa/')) {
            switch (action.type) {
                case DUMP_JSON: {
                    console.log(JSON.stringify(currentPipeline));
                    return null;
                }
                case CREATE_BLOCK: {
                    setIgnoreEvents();
                    currentPipeline.newNode(action.payload.type);
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case CREATE_BLOCK_WITH_CONNECTION: {
                    setIgnoreEvents();
                    const nodeId = action.payload.node;
                    const newNode = currentPipeline.newNode(action.payload.type);
                    try {
                        const node = currentPipeline.getNode(nodeId);
                        if (node) {
                            if (action.payload.portType === 'input') {
                                currentPipeline.connect(newNode, node.input(action.payload.portName));
                            }
                            if (action.payload.portType === 'output') {
                                currentPipeline.connect(node.output(action.payload.portName), newNode);
                            }
                        }
                    } catch (e) {
                        currentPipeline.deleteNode(newNode);
                        return next({
                            type: CONNECTION_ERROR,
                            payload: e.message
                        });
                    }
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case SET_BLOCK_OPTIONS: {
                    const node = currentPipeline.getNode(action.payload.id);
                    if (node.kind !== 'block') throw new Error('Setting options on not-a-block');
                    node.setOptions(action.payload.options);
                    // No need for dispatching. Listener will detect the reset of the Block
                    return null;
                }
                case RUN_PIPELINE: {
                    currentPipeline.run().catch((err) => {
                        console.error(err); // eslint-disable-line no-console
                        next({
                            type: RUN_ERROR,
                            payload: {
                                message: err.message
                            }
                        });
                    });
                    break;
                }
                case RESET_PIPELINE: {
                    currentPipeline.reset();
                    return null;
                }
                case CREATE_PIPELINE: {
                    setIgnoreEvents();
                    const newPipeline = env.newPipeline();
                    currentPipeline.addNode(newPipeline);
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case CREATE_PIPELINE_FROM_JSON: {
                    setIgnoreEvents();
                    const newPipeline = env.pipelineFromJSON(action.payload);
                    currentPipeline.addNode(newPipeline);
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case UPDATE_GRAPH: // Just pass the action to the end user
                case UPDATE_NODES:
                case RUN_ERROR:
                    next(action);
                    return null;
                case DELETE_NODE: {
                    setIgnoreEvents();
                    currentPipeline.deleteNode(currentPipeline.getNode(action.payload));
                    unsetIgnoreEvents();
                    next(action);
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case SET_CURRENT_PIPELINE: {
                    const newCurrentPipeline = rootPipeline.findNode(action.payload.id);
                    if (!newCurrentPipeline) {
                        throw new Error(`Pipeline with id ${action.payload.id} was not found`);
                    }
                    if (newCurrentPipeline.kind !== 'pipeline') {
                        throw new Error('Setting pipeline to be not-a-pipeline');
                    }
                    currentPipeline = newCurrentPipeline;
                    next(action);
                    return next(createUpdateGraphAction(currentPipeline));
                }
                case LINK_INPUT: {
                    setIgnoreEvents();
                    const pipeline = currentPipeline.getNode(action.payload.id);
                    const nodeId = action.payload.input.node.id;
                    const inputName = action.payload.input.info.name;
                    const linkName = action.payload.name;
                    const node = pipeline.getNode(nodeId);
                    pipeline.linkInput(node.input(inputName), linkName);
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case LINK_OUTPUT: {
                    setIgnoreEvents();
                    const pipeline = currentPipeline.getNode(action.payload.id);
                    const nodeId = action.payload.output.node.id;
                    const outputName = action.payload.output.info.name;
                    const linkName = action.payload.name;
                    const node = pipeline.getNode(nodeId);
                    pipeline.linkOutput(node.output(outputName), linkName);
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case INSPECT_NODE: {
                    const node = currentPipeline.findNode(action.payload);
                    if (!node) {
                        throw new Error(`Node with id ${action.payload} was not found for inspection`);
                    }
                    action.payload = node.inspect();
                    next(action);
                    return null;
                }
                case NODE_CHANGED: {
                    action.payload = action.payload.inspect();
                    next(action);
                    return null;
                }
                case SET_NODE_TITLE: {
                    const node = rootPipeline.findNode(action.payload.id);
                    if (!node) throw new Error(`Node with id ${action.payload.id} was not found for setting its title`);
                    node.setTitle(action.payload.value);
                    return null;
                }
                case CREATE_CONNECTION: {
                    const source = action.payload.source;
                    const dest = action.payload.dest;
                    const sourceNode = currentPipeline.getNode(source.node);
                    const destNode = currentPipeline.getNode(dest.node);
                    if (!sourceNode) {
                        throw new Error(`Source node with id ${source.node} was not found to create a connection`);
                    }
                    if (!destNode) {
                        throw new Error(`Destination node with id ${source.node} was not found to create a connection`);
                    }
                    try {
                        setIgnoreEvents();
                        currentPipeline.connect(sourceNode.output(source.name), destNode.input(dest.name));
                        return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                    } catch (e) {
                        let message = e.message;
                        if (message.includes('cycle')) {
                            message = 'Cycles are not allowed';
                        }
                        return next({
                            type: CONNECTION_ERROR,
                            payload: message
                        });
                    } finally {
                        unsetIgnoreEvents();
                    }
                }
                case RUN_NODE:
                    currentPipeline.runNode(action.payload);
                    return next(createUpdateGraphAction(currentPipeline));
                case RESET_NODE: {
                    const node = currentPipeline.getNode(action.payload);
                    node.reset();
                    return next(createUpdateGraphAction(currentPipeline));
                }
                default: {
                    throw new Error(`Unexpected action: ${action.type}`);
                }
            }
        }
        return next(action);
    };

    function dispatchUpdateNodesAndGraphAction(pipeline, next) {
        next({
            type: UPDATE_NODES_AND_GRAPH,
            payload: {
                nodes: createUpdateNodesAction(rootPipeline).payload,
                graph: createUpdateGraphAction(pipeline).payload
            }
        });
    }
};

function createUpdateGraphAction(pipeline) {
    return {
        type: UPDATE_GRAPH,
        payload: JSON.parse(pipeline.graph.toJSON())
    };
}

function createUpdateNodesAction(pipeline) {
    const nodes = {};
    for (const node of pipeline.nodes()) {
        nodes[node.id] = node.inspect();
    }
    return {
        type: UPDATE_NODES,
        payload: nodes
    };
}
<commit_msg>preload predefined pipelines for demo<commit_after>import {
    CREATE_BLOCK,
    CREATE_BLOCK_WITH_CONNECTION,
    CREATE_CONNECTION,
    CREATE_PIPELINE,
    CREATE_PIPELINE_FROM_JSON,
    DELETE_NODE,
    DUMP_JSON,
    INSPECT_NODE,
    LINK_INPUT,
    LINK_OUTPUT,
    NODE_CHANGED,
    CONNECTION_ERROR,
    RESET_NODE,
    RESET_PIPELINE,
    RUN_ERROR,
    RUN_NODE,
    RUN_PIPELINE,
    SET_CURRENT_PIPELINE,
    SET_BLOCK_OPTIONS,
    SET_NODE_TITLE,
    UPDATE_GRAPH,
    UPDATE_NODES,
    UPDATE_NODES_AND_GRAPH,
    setCurrentPipeline,
    nodeChanged
} from './actions';

import predefinedPipelines from '../predefinedPipelines';

export const memoryMiddleware = env => store => {
    // Create root pipeline
    const rootPipeline = env.newPipeline();
    rootPipeline.setTitle('ROOT');
    let currentPipeline = rootPipeline;

    // Add demos
    for (const pipeline of predefinedPipelines) {
        const newPipeline = env.pipelineFromJSON(pipeline.value);
        rootPipeline.addNode(newPipeline);
    }

    {
        // Create dummy node on the pipeline
        const ten = rootPipeline.newNode('number', {value: 10});

        const pIn = env.newPipeline();
        const five = pIn.newNode('number', {value: 5});
        const prod = pIn.newNode('product');
        pIn.connect(five, prod);
        pIn.linkInput(prod.input(), 'number');
        pIn.linkOutput(prod.output(), 'result');

        rootPipeline.addNode(pIn);
        rootPipeline.connect(ten, pIn.input('number'));
    }

    let ignoreEvents = false;
    const setIgnoreEvents = () => ignoreEvents = true;
    const unsetIgnoreEvents = () => ignoreEvents = false;

    // Listen to status changes in the pipeline to dispatch actions
    rootPipeline.on('child-change', (node) => {
        store.dispatch(nodeChanged(node));
        if (ignoreEvents) return;
        store.dispatch(createUpdateNodesAction(rootPipeline));
    });

    rootPipeline.on('deep-child-change', node => {
        store.dispatch(nodeChanged(node));
        if (ignoreEvents) return;
        store.dispatch(createUpdateNodesAction(rootPipeline));
    });

    rootPipeline.on('change', () => {
        store.dispatch(nodeChanged(rootPipeline));
        if (ignoreEvents) return;
        store.dispatch(createUpdateNodesAction(rootPipeline));
    });

    rootPipeline.on('runError', function (err) {
        console.log(err); // eslint-disable-line no-console
        store.dispatch({
            type: RUN_ERROR,
            payload: {
                message: err.message
            }
        });
    });

    // Update on initialization
    store.dispatch(createUpdateNodesAction(rootPipeline));
    store.dispatch(createUpdateGraphAction(rootPipeline));
    store.dispatch(setCurrentPipeline(rootPipeline.toJSON()));

    return next => action => {
        if (action.type.startsWith('@@larissa/')) {
            switch (action.type) {
                case DUMP_JSON: {
                    console.log(JSON.stringify(currentPipeline));
                    return null;
                }
                case CREATE_BLOCK: {
                    setIgnoreEvents();
                    currentPipeline.newNode(action.payload.type);
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case CREATE_BLOCK_WITH_CONNECTION: {
                    setIgnoreEvents();
                    const nodeId = action.payload.node;
                    const newNode = currentPipeline.newNode(action.payload.type);
                    try {
                        const node = currentPipeline.getNode(nodeId);
                        if (node) {
                            if (action.payload.portType === 'input') {
                                currentPipeline.connect(newNode, node.input(action.payload.portName));
                            }
                            if (action.payload.portType === 'output') {
                                currentPipeline.connect(node.output(action.payload.portName), newNode);
                            }
                        }
                    } catch (e) {
                        currentPipeline.deleteNode(newNode);
                        return next({
                            type: CONNECTION_ERROR,
                            payload: e.message
                        });
                    }
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case SET_BLOCK_OPTIONS: {
                    const node = currentPipeline.getNode(action.payload.id);
                    if (node.kind !== 'block') throw new Error('Setting options on not-a-block');
                    node.setOptions(action.payload.options);
                    // No need for dispatching. Listener will detect the reset of the Block
                    return null;
                }
                case RUN_PIPELINE: {
                    currentPipeline.run().catch((err) => {
                        console.error(err); // eslint-disable-line no-console
                        next({
                            type: RUN_ERROR,
                            payload: {
                                message: err.message
                            }
                        });
                    });
                    break;
                }
                case RESET_PIPELINE: {
                    currentPipeline.reset();
                    return null;
                }
                case CREATE_PIPELINE: {
                    setIgnoreEvents();
                    const newPipeline = env.newPipeline();
                    currentPipeline.addNode(newPipeline);
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case CREATE_PIPELINE_FROM_JSON: {
                    setIgnoreEvents();
                    const newPipeline = env.pipelineFromJSON(action.payload);
                    currentPipeline.addNode(newPipeline);
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case UPDATE_GRAPH: // Just pass the action to the end user
                case UPDATE_NODES:
                case RUN_ERROR:
                    next(action);
                    return null;
                case DELETE_NODE: {
                    setIgnoreEvents();
                    currentPipeline.deleteNode(currentPipeline.getNode(action.payload));
                    unsetIgnoreEvents();
                    next(action);
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case SET_CURRENT_PIPELINE: {
                    const newCurrentPipeline = rootPipeline.findNode(action.payload.id);
                    if (!newCurrentPipeline) {
                        throw new Error(`Pipeline with id ${action.payload.id} was not found`);
                    }
                    if (newCurrentPipeline.kind !== 'pipeline') {
                        throw new Error('Setting pipeline to be not-a-pipeline');
                    }
                    currentPipeline = newCurrentPipeline;
                    next(action);
                    return next(createUpdateGraphAction(currentPipeline));
                }
                case LINK_INPUT: {
                    setIgnoreEvents();
                    const pipeline = currentPipeline.getNode(action.payload.id);
                    const nodeId = action.payload.input.node.id;
                    const inputName = action.payload.input.info.name;
                    const linkName = action.payload.name;
                    const node = pipeline.getNode(nodeId);
                    pipeline.linkInput(node.input(inputName), linkName);
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case LINK_OUTPUT: {
                    setIgnoreEvents();
                    const pipeline = currentPipeline.getNode(action.payload.id);
                    const nodeId = action.payload.output.node.id;
                    const outputName = action.payload.output.info.name;
                    const linkName = action.payload.name;
                    const node = pipeline.getNode(nodeId);
                    pipeline.linkOutput(node.output(outputName), linkName);
                    unsetIgnoreEvents();
                    return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                }
                case INSPECT_NODE: {
                    const node = currentPipeline.findNode(action.payload);
                    if (!node) {
                        throw new Error(`Node with id ${action.payload} was not found for inspection`);
                    }
                    action.payload = node.inspect();
                    next(action);
                    return null;
                }
                case NODE_CHANGED: {
                    action.payload = action.payload.inspect();
                    next(action);
                    return null;
                }
                case SET_NODE_TITLE: {
                    const node = rootPipeline.findNode(action.payload.id);
                    if (!node) throw new Error(`Node with id ${action.payload.id} was not found for setting its title`);
                    node.setTitle(action.payload.value);
                    return null;
                }
                case CREATE_CONNECTION: {
                    const source = action.payload.source;
                    const dest = action.payload.dest;
                    const sourceNode = currentPipeline.getNode(source.node);
                    const destNode = currentPipeline.getNode(dest.node);
                    if (!sourceNode) {
                        throw new Error(`Source node with id ${source.node} was not found to create a connection`);
                    }
                    if (!destNode) {
                        throw new Error(`Destination node with id ${source.node} was not found to create a connection`);
                    }
                    try {
                        setIgnoreEvents();
                        currentPipeline.connect(sourceNode.output(source.name), destNode.input(dest.name));
                        return dispatchUpdateNodesAndGraphAction(currentPipeline, next);
                    } catch (e) {
                        let message = e.message;
                        if (message.includes('cycle')) {
                            message = 'Cycles are not allowed';
                        }
                        return next({
                            type: CONNECTION_ERROR,
                            payload: message
                        });
                    } finally {
                        unsetIgnoreEvents();
                    }
                }
                case RUN_NODE:
                    currentPipeline.runNode(action.payload);
                    return next(createUpdateGraphAction(currentPipeline));
                case RESET_NODE: {
                    const node = currentPipeline.getNode(action.payload);
                    node.reset();
                    return next(createUpdateGraphAction(currentPipeline));
                }
                default: {
                    throw new Error(`Unexpected action: ${action.type}`);
                }
            }
        }
        return next(action);
    };

    function dispatchUpdateNodesAndGraphAction(pipeline, next) {
        next({
            type: UPDATE_NODES_AND_GRAPH,
            payload: {
                nodes: createUpdateNodesAction(rootPipeline).payload,
                graph: createUpdateGraphAction(pipeline).payload
            }
        });
    }
};

function createUpdateGraphAction(pipeline) {
    return {
        type: UPDATE_GRAPH,
        payload: JSON.parse(pipeline.graph.toJSON())
    };
}

function createUpdateNodesAction(pipeline) {
    const nodes = {};
    for (const node of pipeline.nodes()) {
        nodes[node.id] = node.inspect();
    }
    return {
        type: UPDATE_NODES,
        payload: nodes
    };
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014-2014 52°North Initiative for Geospatial Open Source
 * Software GmbH
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 *
 * If the program is linked with libraries which are licensed under one of
 * the following licenses, the combination of the program with the linked
 * library is not considered a "derivative work" of the program:
 *
 *     - Apache License, version 2.0
 *     - Apache Software License, version 1.0
 *     - GNU Lesser General Public License, version 3
 *     - Mozilla Public License, versions 1.0, 1.1 and 2.0
 *     - Common Development and Distribution License (CDDL), version 1.0
 *
 * Therefore the distribution of the program linked with libraries licensed
 * under the aforementioned licenses, is permitted by the copyright holders
 * if the distribution is compliant with both the GNU General Public
 * License version 2 and the aforementioned licenses.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 */
var ChartController = {

	selectedLineWidth : 5,
	defaultLineWidth : 2,

	options : {
		series : {
			lines : {
				show : true,
//				lineWidth: 1,
				fill : false
			},
			shadowSize : 1
		},
		selection : {
			mode : "xy"
		},
		xaxis : {
			mode : "time",
			// timeformat: "%Y/%m/%d",
			tickLength : 5
		},
		yaxis : {
			show : true
		},
		legend : {
		// show: false
		}
	},
	visible : true,

	data : [],

	init : function() {
		EventManager.subscribe("timeseries:data:load", $.proxy(this.loadDataForChart, this));
		EventManager.subscribe("timeseries:data:loadfinished", $.proxy(this.loadDataFinished, this));
		EventManager.subscribe("timeseries:synced", $.proxy(this.plotChart, this));
		EventManager.subscribe("timeseries:add:referenceValue", $.proxy(this.showReferenceValue, this));
		EventManager.subscribe("timeseries:remove:referenceValue", $.proxy(this.removeReferenceValue, this));
		EventManager.subscribe("timeseries:remove", $.proxy(this.removeTS, this));
		EventManager.subscribe("timeseries:removeAll", $.proxy(this.clearChart, this));
		EventManager.subscribe("timeseries:selected", $.proxy(this.selectTs, this));
		EventManager.subscribe("timeseries:unselectAll", $.proxy(this.unselectAll, this));
		EventManager.subscribe("timeseries:hide", $.proxy(this.hideData, this));
		EventManager.subscribe("timeseries:show", $.proxy(this.showData, this));
		EventManager.subscribe("navigation:open", $.proxy(this.hideChart, this));
		EventManager.subscribe("navigation:close", $.proxy(this.showChart, this));
		EventManager.subscribe("timeseries:changeColor", $.proxy(this.changeColor, this));

		$(window).resize($.proxy(function() {
			var newRatio = $(document).width() / $(document).height();
			if (newRatio != window.ratio) {
				window.ratio = newRatio;
				this.plotChart();
			}
		}, this));
		$('[data-action=selection]').hide();
		$('[data-action=selection]').on('click', $.proxy(function(){
			EventManager.publish("timeseries:unselectAll");
			this.renderChart();
			$('[data-action=selection]').hide();
		}, this));
	},

	hideChart : function(event, view) {
		this.visible = false;
		$('#placeholder').hide();
	},

	showChart : function(event, view) {
		this.visible = true;
		$('#placeholder').show();
		this.plotChart();
	},

	hideData : function(event, id) {
		this.removeData(id);
		this.plotChart();
	},

	showData : function(event, id) {
		var ts = TimeSeriesController.getTimeseriesCollection()[id];
		this.loadDataFinished(null, ts);
		this.plotChart();
	},

	selectTs : function(event, id) {
		$.each(this.plot.getData(), function(index, elem) {
			debugger;
			if(elem.id == id) {
				elem.lines.lineWidth = ChartController.selectedLineWidth;
			}
		});
		this.plot.draw();
	},

	unselectAll : function(event) {
		$.each(this.plot.getData(), function(index, elem) {
			elem.lines.lineWidth = ChartController.defaultLineWidth;
		});
		this.plot.draw();
	},

	loadDataForChart : function(event, ts) {
		if(this.visible) {
			$('#placeholder').hide();
			var label = ts.getMetadata().label;
			var html = Template.createHtml("data-loading-entry", {
				id : ts.getId(),
				color : ts.getColor(),
				label : label
			});
			$('#loadingDiagram').append(html);
		}
	},

	changeColor : function(event, ts) {
		this.loadDataFinished(null, ts);
		this.plotChart();
	},

	loadDataFinished : function(event, ts) {
		$('#loadingDiagram').find('[data-id=' + ts.getId() + ']').remove();
		$.each(ts.getRefValues(), $.proxy(function(id, elem) {
			var refVal = this.dataAlreadyIn(elem.getId());
			if (refVal != null) {
				refVal.data = elem.getValues();
			}
		}, this));
		if(ts.hasData()) {
			var temp = this.dataAlreadyIn(ts.getId());
			if(temp != null) {
				temp.data = ts.getValues();
				temp.color = ts.getColor();
			} else {
				this.data.push({
					data : ts.getValues(),
					id : ts.getId(),
					color : "#" + ts.getColor(),
					uom : ts.getMetadata().uom
				});
			};
		} else {
			this.removeData(ts.getId());
		}
	},

	removeTS : function(event, ts) {
		$('#loadingDiagram').find('[data-id=' + ts.getId() + ']').remove();
		this.removeData(ts.getId());
		// remove ref values
		$.each(ts.getRefValues(), $.proxy(function(index, elem) {
			this.removeData(elem.getId());
		}, this));
		this.plotChart();
	},

	showReferenceValue : function(event, data) {
		var ts = TimeSeriesController.getTimeseriesCollection()[data.tsId];
		var refVal = ts.getRefValuesForId(data.refId);
		this.data.push({
			data : refVal.getValues(),
			id : refVal.getId(),
			color : refVal.getColor(),
			uom : ts.getMetadata().uom
		});
		this.plotChart();
	},

	removeReferenceValue : function(event, data) {
		this.removeData(data.refId);
		this.plotChart();
	},

	clearChart : function() {
		this.data = [];
		this.plotChart();
	},

	plotChart : function() {
		if(this.visible){
			$('#placeholder').show();
			this.createAxis();
			if (this.data.length > 0) {
				this.plot = $.plot("#placeholder", this.data, this.options);
				$.each(this.plot.getAxes(), $.proxy(function(i, axis){
					if(!axis.show) return;
					var box = axis.box;
					var start;
					if(axis.direction == "y") {
						$("

")
						.data("axis.direction", axis.direction)
						.data("axis.n", axis.n)
						.appendTo(this.plot.getPlaceholder())
						.click($.proxy(function (event) {
							var target = $(event.currentTarget);
							EventManager.publish("timeseries:unselectAll");
							$.each($('.axisTarget'), function(index, elem){
								elem = $(elem);
								if(target.data('axis.n') == elem.data('axis.n')) {
									elem.toggleClass("selected");
								} else {
									elem.removeClass("selected");
								}
							});
							$.each(this.plot.getData(), function(index, elem) {
								if(elem.yaxis.n == axis.n && target.hasClass("selected")) {
//									elem.lines.lineWidth = this.selectedLineWidth;
									EventManager.publish("timeseries:selected", elem.id);
//								} else {
//									elem.lines.lineWidth = this.defaultLineWidth;
								}
							});
							this.plot.draw();
						},this));
					}
				}, this));
			} else {
				$("#placeholder").empty();
			}
		}
	},

	createAxis : function() {
		var axis = {};
		$.each(this.data, function(index, elem){
			if(!axis.hasOwnProperty(elem.uom)) {
				axis[elem.uom] = ++Object.keys(axis).length;
			};
			elem.yaxis = axis[elem.uom];
		});
	},

	dataAlreadyIn : function(id) {
		var elem = null;
		elem = $.map(this.data, function(elem) {
			if(id == elem.id) {
				return elem;
			}
		});
		return elem[0];
	},

	removeData : function(id) {
		var idx = null;
		$.each(this.data, function(i, elem) {
			if(id == elem.id) {
				idx = i;
				return;
			}
		});
		if (idx != null) {
			this.data.splice(idx, 1);
		}
	}
};<commit_msg>fixed chart color error<commit_after>/*
 * Copyright (C) 2014-2014 52°North Initiative for Geospatial Open Source
 * Software GmbH
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 *
 * If the program is linked with libraries which are licensed under one of
 * the following licenses, the combination of the program with the linked
 * library is not considered a "derivative work" of the program:
 *
 *     - Apache License, version 2.0
 *     - Apache Software License, version 1.0
 *     - GNU Lesser General Public License, version 3
 *     - Mozilla Public License, versions 1.0, 1.1 and 2.0
 *     - Common Development and Distribution License (CDDL), version 1.0
 *
 * Therefore the distribution of the program linked with libraries licensed
 * under the aforementioned licenses, is permitted by the copyright holders
 * if the distribution is compliant with both the GNU General Public
 * License version 2 and the aforementioned licenses.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 */
var ChartController = {

	selectedLineWidth : 5,
	defaultLineWidth : 2,

	options : {
		series : {
			lines : {
				show : true,
//				lineWidth: 1,
				fill : false
			},
			shadowSize : 1
		},
		selection : {
			mode : "xy"
		},
		xaxis : {
			mode : "time",
			// timeformat: "%Y/%m/%d",
			tickLength : 5
		},
		yaxis : {
			show : true
		},
		legend : {
		// show: false
		}
	},
	visible : true,

	data : [],

	init : function() {
		EventManager.subscribe("timeseries:data:load", $.proxy(this.loadDataForChart, this));
		EventManager.subscribe("timeseries:data:loadfinished", $.proxy(this.loadDataFinished, this));
		EventManager.subscribe("timeseries:synced", $.proxy(this.plotChart, this));
		EventManager.subscribe("timeseries:add:referenceValue", $.proxy(this.showReferenceValue, this));
		EventManager.subscribe("timeseries:remove:referenceValue", $.proxy(this.removeReferenceValue, this));
		EventManager.subscribe("timeseries:remove", $.proxy(this.removeTS, this));
		EventManager.subscribe("timeseries:removeAll", $.proxy(this.clearChart, this));
		EventManager.subscribe("timeseries:selected", $.proxy(this.selectTs, this));
		EventManager.subscribe("timeseries:unselectAll", $.proxy(this.unselectAll, this));
		EventManager.subscribe("timeseries:hide", $.proxy(this.hideData, this));
		EventManager.subscribe("timeseries:show", $.proxy(this.showData, this));
		EventManager.subscribe("navigation:open", $.proxy(this.hideChart, this));
		EventManager.subscribe("navigation:close", $.proxy(this.showChart, this));
		EventManager.subscribe("timeseries:changeColor", $.proxy(this.changeColor, this));

		$(window).resize($.proxy(function() {
			var newRatio = $(document).width() / $(document).height();
			if (newRatio != window.ratio) {
				window.ratio = newRatio;
				this.plotChart();
			}
		}, this));
		$('[data-action=selection]').hide();
		$('[data-action=selection]').on('click', $.proxy(function(){
			EventManager.publish("timeseries:unselectAll");
			this.renderChart();
			$('[data-action=selection]').hide();
		}, this));
	},

	hideChart : function(event, view) {
		this.visible = false;
		$('#placeholder').hide();
	},

	showChart : function(event, view) {
		this.visible = true;
		$('#placeholder').show();
		this.plotChart();
	},

	hideData : function(event, id) {
		this.removeData(id);
		this.plotChart();
	},

	showData : function(event, id) {
		var ts = TimeSeriesController.getTimeseriesCollection()[id];
		this.loadDataFinished(null, ts);
		this.plotChart();
	},

	selectTs : function(event, id) {
		$.each(this.plot.getData(), function(index, elem) {
			debugger;
			if(elem.id == id) {
				elem.lines.lineWidth = ChartController.selectedLineWidth;
			}
		});
		this.plot.draw();
	},

	unselectAll : function(event) {
		$.each(this.plot.getData(), function(index, elem) {
			elem.lines.lineWidth = ChartController.defaultLineWidth;
		});
		this.plot.draw();
	},

	loadDataForChart : function(event, ts) {
		if(this.visible) {
			$('#placeholder').hide();
			var label = ts.getMetadata().label;
			var html = Template.createHtml("data-loading-entry", {
				id : ts.getId(),
				color : ts.getColor(),
				label : label
			});
			$('#loadingDiagram').append(html);
		}
	},

	changeColor : function(event, ts) {
		this.loadDataFinished(null, ts);
		this.plotChart();
	},

	loadDataFinished : function(event, ts) {
		$('#loadingDiagram').find('[data-id=' + ts.getId() + ']').remove();
		$.each(ts.getRefValues(), $.proxy(function(id, elem) {
			var refVal = this.dataAlreadyIn(elem.getId());
			if (refVal != null) {
				refVal.data = elem.getValues();
			}
		}, this));
		if(ts.hasData()) {
			var temp = this.dataAlreadyIn(ts.getId());
			if(temp != null) {
				temp.data = ts.getValues();
				temp.color = "#" + ts.getColor();
			} else {
				this.data.push({
					data : ts.getValues(),
					id : ts.getId(),
					color : "#" + ts.getColor(),
					uom : ts.getMetadata().uom
				});
			};
		} else {
			this.removeData(ts.getId());
		}
	},

	removeTS : function(event, ts) {
		$('#loadingDiagram').find('[data-id=' + ts.getId() + ']').remove();
		this.removeData(ts.getId());
		// remove ref values
		$.each(ts.getRefValues(), $.proxy(function(index, elem) {
			this.removeData(elem.getId());
		}, this));
		this.plotChart();
	},

	showReferenceValue : function(event, data) {
		var ts = TimeSeriesController.getTimeseriesCollection()[data.tsId];
		var refVal = ts.getRefValuesForId(data.refId);
		this.data.push({
			data : refVal.getValues(),
			id : refVal.getId(),
			color : "#" + refVal.getColor(),
			uom : ts.getMetadata().uom
		});
		this.plotChart();
	},

	removeReferenceValue : function(event, data) {
		this.removeData(data.refId);
		this.plotChart();
	},

	clearChart : function() {
		this.data = [];
		this.plotChart();
	},

	plotChart : function() {
		if(this.visible){
			$('#placeholder').show();
			this.createAxis();
			if (this.data.length > 0) {
				this.plot = $.plot("#placeholder", this.data, this.options);
				$.each(this.plot.getAxes(), $.proxy(function(i, axis){
					if(!axis.show) return;
					var box = axis.box;
					var start;
					if(axis.direction == "y") {
						$("

")
						.data("axis.direction", axis.direction)
						.data("axis.n", axis.n)
						.appendTo(this.plot.getPlaceholder())
						.click($.proxy(function (event) {
							var target = $(event.currentTarget);
							EventManager.publish("timeseries:unselectAll");
							$.each($('.axisTarget'), function(index, elem){
								elem = $(elem);
								if(target.data('axis.n') == elem.data('axis.n')) {
									elem.toggleClass("selected");
								} else {
									elem.removeClass("selected");
								}
							});
							$.each(this.plot.getData(), function(index, elem) {
								if(elem.yaxis.n == axis.n && target.hasClass("selected")) {
//									elem.lines.lineWidth = this.selectedLineWidth;
									EventManager.publish("timeseries:selected", elem.id);
//								} else {
//									elem.lines.lineWidth = this.defaultLineWidth;
								}
							});
							this.plot.draw();
						},this));
					}
				}, this));
			} else {
				$("#placeholder").empty();
			}
		}
	},

	createAxis : function() {
		var axis = {};
		$.each(this.data, function(index, elem){
			if(!axis.hasOwnProperty(elem.uom)) {
				axis[elem.uom] = ++Object.keys(axis).length;
			};
			elem.yaxis = axis[elem.uom];
		});
	},

	dataAlreadyIn : function(id) {
		var elem = null;
		elem = $.map(this.data, function(elem) {
			if(id == elem.id) {
				return elem;
			}
		});
		return elem[0];
	},

	removeData : function(id) {
		var idx = null;
		$.each(this.data, function(i, elem) {
			if(id == elem.id) {
				idx = i;
				return;
			}
		});
		if (idx != null) {
			this.data.splice(idx, 1);
		}
	}
};<|endoftext|>


<commit_before>'use strict';

// Read the config from gulpconfig.js
const config = require('./gulpconfig');

const del = require('del');                         // Clean up files that are no longer needed
const extend = require('util')._extend;             // Used to extend the current process.env object for hot reloading
const fs = require('fs');                           // Used to create a .gitignore file for the production build
const gulp = require('gulp');
const gulpFilter = require('gulp-filter');          // Filter streams to perform operations on a valid subset of files
const gulpMocha = require('gulp-mocha');            // Runs mocha unit tests
const gutil = require('gulp-util');                 // Allows to return unchanged streams when a plugin is disabled
const htmlreplace = require('gulp-html-replace');   // Replaces the jspm script imports with the generated bundled
const install = require('gulp-install');            // Installs npm packages from a package.json file
const jeditor = require('gulp-json-editor');        // Modifies the built package.json
const jspm = require('gulp-jspm');                  // Used to bundle the client source files
const minifyHtml = require('gulp-minify-html');
const mkpath = require('mkpath');                   // Creates folders within a path that don't exist yet
const path = require('path');                       // Ensure path operations are valid in Windows & Unix
const sourcemaps = require('gulp-sourcemaps');      // Writes inline sourcemaps to ease debugging
const spawn = require('child_process').spawn;       // Spawns a node server to enable incremental compilation
const stripDebug = require('gulp-strip-debug');     // Removes debug statements from script files

require('babel-core/register')                      // Use babel to translate client test files

// The watch task spawns a node server which will be referenced in this variable,
// to be able to kill it when the server needs to be restarted.
let node;

// Applies a plugin to the stream if the flag demands for it
function applyPlugin(plugin, flag) {
  return flag ? plugin : gutil.noop();
}

// The default task, which can be called with "gulp"
gulp.task('default', ['test']);

// The general build task which parallely calls its dependants
gulp.task('build', ['copy:server', 'copy:client', 'bundle:client', 'npm:install', 'create:gitignore']);

// Copies all server source files to the build output directory
gulp.task('copy:server', () => {
  // Don't touch jsx view files
  const jsxFilter = gulpFilter([
    '**/**',
    '!**/**.jsx'
  ], { restore: true });

  // Get all server files
  return gulp.src(`${config.serverSourceDir}/**/**`, { base: './' })
  // Remove jsx view files from the stream
    .pipe(jsxFilter)
  // Initialize source maps
    .pipe(applyPlugin(sourcemaps.init(), config.plugins.sourcemaps))
  // Strip debug messages like console.log from the files
    .pipe(applyPlugin(stripDebug(), config.plugins.stripDebug))
  // Write the source maps
    .pipe(applyPlugin(sourcemaps.write(), config.plugins.sourcemaps))
  // Restore jsx view files into the stream
    .pipe(jsxFilter.restore)
  // Put the files in the build output directory
    .pipe(gulp.dest(config.buildOutDir));
});

// Copies all static client files to the build output directory
gulp.task('copy:client', () => {
  const htmlFilter = gulpFilter('**/*.html', { restore: true });

  // Get all static client files
  return gulp.src(`${config.clientStaticDir}/**/**`, { base: './' })

  // Operations on HTML files
    .pipe(htmlFilter)
  // Replace System.js with the bundle reference
    .pipe(htmlreplace({ production: 'index.bundle.js' }))
  // Minify the html
    .pipe(applyPlugin(minifyHtml(), config.plugins.minifyHtml))
  // Restore all files back into the stream
    .pipe(htmlFilter.restore)

  // Put the files in the build output dir
    .pipe(gulp.dest(config.buildOutDir));
});

// Bundles all jspm entry points into single files
gulp.task('bundle:client', () => {
  // Get all entryPoints and prepend the script directory to its path
  const entryPoints = config.entryPoints.map(point => `${config.clientScriptDir}/${point}`);

  return gulp.src(entryPoints, { base: './' })
  // Initialize source maps
    .pipe(applyPlugin(sourcemaps.init(), config.plugins.sourcemaps))
  // Execute the jspm command line tool to generate a bundle
    .pipe(jspm({
      selfExecutingBundle: true,
      minify: config.plugins.uglify,
    }))
  // Strip debug messages like console.log from the files
    .pipe(applyPlugin(stripDebug(), config.plugins.stripDebug))
  // Write the source maps
    .pipe(applyPlugin(sourcemaps.write(), config.plugins.sourcemaps))
  // Put the files in the build output dir
    .pipe(gulp.dest(config.buildOutDir));
});

gulp.task('npm:install', () => {
  // The base for the built package.json will be the current package.json
  return gulp.src('package.json')
  // Clean up the package.json and remove development
  // related sections
    .pipe(jeditor(json => {
      // Tests will not be compiled/copied into the build directory
      delete json.scripts.test;
      // the post install script installs jspm modules, which are no longer needed
      delete json.scripts.postinstall;
      // DevDependencies will no longer be needed in the built project
      delete json.devDependencies;
      // jspm dependencies will also no longer be needed since everything is bundled
      delete json.jspm;
      return json;
    }))
    .pipe(gulp.dest(config.buildOutDir))
  // Run 'npm install' with the production parameter
  // to stop it from installing devDependencies. While the devDependencies are
  // stripped from the package.json before, npm might introduce other production
  // build behavior in the future that we want to preserve
    .pipe(install({
      production: true,
    }));
});

gulp.task('create:gitignore', done => {
  const excluded = [
    'node_modules'
  ];

  mkpath(config.buildOutDir, error => {
    if (error) {
      throw error;
    }

    fs.writeFile(path.join(config.buildOutDir, '.gitignore'), excluded.join('\n'), done);
  });
});

// Runs all unit test within the test directory with mocha
gulp.task('test', ['test:client']);

// Copies all server side tests
gulp.task('copy:server-tests', ['build'], () => {
  return gulp.src([
      `${config.testDirServer}/**/**`,
    ], { base: './' })
    .pipe(gulp.dest(config.buildOutDir));
});

function deleteTestsInBuildOutDir() {
  del(path.join(config.buildOutDir, 'test'));
}

// Runs all server tests
gulp.task('test:server', ['copy:server-tests'], () => {
  return gulp.src(`${path.join(config.buildOutDir, config.testDirServer)}/**/*.js`, { read: false })
    .pipe(gulpMocha({
      require: ['env-test']
    }))
    .on('end', deleteTestsInBuildOutDir)
    .on('error', deleteTestsInBuildOutDir);;
});

// Runs all client side tests
gulp.task('test:client', ['test:server'], () => {
  return gulp.src(`${config.testDirClient}/**/*.js`, { read: false })
    .pipe(gulpMocha({
      compilers: ['js:babel-core/register'],
      require: ['env-test']
    }));
});

// Creates a node server which will be used by 'gulp watch'
gulp.task('server', () => {
  // Check whether a node server has already been started by this task
  if (node) {
    // Kill the process if node has been running before
    node.kill();
  }

  // Pass on another environment variable to emit hot reloading events
  const watchEnvironment = extend(process.env, { 'gulp:watch': true });

  // Start the node server with the node entry point
  node = spawn('node', [path.join(config.serverSourceDir, 'server.js')], {
    stdio: 'inherit',
    env: watchEnvironment,
  });

  // Listen to the close event to detect errors while running the server
  node.on('close', code => {
    // Check for the error code to detect errors correctly
    if (code === 8) {
      console.log('Error detected, waiting for changes...');
    }
  });
});

// Task that watches the source files for modifications and rebuilds
// the project upon file changes
gulp.task('watch', ['server'], () => {
  // Watch for server side files and restart the server after a succesful build
  gulp.watch(`${config.serverSourceDir}/**/**`, ['server']);
});

// This task cleans all files that are generated by the build process
gulp.task('clean', () => {
  return del(config.buildOutDir);
});
<commit_msg>Full/JS: Only use babel in the test:client gulp task<commit_after>'use strict';

// Read the config from gulpconfig.js
const config = require('./gulpconfig');

const del = require('del');                         // Clean up files that are no longer needed
const extend = require('util')._extend;             // Used to extend the current process.env object for hot reloading
const fs = require('fs');                           // Used to create a .gitignore file for the production build
const gulp = require('gulp');
const gulpFilter = require('gulp-filter');          // Filter streams to perform operations on a valid subset of files
const gulpMocha = require('gulp-mocha');            // Runs mocha unit tests
const gutil = require('gulp-util');                 // Allows to return unchanged streams when a plugin is disabled
const htmlreplace = require('gulp-html-replace');   // Replaces the jspm script imports with the generated bundled
const install = require('gulp-install');            // Installs npm packages from a package.json file
const jeditor = require('gulp-json-editor');        // Modifies the built package.json
const jspm = require('gulp-jspm');                  // Used to bundle the client source files
const minifyHtml = require('gulp-minify-html');
const mkpath = require('mkpath');                   // Creates folders within a path that don't exist yet
const path = require('path');                       // Ensure path operations are valid in Windows & Unix
const sourcemaps = require('gulp-sourcemaps');      // Writes inline sourcemaps to ease debugging
const spawn = require('child_process').spawn;       // Spawns a node server to enable incremental compilation
const stripDebug = require('gulp-strip-debug');     // Removes debug statements from script files

// The watch task spawns a node server which will be referenced in this variable,
// to be able to kill it when the server needs to be restarted.
let node;

// Applies a plugin to the stream if the flag demands for it
function applyPlugin(plugin, flag) {
  return flag ? plugin : gutil.noop();
}

// The default task, which can be called with "gulp"
gulp.task('default', ['test']);

// The general build task which parallely calls its dependants
gulp.task('build', ['copy:server', 'copy:client', 'bundle:client', 'npm:install', 'create:gitignore']);

// Copies all server source files to the build output directory
gulp.task('copy:server', () => {
  // Don't touch jsx view files
  const jsxFilter = gulpFilter([
    '**/**',
    '!**/**.jsx'
  ], { restore: true });

  // Get all server files
  return gulp.src(`${config.serverSourceDir}/**/**`, { base: './' })
  // Remove jsx view files from the stream
    .pipe(jsxFilter)
  // Initialize source maps
    .pipe(applyPlugin(sourcemaps.init(), config.plugins.sourcemaps))
  // Strip debug messages like console.log from the files
    .pipe(applyPlugin(stripDebug(), config.plugins.stripDebug))
  // Write the source maps
    .pipe(applyPlugin(sourcemaps.write(), config.plugins.sourcemaps))
  // Restore jsx view files into the stream
    .pipe(jsxFilter.restore)
  // Put the files in the build output directory
    .pipe(gulp.dest(config.buildOutDir));
});

// Copies all static client files to the build output directory
gulp.task('copy:client', () => {
  const htmlFilter = gulpFilter('**/*.html', { restore: true });

  // Get all static client files
  return gulp.src(`${config.clientStaticDir}/**/**`, { base: './' })

  // Operations on HTML files
    .pipe(htmlFilter)
  // Replace System.js with the bundle reference
    .pipe(htmlreplace({ production: 'index.bundle.js' }))
  // Minify the html
    .pipe(applyPlugin(minifyHtml(), config.plugins.minifyHtml))
  // Restore all files back into the stream
    .pipe(htmlFilter.restore)

  // Put the files in the build output dir
    .pipe(gulp.dest(config.buildOutDir));
});

// Bundles all jspm entry points into single files
gulp.task('bundle:client', () => {
  // Get all entryPoints and prepend the script directory to its path
  const entryPoints = config.entryPoints.map(point => `${config.clientScriptDir}/${point}`);

  return gulp.src(entryPoints, { base: './' })
  // Initialize source maps
    .pipe(applyPlugin(sourcemaps.init(), config.plugins.sourcemaps))
  // Execute the jspm command line tool to generate a bundle
    .pipe(jspm({
      selfExecutingBundle: true,
      minify: config.plugins.uglify,
    }))
  // Strip debug messages like console.log from the files
    .pipe(applyPlugin(stripDebug(), config.plugins.stripDebug))
  // Write the source maps
    .pipe(applyPlugin(sourcemaps.write(), config.plugins.sourcemaps))
  // Put the files in the build output dir
    .pipe(gulp.dest(config.buildOutDir));
});

gulp.task('npm:install', () => {
  // The base for the built package.json will be the current package.json
  return gulp.src('package.json')
  // Clean up the package.json and remove development
  // related sections
    .pipe(jeditor(json => {
      // Tests will not be compiled/copied into the build directory
      delete json.scripts.test;
      // the post install script installs jspm modules, which are no longer needed
      delete json.scripts.postinstall;
      // DevDependencies will no longer be needed in the built project
      delete json.devDependencies;
      // jspm dependencies will also no longer be needed since everything is bundled
      delete json.jspm;
      return json;
    }))
    .pipe(gulp.dest(config.buildOutDir))
  // Run 'npm install' with the production parameter
  // to stop it from installing devDependencies. While the devDependencies are
  // stripped from the package.json before, npm might introduce other production
  // build behavior in the future that we want to preserve
    .pipe(install({
      production: true,
    }));
});

gulp.task('create:gitignore', done => {
  const excluded = [
    'node_modules'
  ];

  mkpath(config.buildOutDir, error => {
    if (error) {
      throw error;
    }

    fs.writeFile(path.join(config.buildOutDir, '.gitignore'), excluded.join('\n'), done);
  });
});

// Runs all unit test within the test directory with mocha
gulp.task('test', ['test:client']);

// Copies all server side tests
gulp.task('copy:server-tests', ['build'], () => {
  return gulp.src([
      `${config.testDirServer}/**/**`,
    ], { base: './' })
    .pipe(gulp.dest(config.buildOutDir));
});

function deleteTestsInBuildOutDir() {
  del(path.join(config.buildOutDir, 'test'));
}

// Runs all server tests
gulp.task('test:server', ['copy:server-tests'], () => {
  return gulp.src(`${path.join(config.buildOutDir, config.testDirServer)}/**/*.js`, { read: false })
    .pipe(gulpMocha({
      require: ['env-test']
    }))
    .on('end', deleteTestsInBuildOutDir)
    .on('error', deleteTestsInBuildOutDir);;
});

// Runs all client side tests
gulp.task('test:client', ['test:server'], () => {
  require('babel-core/register') // Use babel to translate client test files
  return gulp.src(`${config.testDirClient}/**/*.js`, { read: false })
    .pipe(gulpMocha({
      compilers: ['js:babel-core/register'],
      require: ['env-test']
    }));
});

// Creates a node server which will be used by 'gulp watch'
gulp.task('server', () => {
  // Check whether a node server has already been started by this task
  if (node) {
    // Kill the process if node has been running before
    node.kill();
  }

  // Pass on another environment variable to emit hot reloading events
  const watchEnvironment = extend(process.env, { 'gulp:watch': true });

  // Start the node server with the node entry point
  node = spawn('node', [path.join(config.serverSourceDir, 'server.js')], {
    stdio: 'inherit',
    env: watchEnvironment,
  });

  // Listen to the close event to detect errors while running the server
  node.on('close', code => {
    // Check for the error code to detect errors correctly
    if (code === 8) {
      console.log('Error detected, waiting for changes...');
    }
  });
});

// Task that watches the source files for modifications and rebuilds
// the project upon file changes
gulp.task('watch', ['server'], () => {
  // Watch for server side files and restart the server after a succesful build
  gulp.watch(`${config.serverSourceDir}/**/**`, ['server']);
});

// This task cleans all files that are generated by the build process
gulp.task('clean', () => {
  return del(config.buildOutDir);
});
<|endoftext|>


<commit_before>var MediaControl = require('../spec_helper').MediaControl;
var Container = require('../spec_helper').Container;
var _ = require('underscore');

describe('MediaControl', function() {
  beforeEach(function() {
    this.container = new Container({settings: ['play', 'pause']});
    this.control = new MediaControl({container: this.container});
  });

  it('should have a custom template', function() {
    this.control.template = _.template("
foo
");
    this.control.template().should.be.equal("
foo
");
  });

  it('should be able to have a default template', function() {
    this.control.render().$el.html().should.not.be.equal('');
  });

  it('should render the template into the element', function() {
    this.control.container.settings = {left: ['play']};
    this.control.render();
    this.control.$el.find('button').attr('data-play').should.not.be.equal(undefined);
  });

  it('should be able to update seekbar', function() {
      this.control.template = _.template('<input data-seekbar value="0"></input>');
      this.control.render();
      this.control.updateSeekBar(10);
      this.control.$('[data-seekbar]').val().should.be.equal('10');
  });

  it('should switch between containers', function() {
    var stopListening = sinon.spy(this.control, 'stopListening');
    var listenTo = sinon.spy(this.control, 'listenTo');
    var newContainer = new Container();
    this.control.setContainer(newContainer);

    stopListening.withArgs(this.container).calledOnce.should.be.true;
    listenTo.calledOnce.should.be.true;
  });

  describe('events', function() {
    beforeEach(function() {
      this.container = new Container();
      this.control = new MediaControl({container: this.container});
    });

    it('should react for clicks on play', function() {
      var spy = sinon.spy(this.container, 'play');
      this.control.render();
      this.control.$('[data-play]').click();
      spy.called.should.be.true;
    });

    it('should react for clicks on pause', function() {
      var spy = sinon.spy(this.container, 'pause');
      this.control.render();
      this.control.$('[data-pause]').click();
      spy.called.should.be.true;
    });

    it('should react for clicks on stop', function() {
      var spy = sinon.spy(this.container, 'stop');
      this.control.render();
      this.control.$('[data-stop]').click();
      spy.called.should.be.true;
    });

    it('should react for clicks on the seekbar', function() {
      var spy = sinon.spy(this.container, 'setCurrentTime');
      this.control.render();
      this.control.$('[data-seekbar]').click();
      spy.called.should.be.true;
    });

    it("should change the container's volume", function() {
      var spy = sinon.spy(this.container, 'setVolume');
      this.container.settings = {left: ['volume']};
      this.control.render();
      this.control.$('input[data-volume]').click();
      spy.called.should.be.true;
    });

    it('should react for clicks on the fullscreen button', function() {
      this.control.template = _.template('

');
      var spy = sinon.spy(this.control, 'trigger');
      this.control.render();
      this.control.$('[data-fullscreen]').click();
      spy.withArgs('mediacontrol:fullscreen').calledOnce.should.be.true;
    });
  });
});
<commit_msg>media control: fix container switching test<commit_after>var MediaControl = require('../spec_helper').MediaControl;
var Container = require('../spec_helper').Container;
var _ = require('underscore');

describe('MediaControl', function() {
  beforeEach(function() {
    this.container = new Container({settings: ['play', 'pause']});
    this.control = new MediaControl({container: this.container});
  });

  it('should have a custom template', function() {
    this.control.template = _.template("
foo
");
    this.control.template().should.be.equal("
foo
");
  });

  it('should be able to have a default template', function() {
    this.control.render().$el.html().should.not.be.equal('');
  });

  it('should render the template into the element', function() {
    this.control.container.settings = {left: ['play']};
    this.control.render();
    this.control.$el.find('button').attr('data-play').should.not.be.equal(undefined);
  });

  it('should be able to update seekbar', function() {
      this.control.template = _.template('<input data-seekbar value="0"></input>');
      this.control.render();
      this.control.updateSeekBar(10);
      this.control.$('[data-seekbar]').val().should.be.equal('10');
  });

  it('should switch between containers', function() {
    var stopListening = sinon.spy(this.control, 'stopListening');
    var listenTo = sinon.spy(this.control, 'listenTo');
    var newContainer = new Container();
    this.control.setContainer(newContainer);

    stopListening.withArgs(this.container).calledOnce.should.be.true;
    listenTo.withArgs(newContainer, 'container:timeupdate').calledOnce.should.be.true;
  });

  describe('events', function() {
    beforeEach(function() {
      this.container = new Container();
      this.control = new MediaControl({container: this.container});
    });

    it('should react for clicks on play', function() {
      var spy = sinon.spy(this.container, 'play');
      this.control.render();
      this.control.$('[data-play]').click();
      spy.called.should.be.true;
    });

    it('should react for clicks on pause', function() {
      var spy = sinon.spy(this.container, 'pause');
      this.control.render();
      this.control.$('[data-pause]').click();
      spy.called.should.be.true;
    });

    it('should react for clicks on stop', function() {
      var spy = sinon.spy(this.container, 'stop');
      this.control.render();
      this.control.$('[data-stop]').click();
      spy.called.should.be.true;
    });

    it('should react for clicks on the seekbar', function() {
      var spy = sinon.spy(this.container, 'setCurrentTime');
      this.control.render();
      this.control.$('[data-seekbar]').click();
      spy.called.should.be.true;
    });

    it("should change the container's volume", function() {
      var spy = sinon.spy(this.container, 'setVolume');
      this.container.settings = {left: ['volume']};
      this.control.render();
      this.control.$('input[data-volume]').click();
      spy.called.should.be.true;
    });

    it('should react for clicks on the fullscreen button', function() {
      this.control.template = _.template('

');
      var spy = sinon.spy(this.control, 'trigger');
      this.control.render();
      this.control.$('[data-fullscreen]').click();
      spy.withArgs('mediacontrol:fullscreen').calledOnce.should.be.true;
    });
  });
});
<|endoftext|>


<commit_before>define(['sugar-web/activity/activity', 'webL10n', 'activity/progress', 'activity/stopwatch'], function (activity, l10n, Progress, Stopwatch) {

  // Manipulate the DOM only when it is ready.
  require(['domReady!'], function (doc) {

    // Initialize the activity.
    activity.setup();
    var datastore = activity.getDatastoreObject();
    activity.getXOColor(function (err, color) {
      window.addEventListener('localized', function () {
        main(Progress, Stopwatch, l10n, color, datastore);
      });
    });
  });
});

function convertReadableMS(timeInMs) {
  var parsedTime = parseMs(timeInMs);
  var timeStr = parsedTime.hours ? parsedTime.hours + parsedTime.days * 24 + ':' + parsedTime.minutes + ':' + parsedTime.seconds : parsedTime.minutes + ':' + parsedTime.seconds;
  return timeStr.split(':').map(function (num) {
    return ('' + num).padStart(2, '0');
  }).join(':');
}

var defaultWorkTimerLimit = 0.1;
var defaultBreakTimerLimit = 0.1;
function main(Progress, Stopwatch, l10n, color, datastore) {
  var _this = this;
  console.log(this)
  this.state = {
    status: 'work',
    workTimerLimit: defaultWorkTimerLimit,
    breakTimerLimit: defaultBreakTimerLimit,
    progress: 1,
    currentWorkText: convertReadableMS(defaultWorkTimerLimit * 1000 * 60),
    currentBreakText: convertReadableMS(defaultBreakTimerLimit * 1000 * 60),
    themeColor: '#FF0060',
    isButtonsDisable: false
  };
  datastore.loadAsText(function (err, metadata, data) {
    if (data) {
      _this.state = data;
    }
  });
  startWork();
  renderPomodoroText();
  renderStatusText(l10n.get(this.state.status));
  var pomodoroContainer = document.getElementById('pomodoro-container');
  this.pomodoro = new Progress(280, color.stroke, this.state.progress, pomodoroContainer);
  this.pomodoro.draw();
  renderTheme(color.stroke);
  saveInDataStore();

  function mapRange(obj, num) {
    return (num - obj.from[0]) * (obj.to[1] - obj.to[0]) / (obj.from[1] - obj.from[0]) + obj.to[0];
  }

  function parseMs(ms) {
    if (typeof ms !== 'number') {
      throw new TypeError('Expected a number');
    }

    var roundTowardZero = ms > 0 ? Math.floor : Math.ceil;

    return {
      days: roundTowardZero(ms / 86400000),
      hours: roundTowardZero(ms / 3600000) % 24,
      minutes: roundTowardZero(ms / 60000) % 60,
      seconds: roundTowardZero(ms / 1000) % 60,
      milliseconds: roundTowardZero(ms) % 1000
    };
  }

  function saveInDataStore() {
    datastore.setDataAsText({
      state: this.state
    });
    datastore.save(function (err) {
      if (err) {
        console.errror(err);
      }
    });
    setTimeout(saveInDataStore, 1000);
  }

  function convertReadableMS(timeInMs) {
    var parsedTime = parseMs(timeInMs);
    var timeStr = parsedTime.hours ? parsedTime.hours + parsedTime.days * 24 + ':' + parsedTime.minutes + ':' + parsedTime.seconds : parsedTime.minutes + ':' + parsedTime.seconds;
    return timeStr.split(':').map(function (num) {
      return ('' + num).padStart(2, '0');
    }).join(':');
  }

  this.handlePausePlay = function () {
    var playPauseButton = document.getElementById('play-button');
    if (this.state.status === 'work') {
      if (this.workTimer.state === 1) {
        //if timer is running
        this.workTimer.stop();
        enableButtons();
        playPauseButton.classList.remove('pause');
        playPauseButton.classList.add('play');
      } else {
        this.workTimer.start();
        disableButtons();
        playPauseButton.classList.remove('play');
        playPauseButton.classList.add('pause');
      }
    } else {
      if (this.breakTimer.state === 1) {
        this.breakTimer.stop();
        enableButtons();
        playPauseButton.classList.remove('pause');
        playPauseButton.classList.add('play');
      } else {
        disableButtons();
        this.breakTimer.start();
        playPauseButton.classList.remove('play');
        playPauseButton.classList.add('pause');
      }
    }
  };

  function startWork() {
    var _this2 = this;

    var timerInMS = this.state.workTimerLimit * 60 * 1000;
    this.workTimer = new Stopwatch(timerInMS);
    this.state.status = 'work';
    this.workTimer.onTime(function (time) {
      var progress = mapRange({
        from: [timerInMS, 0],
        to: [1, 0]
      }, time.ms);
      setProgress(progress, convertReadableMS(time.ms));
    });
    this.workTimer.onDone(function () {
      renderTheme(color.fill);
      setTimeout(function () {
        renderStatusText(l10n.get('break'));
        startBreak();
        _this2.breakTimer.start();
      }, 1000);
    });
  }

  function startBreak() {
    var _this3 = this;

    var timerInMS = this.state.breakTimerLimit * 60 * 1000;
    this.breakTimer = new Stopwatch(timerInMS);
    this.state.status = 'break';
    this.breakTimer.onTime(function (time) {
      var progress = mapRange({
        from: [timerInMS, 0],
        to: [1, 0]
      }, time.ms);
      setProgress(progress, convertReadableMS(time.ms));
    });
    this.breakTimer.onDone(function () {
      renderTheme(color.stroke);
      renderStatusText(l10n.get('work'));
      setTimeout(function () {
        startWork();
        _this3.workTimer.start();
      }, 1000);
    });
  }

  function renderStatusText(text) {
    document.querySelector('.status').innerText = text;
  }

  function setProgress(progress, currentTimerText) {
    this.state.progress = progress;
    if (this.state.status === 'work') {
      this.state.currentWorkText = currentTimerText;
    } else {
      this.state.currentBreakText = currentTimerText;
    }
    renderPomodoro();
    renderPomodoroText();
  }

  function renderPomodoro() {
    this.pomodoro.update(this.state.progress);
  }

  function renderPomodoroText() {
    var timerTextElem = document.querySelector('.info-circle span');
    if (this.state.status === 'work') {
      timerTextElem.innerText = this.state.currentWorkText;
    } else {
      timerTextElem.innerText = this.state.currentBreakText;
    }
  }

  function renderTheme(themeColor) {
    this.state.themeColor = themeColor;
    document.querySelector('.info-circle').style.backgroundColor = themeColor;
    document.querySelector('.base-circle').style.backgroundColor = themeColor + '2b';
    this.pomodoro.updateColor(themeColor, this.state.progress);
    renderButtons();
  }

  function renderButtons() {
    var _this4 = this;

    document.querySelectorAll('.button').forEach(function (elem) {
      elem.style.background = _this4.state.themeColor;
    });

    document.querySelectorAll('.button-label, .button-time').forEach(function (elem) {
      elem.style.color = _this4.state.themeColor;
    });
  }

  function disableButtons() {
    this.state.isButtonsDisable = true;
    document.querySelectorAll('.button, .button-time, .button-label').forEach(function (elem) {
      elem.classList.add('disable');
    });
  }
  function enableButtons() {
    this.state.isButtonsDisable = false;
    document.querySelectorAll('.button, .button-time, .button-label').forEach(function (elem) {
      elem.classList.remove('disable');
    });
  }

  function updateWorkPomodoro() {
    this.state.currentWorkText = convertReadableMS(this.state.workTimerLimit * 60 * 1000);
    document.querySelector('.button-time.work').innerText = this.state.workTimerLimit;
    this.pomodoro.update(1);
    renderPomodoroText();
    if (this.state.status === 'work') {
      startWork();
    }
  }

  function updateBreakPomodoro() {
    this.state.currentBreakText = convertReadableMS(this.state.breakTimerLimit * 60 * 1000);
    document.querySelector('.button-time.break').innerText = this.state.breakTimerLimit;
    renderPomodoroText();
    if (this.state.status === 'break') {
      startBreak();
    }
  }

  this.handleWorkPlusClick = function () {
    if (!this.state.isButtonsDisable) {
      this.state.workTimerLimit = this.state.workTimerLimit + 1;
      updateWorkPomodoro();
    }
  };

  this.handleWorkMinusClick = function () {
    if (!this.state.isButtonsDisable && this.state.workTimerLimit > 1) {
      this.state.workTimerLimit = this.state.workTimerLimit - 1;
      updateWorkPomodoro();
    }
  };

  this.handleBreakPlusClick = function () {
    if (!this.state.isButtonsDisable) {
      this.state.breakTimerLimit = this.state.breakTimerLimit + 1;
      updateBreakPomodoro();
    }
  };

  this.handleBreakMinusClick = function () {
    if (!this.state.isButtonsDisable) {
      this.state.breakTimerLimit = this.state.breakTimerLimit - 1;
      updateBreakPomodoro();
    }
  };

  function initTimerText() {
    document.querySelector('.button-time.work').innerText = this.state.workTimerLimit;
    document.querySelector('.button-time.break').innerText = this.state.breakTimerLimit;
  }

  initTimerText();
  document.querySelector('.plus-button.work').addEventListener('click', handleWorkPlusClick.bind(this));
  document.querySelector('.minus-button.work').addEventListener('click', handleWorkMinusClick.bind(this));
  document.querySelector('.plus-button.break').addEventListener('click', handleBreakPlusClick.bind(this));
  document.querySelector('.minus-button.break').addEventListener('click', handleBreakMinusClick.bind(this));
  document.querySelector('#play-button').addEventListener('click', function () {
    _this.handlePausePlay();
  });
  document.querySelector('#replay-button').addEventListener('click', function () {
    var shouldPlay = _this.breakTimer ? _this.workTimer.state === 1 || _this.breakTimer.state === 1 : _this.workTimer.state === 1;
    if (_this.state.status === 'break') {
      _this.breakTimer.stop();
      renderTheme(color.stroke);
      renderStatusText(l10n.get('work'));
      startWork();
      if (shouldPlay) {
        _this.workTimer.start();
      }
    }
    if (shouldPlay) {
      _this.handlePausePlay();
      updateWorkPomodoro();
      _this.handlePausePlay();
    } else {
      updateBreakPomodoro();
    }
  });
}<commit_msg>changed default values<commit_after>define(['sugar-web/activity/activity', 'webL10n', 'activity/progress', 'activity/stopwatch'], function (activity, l10n, Progress, Stopwatch) {

  // Manipulate the DOM only when it is ready.
  require(['domReady!'], function (doc) {

    // Initialize the activity.
    activity.setup();
    var datastore = activity.getDatastoreObject();
    activity.getXOColor(function (err, color) {
      window.addEventListener('localized', function () {
        main(Progress, Stopwatch, l10n, color, datastore);
      });
    });
  });
});

function convertReadableMS(timeInMs) {
  var parsedTime = parseMs(timeInMs);
  var timeStr = parsedTime.hours ? parsedTime.hours + parsedTime.days * 24 + ':' + parsedTime.minutes + ':' + parsedTime.seconds : parsedTime.minutes + ':' + parsedTime.seconds;
  return timeStr.split(':').map(function (num) {
    return ('' + num).padStart(2, '0');
  }).join(':');
}

var defaultWorkTimerLimit = 1;
var defaultBreakTimerLimit = 1;
function main(Progress, Stopwatch, l10n, color, datastore) {
  var _this = this;
  console.log(this)
  this.state = {
    status: 'work',
    workTimerLimit: defaultWorkTimerLimit,
    breakTimerLimit: defaultBreakTimerLimit,
    progress: 1,
    currentWorkText: convertReadableMS(defaultWorkTimerLimit * 1000 * 60),
    currentBreakText: convertReadableMS(defaultBreakTimerLimit * 1000 * 60),
    themeColor: '#FF0060',
    isButtonsDisable: false
  };
  datastore.loadAsText(function (err, metadata, data) {
    if (data) {
      _this.state = data;
    }
  });
  startWork();
  renderPomodoroText();
  renderStatusText(l10n.get(this.state.status));
  var pomodoroContainer = document.getElementById('pomodoro-container');
  this.pomodoro = new Progress(280, color.stroke, this.state.progress, pomodoroContainer);
  this.pomodoro.draw();
  renderTheme(color.stroke);
  saveInDataStore();

  function mapRange(obj, num) {
    return (num - obj.from[0]) * (obj.to[1] - obj.to[0]) / (obj.from[1] - obj.from[0]) + obj.to[0];
  }

  function parseMs(ms) {
    if (typeof ms !== 'number') {
      throw new TypeError('Expected a number');
    }

    var roundTowardZero = ms > 0 ? Math.floor : Math.ceil;

    return {
      days: roundTowardZero(ms / 86400000),
      hours: roundTowardZero(ms / 3600000) % 24,
      minutes: roundTowardZero(ms / 60000) % 60,
      seconds: roundTowardZero(ms / 1000) % 60,
      milliseconds: roundTowardZero(ms) % 1000
    };
  }

  function saveInDataStore() {
    datastore.setDataAsText({
      state: this.state
    });
    datastore.save(function (err) {
      if (err) {
        console.errror(err);
      }
    });
    setTimeout(saveInDataStore, 1000);
  }

  function convertReadableMS(timeInMs) {
    var parsedTime = parseMs(timeInMs);
    var timeStr = parsedTime.hours ? parsedTime.hours + parsedTime.days * 24 + ':' + parsedTime.minutes + ':' + parsedTime.seconds : parsedTime.minutes + ':' + parsedTime.seconds;
    return timeStr.split(':').map(function (num) {
      return ('' + num).padStart(2, '0');
    }).join(':');
  }

  this.handlePausePlay = function () {
    var playPauseButton = document.getElementById('play-button');
    if (this.state.status === 'work') {
      if (this.workTimer.state === 1) {
        //if timer is running
        this.workTimer.stop();
        enableButtons();
        playPauseButton.classList.remove('pause');
        playPauseButton.classList.add('play');
      } else {
        this.workTimer.start();
        disableButtons();
        playPauseButton.classList.remove('play');
        playPauseButton.classList.add('pause');
      }
    } else {
      if (this.breakTimer.state === 1) {
        this.breakTimer.stop();
        enableButtons();
        playPauseButton.classList.remove('pause');
        playPauseButton.classList.add('play');
      } else {
        disableButtons();
        this.breakTimer.start();
        playPauseButton.classList.remove('play');
        playPauseButton.classList.add('pause');
      }
    }
  };

  function startWork() {
    var _this2 = this;

    var timerInMS = this.state.workTimerLimit * 60 * 1000;
    this.workTimer = new Stopwatch(timerInMS);
    this.state.status = 'work';
    this.workTimer.onTime(function (time) {
      var progress = mapRange({
        from: [timerInMS, 0],
        to: [1, 0]
      }, time.ms);
      setProgress(progress, convertReadableMS(time.ms));
    });
    this.workTimer.onDone(function () {
      renderTheme(color.fill);
      setTimeout(function () {
        renderStatusText(l10n.get('break'));
        startBreak();
        _this2.breakTimer.start();
      }, 1000);
    });
  }

  function startBreak() {
    var _this3 = this;

    var timerInMS = this.state.breakTimerLimit * 60 * 1000;
    this.breakTimer = new Stopwatch(timerInMS);
    this.state.status = 'break';
    this.breakTimer.onTime(function (time) {
      var progress = mapRange({
        from: [timerInMS, 0],
        to: [1, 0]
      }, time.ms);
      setProgress(progress, convertReadableMS(time.ms));
    });
    this.breakTimer.onDone(function () {
      renderTheme(color.stroke);
      renderStatusText(l10n.get('work'));
      setTimeout(function () {
        startWork();
        _this3.workTimer.start();
      }, 1000);
    });
  }

  function renderStatusText(text) {
    document.querySelector('.status').innerText = text;
  }

  function setProgress(progress, currentTimerText) {
    this.state.progress = progress;
    if (this.state.status === 'work') {
      this.state.currentWorkText = currentTimerText;
    } else {
      this.state.currentBreakText = currentTimerText;
    }
    renderPomodoro();
    renderPomodoroText();
  }

  function renderPomodoro() {
    this.pomodoro.update(this.state.progress);
  }

  function renderPomodoroText() {
    var timerTextElem = document.querySelector('.info-circle span');
    if (this.state.status === 'work') {
      timerTextElem.innerText = this.state.currentWorkText;
    } else {
      timerTextElem.innerText = this.state.currentBreakText;
    }
  }

  function renderTheme(themeColor) {
    this.state.themeColor = themeColor;
    document.querySelector('.info-circle').style.backgroundColor = themeColor;
    document.querySelector('.base-circle').style.backgroundColor = themeColor + '2b';
    this.pomodoro.updateColor(themeColor, this.state.progress);
    renderButtons();
  }

  function renderButtons() {
    var _this4 = this;

    document.querySelectorAll('.button').forEach(function (elem) {
      elem.style.background = _this4.state.themeColor;
    });

    document.querySelectorAll('.button-label, .button-time').forEach(function (elem) {
      elem.style.color = _this4.state.themeColor;
    });
  }

  function disableButtons() {
    this.state.isButtonsDisable = true;
    document.querySelectorAll('.button, .button-time, .button-label').forEach(function (elem) {
      elem.classList.add('disable');
    });
  }
  function enableButtons() {
    this.state.isButtonsDisable = false;
    document.querySelectorAll('.button, .button-time, .button-label').forEach(function (elem) {
      elem.classList.remove('disable');
    });
  }

  function updateWorkPomodoro() {
    this.state.currentWorkText = convertReadableMS(this.state.workTimerLimit * 60 * 1000);
    document.querySelector('.button-time.work').innerText = this.state.workTimerLimit;
    this.pomodoro.update(1);
    renderPomodoroText();
    if (this.state.status === 'work') {
      startWork();
    }
  }

  function updateBreakPomodoro() {
    this.state.currentBreakText = convertReadableMS(this.state.breakTimerLimit * 60 * 1000);
    document.querySelector('.button-time.break').innerText = this.state.breakTimerLimit;
    renderPomodoroText();
    if (this.state.status === 'break') {
      startBreak();
    }
  }

  this.handleWorkPlusClick = function () {
    if (!this.state.isButtonsDisable) {
      this.state.workTimerLimit = this.state.workTimerLimit + 1;
      updateWorkPomodoro();
    }
  };

  this.handleWorkMinusClick = function () {
    if (!this.state.isButtonsDisable && this.state.workTimerLimit > 1) {
      this.state.workTimerLimit = this.state.workTimerLimit - 1;
      updateWorkPomodoro();
    }
  };

  this.handleBreakPlusClick = function () {
    if (!this.state.isButtonsDisable) {
      this.state.breakTimerLimit = this.state.breakTimerLimit + 1;
      updateBreakPomodoro();
    }
  };

  this.handleBreakMinusClick = function () {
    if (!this.state.isButtonsDisable) {
      this.state.breakTimerLimit = this.state.breakTimerLimit - 1;
      updateBreakPomodoro();
    }
  };

  function initTimerText() {
    document.querySelector('.button-time.work').innerText = this.state.workTimerLimit;
    document.querySelector('.button-time.break').innerText = this.state.breakTimerLimit;
  }

  initTimerText();
  document.querySelector('.plus-button.work').addEventListener('click', handleWorkPlusClick.bind(this));
  document.querySelector('.minus-button.work').addEventListener('click', handleWorkMinusClick.bind(this));
  document.querySelector('.plus-button.break').addEventListener('click', handleBreakPlusClick.bind(this));
  document.querySelector('.minus-button.break').addEventListener('click', handleBreakMinusClick.bind(this));
  document.querySelector('#play-button').addEventListener('click', function () {
    _this.handlePausePlay();
  });
  document.querySelector('#replay-button').addEventListener('click', function () {
    var shouldPlay = _this.breakTimer ? _this.workTimer.state === 1 || _this.breakTimer.state === 1 : _this.workTimer.state === 1;
    if (_this.state.status === 'break') {
      _this.breakTimer.stop();
      renderTheme(color.stroke);
      renderStatusText(l10n.get('work'));
      startWork();
      if (shouldPlay) {
        _this.workTimer.start();
      }
    }
    if (shouldPlay) {
      _this.handlePausePlay();
      updateWorkPomodoro();
      _this.handlePausePlay();
    } else {
      updateBreakPomodoro();
    }
  });
}<|endoftext|>


<commit_before>import Ember from 'ember';
import layout from '../templates/components/keyboard-navigable-list';
import { EKMixin, keyUp } from 'ember-keyboard';

export default Ember.Component.extend(EKMixin, {
  layout,
  objectKey: null,
  activeItem: -1,
  contentArray: [],
  hasLink: false,
  linkDirection: null,
  sendItem: null,
  afterLastItem: null,
  init(){
    this._super(...arguments);
    this.set('keyboardActivated', true);
    this.set('hasLink', !!this.get('linkDirection'));
  },
  upWatcher: Ember.on(keyUp('KeyK'), function() {
    this.moveUp();
  }),
  kWatcher: Ember.on(keyUp('ArrowUp'), function() {
    this.moveUp();
  }),
  Jwatcher: Ember.on(keyUp('KeyJ'), function() {
    this.moveDown();
  }),
  downWatcher: Ember.on(keyUp('ArrowDown'), function() {
    this.moveDown();
  }),
  moveUp(){
    if(this.get('activeItem') > 0){
      let activeItem = this.decrementProperty('activeItem');
      let selectedObj = this.getSelected(activeItem);
      this.sendSelectedItem(selectedObj);
    }

  },
  moveDown(){
    let contentArray = this.get('contentArray');
    if(this.get('activeItem') < contentArray.length - 1){
      let activeItem = this.incrementProperty('activeItem');
      let selectedObj = this.getSelected(activeItem);
      this.sendSelectedItem(selectedObj);
    } else {
      this.afterLastArrayItem(selectedObj)
    }
  },
  getSelected(activeIndex){
    return this.get('contentArray')[activeIndex];
  },
  sendSelectedItem(item){
    if(this.get('sendItem')){
      this.get('sendItem')(item);
    }
  },
  afterLastArrayItem(){
    if(this.get('afterLastItem')){
      this.get('afterLastItem')();
    }
  }
});
<commit_msg>fixed a bug<commit_after>import Ember from 'ember';
import layout from '../templates/components/keyboard-navigable-list';
import { EKMixin, keyUp } from 'ember-keyboard';

export default Ember.Component.extend(EKMixin, {
  layout,
  objectKey: null,
  activeItem: -1,
  contentArray: [],
  hasLink: false,
  linkDirection: null,
  sendItem: null,
  afterLastItem: null,
  init(){
    this._super(...arguments);
    this.set('keyboardActivated', true);
    this.set('hasLink', !!this.get('linkDirection'));
  },
  upWatcher: Ember.on(keyUp('KeyK'), function() {
    this.moveUp();
  }),
  kWatcher: Ember.on(keyUp('ArrowUp'), function() {
    this.moveUp();
  }),
  Jwatcher: Ember.on(keyUp('KeyJ'), function() {
    this.moveDown();
  }),
  downWatcher: Ember.on(keyUp('ArrowDown'), function() {
    this.moveDown();
  }),
  moveUp(){
    if(this.get('activeItem') > 0){
      let activeItem = this.decrementProperty('activeItem');
      let selectedObj = this.getSelected(activeItem);
      this.sendSelectedItem(selectedObj);
    }

  },
  moveDown(){
    let contentArray = this.get('contentArray');
    let active = this.get('activeItem');
    if(active < contentArray.length - 1){
      let activeItem = this.incrementProperty('activeItem');
      let selectedObj = this.getSelected(activeItem);
      this.sendSelectedItem(selectedObj);
    } else {
      this.afterLastArrayItem(active)
    }
  },
  getSelected(activeIndex){
    return this.get('contentArray')[activeIndex];
  },
  sendSelectedItem(item){
    if(this.get('sendItem')){
      this.get('sendItem')(item);
    }
  },
  afterLastArrayItem(){
    if(this.get('afterLastItem')){
      this.get('afterLastItem')();
    }
  }
});
<|endoftext|>


<commit_before>import Component from 'ember-component';
import closestPoint from 'ember-primer/utils/find-closest-cursor-points';
import run from 'ember-runloop';
import layout from './template';
import { ascending } from 'd3-array';

export default Component.extend({
  layout,
  tagName: 'g',

  classNames: ['Primer-Cursor'],

  xScale: null,

  yScale: null,

  values: [],

  x: 0,

  y: 0,

  isActive: false,

  _lastPosition: [],

  didInsertElement() {
    let svg = this.element.closest('svg');
    svg.addEventListener('mousemove', this.handleMouseMove.bind(this));
    svg.addEventListener('mouseleave', this.handleMouseLeave.bind(this));
    svg.addEventListener('mouseenter', this.handleMouseEnter.bind(this));
  },

  willRemoveElement() {
    let svg = this.element.closest('svg');
    svg.removeEventListener('mousemove', this.handleMouseMove);
    svg.removeEventListener('mouseleave', this.handleMouseLeave);
    svg.removeEventListener('mouseenter', this.handleMouseEnter);
  },

  handleMouseMove(event) {
    run.throttle(this, '_mouseMove', event, 16);
  },

  handleMouseLeave() {
    this.set('isActive', false);
  },

  handleMouseEnter() {
    this.set('isActive', true);
  },

  _convertCursorCoordinatesToDataCoordinates([x, y]) {
    return this.getAttr('convertCursorCoordinatesToDataCoordinates')([x, y]);
  },

  _mouseMove(event) {
    let { offsetY: y, offsetX: x } = event;
    let { xScale, yScale, values } = this.getProperties('xScale', 'yScale', 'values');
    let { xOffset, yOffset } = this.getProperties('xOffset', 'yOffset');

    if (!xScale) {
      this.setProperties({ isActive: true, x, y });
      this.sendAction('change', [x, y]);
    } else {
      let [[xPointer, yPointer], [xValue, ...yValues]] = closestPoint([x, y], [xOffset, yOffset], xScale, yScale, values);

      let [xLast, yLast] = this._lastPosition;
      if (xLast !== xPointer || yLast !== yPointer) {
        this.setProperties({ isActive: true, x: xPointer, y: yPointer });
        this.sendAction('change', [xValue, yValues], [xPointer, yPointer]);
        this.sendAction('_change', [xValue, yValues], [xPointer, yPointer]);

        this._lastPosition = [xPointer, yPointer];
      }

    }
  }
});
<commit_msg>Cursor now supports showing latest data by default<commit_after>import Component from 'ember-component';
import closestPoint from 'ember-primer/utils/find-closest-cursor-points';
import run from 'ember-runloop';
import layout from './template';
import { ascending } from 'd3-array';

export default Component.extend({
  layout,
  tagName: 'g',

  classNames: ['Primer-Cursor'],

  xScale: null,

  yScale: null,

  values: [],

  x: 0,

  y: 0,

  isActive: false,

  _lastPosition: [],

  position: [],

  showLatestWhenInactive: true,

  didInsertElement() {
    let svg = this.element.closest('svg');
    svg.addEventListener('mousemove', this.handleMouseMove.bind(this));
    svg.addEventListener('mouseleave', this.handleMouseLeave.bind(this));
    svg.addEventListener('mouseenter', this.handleMouseEnter.bind(this));

    run.next(this, this.handleMouseLeave);
  },

  willDestroyElement() {
    let svg = this.element.closest('svg');
    svg.removeEventListener('mousemove', this.handleMouseMove);
    svg.removeEventListener('mouseleave', this.handleMouseLeave);
    svg.removeEventListener('mouseenter', this.handleMouseEnter);
  },

  handleMouseMove(event) {
    run.throttle(this, '_mouseMove', event, 16);
  },

  handleMouseLeave() {
    let showLatestWhenInactive = this.get('showLatestWhenInactive');

    if (showLatestWhenInactive) {
      let xScale = this.get('xScale');
      let values = this.get('values');
      let [x1] = values[values.length - 1];
      this._mouseMove({ isActive: true, y: 0, x: xScale(x1) });

    } else {
      this.set('isActive', false);
    }
  },

  handleMouseEnter() {
    this.set('isActive', true);
  },

  _convertCursorCoordinatesToDataCoordinates([x, y]) {
    return this.getAttr('convertCursorCoordinatesToDataCoordinates')([x, y]);
  },

  didReceiveAttrs() {

  //   let xLast = this.get('x');
  //   let [x, y] = this.get('position');

  //   if (xLast !== x) {
  //     // let { offsetY: y, offsetX: x } = event;
  //     let { xScale, yScale, values } = this.getProperties('xScale', 'yScale', 'values');
  //     let { xOffset, yOffset } = this.getProperties('xOffset', 'yOffset');

  //     // console.log(this.debugKey, xLast, xPointer);
  //     let [[xPointer, yPointer], [xValue, ...yValues]] = closestPoint([x, y], [xOffset, yOffset], xScale, yScale, values);

  //     this.setProperties({ isActive: true, x: xPointer, y: yPointer });
  //   }
  },

  _mouseMove(event) {
    let { offsetY: y, offsetX: x } = event;
    let { xScale, yScale, values } = this.getProperties('xScale', 'yScale', 'values');
    let { xOffset, yOffset } = this.getProperties('xOffset', 'yOffset');

    if (!xScale) {
      this.setProperties({ isActive: true, x, y });
      this.sendAction('change', [x, y]);
    } else {
      let [[xPointer, yPointer], [xValue, ...yValues]] = closestPoint([x, y], [xOffset, yOffset], xScale, yScale, values);

      let [xLast, yLast] = this._lastPosition;
      if (xLast !== xPointer || yLast !== yPointer) {
        this.setProperties({ isActive: true, x: xPointer, y: yPointer });
        this.sendAction('change', [xValue, yValues], [xPointer, yPointer]);
        this.sendAction('_change', [xValue, yValues], [xPointer, yPointer]);

        this._lastPosition = [xPointer, yPointer];
      }

    }
  }
});
<|endoftext|>