<commit_before>/**
 * Holds shared logic for subgames.
 * How to use:
 * Number amount:        this.amount
 * Representation:       this.representation
 * The number to answer: this.currentNumber
 *
 * Add game objects to:     this.gameGroup
 * Add buttons and HUD to:  this.hudGroup
 * Use agent with:          this.agent (default visibility = false)
 *
 *
 * Functions:
 * Disable/Enable input:    this.disable (default is disabled)
 * Add event subscriptions: this.addEvent
 * Start game:              this.startGame
 * Go to next mode:         this.nextMode (only use this in intro-mode, the others change automatically)
 * Run next round:          this.nextRound (this will call the appropriate mode function)
 * Try a number:            this.tryNumber
 * Add water to the can:    this.addWater
 *
 * These function should be overshadowed by the game:
 * modeIntro      // Introduce the game, call nextMode and nextRound to start next mode.
 * modePlayerDo   // Player only
 * modePlayerShow // Player is showing the TA
 * modeAgentTry   // TA is guessing and the player is helping out
 * modeAgentDo    // TA only
 * modeOutro      // The game is finished, celebrate!
 *
 *
 * Typical game flow:
 * this.startGame();    // the first mode, this.modeIntro, will be called
 * this.nextMode();     // change mode, now this.modePlayerDo will be called
 * this.nextRound();    // start the mode
 * this.disable(false); // Make it possible to press anything.
 * this.tryNumber(x);   // try a number against the current one, returns true or false
 * this.nextRound();    // do this regardless if right or wrong,
 *                      // it takes care of mode switching and function calls for you
 * // Do until game is done, then quit by using: this.state.start(GLOBAL.STATE.garden);
 */
function Subgame () {}

/* Phaser state function */
Subgame.prototype.init = function (options) {
	/* "Private" variables */
	var _this = this; // Event subscriptions does not have access to this
	this._modes = options.mode || [
		GLOBAL.MODE.intro,
		GLOBAL.MODE.playerDo,
		GLOBAL.MODE.playerShow,
		GLOBAL.MODE.agentTry,
		GLOBAL.MODE.agentDo,
		GLOBAL.MODE.outro
	];
	this._mode = null;
	this._pendingMode = null;
	this._first = true;
	/* Keep track of how many rounds that have been played */
	this._counter = new Counter(options.roundsPerMode || 3, true);
	/* When enough rounds have been played, trigger a mode change */
	this._counter.onMax = function () {
		_this.nextMode();
	};
	this._currentTries = 0;
	this._totalTries = 0;
	this._events = [];
	this._skipper = null;

	/* Public variables */
	this.representation = options.representation;
	this.amount = options.amount;
	/* The current number to answer */
	this.currentNumber = null;
	/* The current mode running */
	this.currentMode = null;
	/* Stores the offset of the last try, can be used to judge last try */
	/* Ex: -1 means that last try was one less than currentNumber */
	this.lastTry = 0;

	/* Setup game objects */
	this.gameGroup = game.add.group();
	this.agent = new user.agent();
	this.agent.visible = false;
	this.gameGroup.add(this.agent);

	this.hudGroup = game.add.group();

	//  TODO: Is there an easier way to disable all input, except the menu?
	var disabler = new Cover('#ffffff', 0);
	disabler.inputEnabled = true;
	game.world.add(disabler);
	this.disable = function (value) { disabler.visible = value; };

	this.menuGroup = game.add.group();
	this.waterCan = new WaterCan(this.game.width - 100, 10);
	this.menuGroup.add(this.waterCan);
	this.menuGroup.add(new Menu());
	this.skipButton = game.add.button(75, 5, 'wood', this.skip, this, 0, 0, 1, 0, this.menuGroup);
	this.skipButton.visible = false;
	this.menuGroup.visible = false;
};

/* Phaser state function */
Subgame.prototype.shutdown = function () {
	TweenMax.killAll();
	this.sound.stopAll();

	for (var i = 0; i < this._events.length; i++) {
		unsubscribe(this._events[i]);
	}
};

Object.defineProperty(Subgame.prototype, 'skipper', {
	get: function() { return this._skipper; },
	set: function(value) {
		this._skipper = value;
		if (this._skipper){
			this.skipButton.visible = true;
			this._skipper.addCallback(function () {
				this.skipper = null;
			}, null, null, this);
		} else {
			this.skipButton.visible = false;
		}
	}
});

/* Skip a timeline. How to:
 * Set 'this.skipper' to a timeline (a skip button will appear next to the menu)
 * When the timeline is complete, make sure to set 'this.skipper' to null.
 */
Subgame.prototype.skip = function () {
	if (this.skipper) {
		this.skipper.totalProgress(1);
	}
};

/* Always add event subscriptions with these functions, they are then removed when shutting down */
/**
 * Subscribe to an event.
 * @param {*} The name of the event
 * @param {Function} The function to run when the event is published
 */
Subgame.prototype.addEvent = function (ev, func) {
	this._events.push(subscribe(ev, func));
};

/**
 * Unsubscribe to an event.
 * @param {*} The name of the event
 */
Subgame.prototype.removeEvent = function (ev) {
	for (var i = 0; i < this._events.length; i++) {
		if (this._events[i] === ev) {
			unsubscribe(this._events[i]);
			break;
		}
	}
};

/**
 * Calls the current mode function.
 * It will be called with two parameters:
 * 1) If it is the first time on this mode.
 * 2) How many tries that have been made on the current number.
 */
Subgame.prototype.nextRound = function () {
	this._mode(this._first, this._currentTries);
	this.currentMode = this._pendingMode;
	this._first = false;
};

/**
 * Translate from integer to mode function
 * @param {Number}
 */
Subgame.prototype.decideMode = function (mode) {
	if (mode === GLOBAL.MODE.intro) {
		this._mode = this.modeIntro;
	} else if (mode === GLOBAL.MODE.playerDo) {
		this._mode = this.modePlayerDo;
	} else if (mode === GLOBAL.MODE.playerShow) {
		this._mode = this.modePlayerShow;
	} else if (mode === GLOBAL.MODE.agentTry) {
		this._mode = this.modeAgentTry;
	} else if (mode === GLOBAL.MODE.agentDo) {
		this._mode = this.modeAgentDo;
	} else { // mode === GLOBAL.MODE.outro
		this._mode = this.modeOutro;
	}
};

/** Change to the next mode in the queue (publishes modeChange event). */
Subgame.prototype.nextMode = function () {
	var newMode = this._modes.shift();
	this.decideMode(newMode);
	this._pendingMode = newMode;
	this._first = true;
	publish(GLOBAL.EVENT.modeChange, [newMode]);
};

/** Change this.currentNumber to a new one (resets the tries). */
Subgame.prototype.nextNumber = function () {
	// Should we allow the same number again?
	this._totalTries += this._currentTries;
	this._currentTries = 0;
	this.currentNumber = parseInt(1+Math.random()*this.amount);
};

/**
 * Try a number against this.currentNumber (publishes tryNumber event).
 * The offset of the last try is stored in this.lastTry.
 *
 * @param {Number} The number to try.
 * @returns {Boolean} The offset of the last try (0 is correct, -x is too low, +x is too high).
 */
Subgame.prototype.tryNumber = function (number) {
	this._currentTries++;
	this.lastTry = number - this.currentNumber;

	if (!this.lastTry) {
		this._counter.value++; // This will trigger next mode if we loop.
		this.nextNumber();
	}
	publish(GLOBAL.EVENT.tryNumber, [this.currentNumber, number]);
	return this.lastTry;
};

Subgame.prototype.addWater = function (x, y, force) {
	var t = new TimelineMax();
	if (this.currentMode === GLOBAL.MODE.playerShow ||
		this.currentMode === GLOBAL.MODE.agentTry ||
		this.currentMode === GLOBAL.MODE.agentDo ||
		force) {
		var drop = this.add.sprite(x, y, 'drop', 0, this.menuGroup);
		drop.anchor.setTo(0.5);
		drop.scale.y = 0;

		// Show drop
		t.to(drop.scale, 1.5, { y: 1, ease:Elastic.easeOut })
			// Move drop
			.to(drop, 1.5, { x: this.waterCan.x + 30, y: this.waterCan.y, ease:Power2.easeOut })
			// Hide drop and add water
			.to(drop, 0.5, { height: 0,
				onStart: function () { user.water++; },
				onComplete: function () { drop.destroy(); }
			});
	}
	return t;
};

/** Start the game! */
Subgame.prototype.startGame = function () {
	// TODO: Should we wait for sounds to be decoded here, or are there any easier way?
	this.menuGroup.visible = true;
	this.nextMode();
	this.nextNumber();
	this.nextRound();
};

/* Overshadowing mode functions */
/* These functions should be overshadowed in the game object */
Subgame.prototype.modeIntro      = function () { this.nextMode(); };
Subgame.prototype.modePlayerDo   = function () { this.nextMode(); };
Subgame.prototype.modePlayerShow = function () { this.nextMode(); };
Subgame.prototype.modeAgentTry   = function () { this.nextMode(); };
Subgame.prototype.modeAgentDo    = function () { this.nextMode(); };
Subgame.prototype.modeOutro      = function () { this.nextMode(); };<commit_msg>Prettier skip button<commit_after>/**
 * Holds shared logic for subgames.
 * How to use:
 * Number amount:        this.amount
 * Representation:       this.representation
 * The number to answer: this.currentNumber
 *
 * Add game objects to:     this.gameGroup
 * Add buttons and HUD to:  this.hudGroup
 * Use agent with:          this.agent (default visibility = false)
 *
 *
 * Functions:
 * Disable/Enable input:    this.disable (default is disabled)
 * Add event subscriptions: this.addEvent
 * Start game:              this.startGame
 * Go to next mode:         this.nextMode (only use this in intro-mode, the others change automatically)
 * Run next round:          this.nextRound (this will call the appropriate mode function)
 * Try a number:            this.tryNumber
 * Add water to the can:    this.addWater
 *
 * These function should be overshadowed by the game:
 * modeIntro      // Introduce the game, call nextMode and nextRound to start next mode.
 * modePlayerDo   // Player only
 * modePlayerShow // Player is showing the TA
 * modeAgentTry   // TA is guessing and the player is helping out
 * modeAgentDo    // TA only
 * modeOutro      // The game is finished, celebrate!
 *
 *
 * Typical game flow:
 * this.startGame();    // the first mode, this.modeIntro, will be called
 * this.nextMode();     // change mode, now this.modePlayerDo will be called
 * this.nextRound();    // start the mode
 * this.disable(false); // Make it possible to press anything.
 * this.tryNumber(x);   // try a number against the current one, returns true or false
 * this.nextRound();    // do this regardless if right or wrong,
 *                      // it takes care of mode switching and function calls for you
 * // Do until game is done, then quit by using: this.state.start(GLOBAL.STATE.garden);
 */
function Subgame () {}

/* Phaser state function */
Subgame.prototype.init = function (options) {
	/* "Private" variables */
	var _this = this; // Event subscriptions does not have access to this
	this._modes = options.mode || [
		GLOBAL.MODE.intro,
		GLOBAL.MODE.playerDo,
		GLOBAL.MODE.playerShow,
		GLOBAL.MODE.agentTry,
		GLOBAL.MODE.agentDo,
		GLOBAL.MODE.outro
	];
	this._mode = null;
	this._pendingMode = null;
	this._first = true;
	/* Keep track of how many rounds that have been played */
	this._counter = new Counter(options.roundsPerMode || 3, true);
	/* When enough rounds have been played, trigger a mode change */
	this._counter.onMax = function () {
		_this.nextMode();
	};
	this._currentTries = 0;
	this._totalTries = 0;
	this._events = [];
	this._skipper = null;

	/* Public variables */
	this.representation = options.representation;
	this.amount = options.amount;
	/* The current number to answer */
	this.currentNumber = null;
	/* The current mode running */
	this.currentMode = null;
	/* Stores the offset of the last try, can be used to judge last try */
	/* Ex: -1 means that last try was one less than currentNumber */
	this.lastTry = 0;

	/* Setup game objects */
	this.gameGroup = game.add.group();
	this.agent = new user.agent();
	this.agent.visible = false;
	this.gameGroup.add(this.agent);

	this.hudGroup = game.add.group();

	//  TODO: Is there an easier way to disable all input, except the menu?
	var disabler = new Cover('#ffffff', 0);
	disabler.inputEnabled = true;
	game.world.add(disabler);
	this.disable = function (value) { disabler.visible = value; };

	this.menuGroup = game.add.group();
	this.waterCan = new WaterCan(this.game.width - 100, 10);
	this.menuGroup.add(this.waterCan);
	this.menuGroup.add(new Menu());
	this.skipGroup = game.add.group(this.menuGroup);
	this.skipGroup.x = 75;
	this.skipGroup.y = 5;
	this.skipGroup.visible = false;
	game.add.button(0, 0, 'wood', this.skip, this, 0, 0, 1, 0, this.skipGroup);
	game.add.text(10, -5, '>>', {
		font: '30pt The Girl Next Door',
		stroke: '#000000',
		strokeThickness: 3
	}, this.skipGroup);
	this.menuGroup.visible = false;
};

/* Phaser state function */
Subgame.prototype.shutdown = function () {
	TweenMax.killAll();
	this.sound.stopAll();

	for (var i = 0; i < this._events.length; i++) {
		unsubscribe(this._events[i]);
	}
};

Object.defineProperty(Subgame.prototype, 'skipper', {
	get: function() { return this._skipper; },
	set: function(value) {
		this._skipper = value;
		if (this._skipper){
			this.skipGroup.visible = true;
			this._skipper.addCallback(function () {
				this.skipper = null;
			}, null, null, this);
		} else {
			this.skipGroup.visible = false;
		}
	}
});

/* Skip a timeline. How to:
 * Set 'this.skipper' to a timeline (a skip button will appear next to the menu)
 * When the timeline is complete, make sure to set 'this.skipper' to null.
 */
Subgame.prototype.skip = function () {
	if (this.skipper) {
		this.skipper.totalProgress(1);
	}
};

/* Always add event subscriptions with these functions, they are then removed when shutting down */
/**
 * Subscribe to an event.
 * @param {*} The name of the event
 * @param {Function} The function to run when the event is published
 */
Subgame.prototype.addEvent = function (ev, func) {
	this._events.push(subscribe(ev, func));
};

/**
 * Unsubscribe to an event.
 * @param {*} The name of the event
 */
Subgame.prototype.removeEvent = function (ev) {
	for (var i = 0; i < this._events.length; i++) {
		if (this._events[i] === ev) {
			unsubscribe(this._events[i]);
			break;
		}
	}
};

/**
 * Calls the current mode function.
 * It will be called with two parameters:
 * 1) If it is the first time on this mode.
 * 2) How many tries that have been made on the current number.
 */
Subgame.prototype.nextRound = function () {
	this._mode(this._first, this._currentTries);
	this.currentMode = this._pendingMode;
	this._first = false;
};

/**
 * Translate from integer to mode function
 * @param {Number}
 */
Subgame.prototype.decideMode = function (mode) {
	if (mode === GLOBAL.MODE.intro) {
		this._mode = this.modeIntro;
	} else if (mode === GLOBAL.MODE.playerDo) {
		this._mode = this.modePlayerDo;
	} else if (mode === GLOBAL.MODE.playerShow) {
		this._mode = this.modePlayerShow;
	} else if (mode === GLOBAL.MODE.agentTry) {
		this._mode = this.modeAgentTry;
	} else if (mode === GLOBAL.MODE.agentDo) {
		this._mode = this.modeAgentDo;
	} else { // mode === GLOBAL.MODE.outro
		this._mode = this.modeOutro;
	}
};

/** Change to the next mode in the queue (publishes modeChange event). */
Subgame.prototype.nextMode = function () {
	var newMode = this._modes.shift();
	this.decideMode(newMode);
	this._pendingMode = newMode;
	this._first = true;
	publish(GLOBAL.EVENT.modeChange, [newMode]);
};

/** Change this.currentNumber to a new one (resets the tries). */
Subgame.prototype.nextNumber = function () {
	// Should we allow the same number again?
	this._totalTries += this._currentTries;
	this._currentTries = 0;
	this.currentNumber = parseInt(1+Math.random()*this.amount);
};

/**
 * Try a number against this.currentNumber (publishes tryNumber event).
 * The offset of the last try is stored in this.lastTry.
 *
 * @param {Number} The number to try.
 * @returns {Boolean} The offset of the last try (0 is correct, -x is too low, +x is too high).
 */
Subgame.prototype.tryNumber = function (number) {
	this._currentTries++;
	this.lastTry = number - this.currentNumber;

	if (!this.lastTry) {
		this._counter.value++; // This will trigger next mode if we loop.
		this.nextNumber();
	}
	publish(GLOBAL.EVENT.tryNumber, [this.currentNumber, number]);
	return this.lastTry;
};

Subgame.prototype.addWater = function (x, y, force) {
	var t = new TimelineMax();
	if (this.currentMode === GLOBAL.MODE.playerShow ||
		this.currentMode === GLOBAL.MODE.agentTry ||
		this.currentMode === GLOBAL.MODE.agentDo ||
		force) {
		var drop = this.add.sprite(x, y, 'drop', 0, this.menuGroup);
		drop.anchor.setTo(0.5);
		drop.scale.y = 0;

		// Show drop
		t.to(drop.scale, 1.5, { y: 1, ease:Elastic.easeOut })
			// Move drop
			.to(drop, 1.5, { x: this.waterCan.x + 30, y: this.waterCan.y, ease:Power2.easeOut })
			// Hide drop and add water
			.to(drop, 0.5, { height: 0,
				onStart: function () { user.water++; },
				onComplete: function () { drop.destroy(); }
			});
	}
	return t;
};

/** Start the game! */
Subgame.prototype.startGame = function () {
	// TODO: Should we wait for sounds to be decoded here, or are there any easier way?
	this.menuGroup.visible = true;
	this.nextMode();
	this.nextNumber();
	this.nextRound();
};

/* Overshadowing mode functions */
/* These functions should be overshadowed in the game object */
Subgame.prototype.modeIntro      = function () { this.nextMode(); };
Subgame.prototype.modePlayerDo   = function () { this.nextMode(); };
Subgame.prototype.modePlayerShow = function () { this.nextMode(); };
Subgame.prototype.modeAgentTry   = function () { this.nextMode(); };
Subgame.prototype.modeAgentDo    = function () { this.nextMode(); };
Subgame.prototype.modeOutro      = function () { this.nextMode(); };<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2014 FusionCharts Technologies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

(function () {
    var fc = angular.module('ng-fusioncharts', []);

    fc.directive('fusioncharts', ['$http', function ($http) {
            return {
                scope: {
                    width: '@',
                    height: '@',
                    data: '@',
                    dataset: '@',
                    categories: '@',
                    chart: '@',
                    linkeddata: '@',
                    trendlines: '@',
                    vtrendlines: '@',
                    annotations: '@',
                    colorrange: '@',
                    lineset: '@',
                    axis: '@',
                    connectors: '@',
                    pointers: '@',
                    value: '@',
                    processes: '@',
                    tasks: '@',
                    rows: '@',
                    columns: '@',
                    map: '@',
                    markers: '@'
                },
                link: function (scope, element, attrs) {
                    var observeConf = {
                        'width': {
                            ifExist: false,
                            observer: function (newVal) {
                                chart.resizeTo(scope.width, scope.height);
                            }
                        },
                        'height': {
                            ifExist: false,
                            observer: function (newVal) {
                                chart.resizeTo(scope.width, scope.height);
                            }
                        },
                        'chart': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.chart = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    }, 0);
                                }
                            }
                        },
                        'data': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.data = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    }, 0);
                                }
                            }
                        },
                        'categories': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.categories = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    }, 0);
                                }
                            }
                        },
                        'dataset': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.dataset = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'linkeddata': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.linkeddata = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'trendlines': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.trendlines = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'vtrendlines': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.vtrendlines = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'annotations': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.annotations = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'colorrange': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.colorrange = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'lineset': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.lineset = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'axis': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.axis = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'connectors': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.connectors = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'pointers': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.pointers = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'value': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.value = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'processes': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.processes = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'tasks': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.tasks = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'rows': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.rows = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'columns': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.columns = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'map': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.map = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            },
                        },
                        'markers': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.markers = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'datasource': {
                            ifExist: true,
                            observer: function (newVal) {
                                chartConfigObject.dataSource = newVal;
                                if (chartConfigObject.dataFormat === 'json') {
                                    chartConfigObject.dataSource = JSON.parse(newVal);
                                    chart.setChartData(chartConfigObject.dataSource);
                                } else if (chartConfigObject.dataFormat === 'xml') {
                                    chart.setXMLData(newVal);
                                } else if (chartConfigObject.dataFormat === 'jsonurl') {
                                    chart.setJSONUrl(newVal);
                                } else if (chartConfigObject.dataFormat === 'xmlurl') {
                                    chart.setXMLUrl(newVal);
                                }
                            }
                        },
                        'type': {
                            ifExist: false,
                            observer: function (newVal) {
                                if (newVal) {
                                    chartConfigObject.type = newVal;
                                    createFCChart();
                                }
                            }
                        },
                        'config': {
                            ifExist: false,
                            observer: function (newVal) {
                                var configObj;
                                if (newVal) {
                                    configObj = JSON.parse(newVal),
                                    attr;
                                    for (attr in configObj) {
                                        chartConfigObject[attr] = configObj[attr];
                                    }
                                    createFCChart();
                                }
                            }
                        }
                    },
                    eventsObj = {},
                    attribs = Object.keys(attrs),
                    chart = null,
                    events = {
                        '*': function (ev, props) {
                            if (eventsObj.hasOwnProperty(ev.eventType)) {
                                eventsObj[ev.eventType](ev, props);
                            }
                        }
                    },
                    createFCChart = function () {
                        // dispose if previous chart exists
                        if(chart && chart.dispose){
                            chart.dispose();
                        }
                        chart = new FusionCharts(chartConfigObject);
                        scope[attrs.chartobject] = chart;
                        chart.render();
                    },
                    i,
                    attr,
                    _eobj,
                    key,
                    observableAttr,
                    chartConfigObject,
                    configObj;

                    if (attrs.events) {
                        if (scope.$parent[attrs.events]) {
                            _eobj = scope.$parent[attrs.events];
                            for (key in _eobj) {
                                if (_eobj.hasOwnProperty(key)) {
                                    eventsObj[key.toLowerCase()] = _eobj[key];
                                }
                            }
                        }
                    }
                    for (i = 0; i < attribs.length; i++) {
                        attr = attribs[i];
                        if (attr.match(/^on/i)) {
                            key = attr.slice(2).toLowerCase();
                            eventsObj[key] = scope.$parent[attrs[attr]];
                        }
                    }

                    chartConfigObject = {
                        type: attrs.type,
                        width: attrs.width,
                        height: attrs.height,
                        renderAt: element[0],
                        id: attrs.chartid,
                        dataFormat: attrs.dataformat || 'json',
                        dataSource: {},
                        events: events
                    };

                    for (observableAttr in observeConf) {
                        attrConfig = observeConf[observableAttr];
                        if (attrConfig.ifExist === false || attrs[observableAttr]) {
                            attrs.$observe(observableAttr, attrConfig.observer);
                        }
                    }

                    createFCChart();
                }
            };
        }
    ]);
}());<commit_msg>Fixes #11. This commit fix the issue of restored dimension after config change.<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2014 FusionCharts Technologies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

(function () {
    var fc = angular.module('ng-fusioncharts', []);

    fc.directive('fusioncharts', ['$http', function ($http) {
            return {
                scope: {
                    width: '@',
                    height: '@',
                    data: '@',
                    dataset: '@',
                    categories: '@',
                    chart: '@',
                    linkeddata: '@',
                    trendlines: '@',
                    vtrendlines: '@',
                    annotations: '@',
                    colorrange: '@',
                    lineset: '@',
                    axis: '@',
                    connectors: '@',
                    pointers: '@',
                    value: '@',
                    processes: '@',
                    tasks: '@',
                    rows: '@',
                    columns: '@',
                    map: '@',
                    markers: '@'
                },
                link: function (scope, element, attrs) {
                    var observeConf = {
                        'width': {
                            ifExist: false,
                            observer: function (newVal) {
                                if (newVal) {
                                    chartConfigObject.width = newVal;
                                    chart.resizeTo(scope.width, scope.height);
                                }
                            }
                        },
                        'height': {
                            ifExist: false,
                            observer: function (newVal) {
                                if (newVal){
                                    chartConfigObject.height = newVal;
                                    chart.resizeTo(scope.width, scope.height);
                                }
                            }
                        },
                        'chart': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.chart = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    }, 0);
                                }
                            }
                        },
                        'data': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.data = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    }, 0);
                                }
                            }
                        },
                        'categories': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.categories = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    }, 0);
                                }
                            }
                        },
                        'dataset': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.dataset = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'linkeddata': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.linkeddata = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'trendlines': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.trendlines = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'vtrendlines': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.vtrendlines = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'annotations': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.annotations = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'colorrange': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.colorrange = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'lineset': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.lineset = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'axis': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.axis = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'connectors': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.connectors = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'pointers': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.pointers = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'value': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.value = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'processes': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.processes = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'tasks': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.tasks = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'rows': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.rows = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'columns': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.columns = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'map': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.map = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            },
                        },
                        'markers': {
                            ifExist: true,
                            observer: function (newVal) {
                                if (chartConfigObject.dataFormat === 'json' && typeof chartConfigObject.dataSource == "object") {
                                    setTimeout(function () {
                                        chartConfigObject.dataSource.markers = JSON.parse(newVal);
                                        chart.setJSONData(chartConfigObject.dataSource);
                                    });
                                }
                            }
                        },
                        'datasource': {
                            ifExist: true,
                            observer: function (newVal) {
                                chartConfigObject.dataSource = newVal;
                                if (chartConfigObject.dataFormat === 'json') {
                                    chartConfigObject.dataSource = JSON.parse(newVal);
                                    chart.setChartData(chartConfigObject.dataSource);
                                } else if (chartConfigObject.dataFormat === 'xml') {
                                    chart.setXMLData(newVal);
                                } else if (chartConfigObject.dataFormat === 'jsonurl') {
                                    chart.setJSONUrl(newVal);
                                } else if (chartConfigObject.dataFormat === 'xmlurl') {
                                    chart.setXMLUrl(newVal);
                                }
                            }
                        },
                        'type': {
                            ifExist: false,
                            observer: function (newVal) {
                                if (newVal) {
                                    chartConfigObject.type = newVal;
                                    createFCChart();
                                }
                            }
                        },
                        'config': {
                            ifExist: false,
                            observer: function (newVal) {
                                var configObj;
                                if (newVal) {
                                    configObj = JSON.parse(newVal),
                                    attr;
                                    for (attr in configObj) {
                                        chartConfigObject[attr] = configObj[attr];
                                    }
                                    createFCChart();
                                }
                            }
                        }
                    },
                    eventsObj = {},
                    attribs = Object.keys(attrs),
                    chart = null,
                    events = {
                        '*': function (ev, props) {
                            if (eventsObj.hasOwnProperty(ev.eventType)) {
                                eventsObj[ev.eventType](ev, props);
                            }
                        }
                    },
                    createFCChart = function () {
                        // dispose if previous chart exists
                        if(chart && chart.dispose){
                            chart.dispose();
                        }
                        chart = new FusionCharts(chartConfigObject);
                        scope[attrs.chartobject] = chart;
                        chart.render();
                    },
                    i,
                    attr,
                    _eobj,
                    key,
                    observableAttr,
                    chartConfigObject,
                    configObj;

                    if (attrs.events) {
                        if (scope.$parent[attrs.events]) {
                            _eobj = scope.$parent[attrs.events];
                            for (key in _eobj) {
                                if (_eobj.hasOwnProperty(key)) {
                                    eventsObj[key.toLowerCase()] = _eobj[key];
                                }
                            }
                        }
                    }
                    for (i = 0; i < attribs.length; i++) {
                        attr = attribs[i];
                        if (attr.match(/^on/i)) {
                            key = attr.slice(2).toLowerCase();
                            eventsObj[key] = scope.$parent[attrs[attr]];
                        }
                    }

                    chartConfigObject = {
                        type: attrs.type,
                        width: attrs.width,
                        height: attrs.height,
                        renderAt: element[0],
                        id: attrs.chartid,
                        dataFormat: attrs.dataformat || 'json',
                        dataSource: {},
                        events: events
                    };

                    for (observableAttr in observeConf) {
                        attrConfig = observeConf[observableAttr];
                        if (attrConfig.ifExist === false || attrs[observableAttr]) {
                            attrs.$observe(observableAttr, attrConfig.observer);
                        }
                    }

                    createFCChart();
                }
            };
        }
    ]);
}());<|endoftext|>


<commit_before>var webpack=require("webpack");
var HtmlWebpackPlugin=require("html-webpack-plugin");
var ExtractTextPlugin=require("extract-text-webpack-plugin");
var path=require("path");
var debug=true;

module.exports={
    entry: {
        index:'./src/index',
        vendor:['vue','vue-router']
    },
    output:{
        path:path.join(__dirname,'/dist'),
        filename:debug?'[name].min.js':'[name].[chunkhash:5].min.js',
        publicPath:'/dist/',
        chunkFilename:'[id].[chunkhash:5].chunk.js'
    },
    module:{
        loaders:[
            {
                test:/\.js[x]?$/,
                loader:'babel',
                exclude:/node_modules/,
                plugins:['transform-time']
            },
            {
                test:/\.css$/,
                loader:debug?'style!css':ExtractTextPlugin.extract('style-loader','css-loader')
            },
            {
                test:/\.(eot|svg|ttf|woff|woff2)(\?\S*)$/,
                loader:'file-loader'
            },
            {
                test:/\.(png|jpe?g|svg|gif)(\?\S*)?$/,
                loader:'url?limit=8192',
                query:{
                    name:'[name].[ext]?[hash]'
                }
            },
            {
                test:/\.vue$/,
                loader:'vue'
            }
        ]
    },
    resolve:{
        extensions:['','.js','.jsx'],
        alias:{
            'vue$':'vue/dist/vue.min.js'
        }
    },
    plugins:[
        // new webpack.DefinePlugin({
        //     'process.env':{
        //         NODE_ENV:'"production"'
        //     }
        // }),
        new ExtractTextPlugin("[name].css"),
        new webpack.optimize.CommonsChunkPlugin('vendor',debug?'commons.js':'commons.[chunkhash:5].js'),
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        }),
        new HtmlWebpackPlugin({
            template:path.resolve(__dirname,'index.html'),
            inject:true,
            minify:{
                removeComments:true,
                collapseWhitespace:true
            }
        })
    ]
}

<commit_msg>debug false<commit_after>var webpack=require("webpack");
var HtmlWebpackPlugin=require("html-webpack-plugin");
var ExtractTextPlugin=require("extract-text-webpack-plugin");
var path=require("path");
var debug=false;

module.exports={
    entry: {
        index:'./src/index',
        vendor:['vue','vue-router']
    },
    output:{
        path:path.join(__dirname,'/dist'),
        filename:debug?'[name].min.js':'[name].[chunkhash:5].min.js',
        publicPath:'/dist/',
        chunkFilename:'[id].[chunkhash:5].chunk.js'
    },
    module:{
        loaders:[
            {
                test:/\.js[x]?$/,
                loader:'babel',
                exclude:/node_modules/,
                plugins:['transform-time']
            },
            {
                test:/\.css$/,
                loader:debug?'style!css':ExtractTextPlugin.extract('style-loader','css-loader')
            },
            {
                test:/\.(eot|svg|ttf|woff|woff2)(\?\S*)$/,
                loader:'file-loader'
            },
            {
                test:/\.(png|jpe?g|svg|gif)(\?\S*)?$/,
                loader:'url?limit=8192',
                query:{
                    name:'[name].[ext]?[hash]'
                }
            },
            {
                test:/\.vue$/,
                loader:'vue'
            }
        ]
    },
    resolve:{
        extensions:['','.js','.jsx'],
        alias:{
            'vue$':'vue/dist/vue.min.js'
        }
    },
    plugins:[
        // new webpack.DefinePlugin({
        //     'process.env':{
        //         NODE_ENV:'"production"'
        //     }
        // }),
        new ExtractTextPlugin("[name].css"),
        new webpack.optimize.CommonsChunkPlugin('vendor',debug?'commons.js':'commons.[chunkhash:5].js'),
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        }),
        new HtmlWebpackPlugin({
            template:path.resolve(__dirname,'index.html'),
            inject:true,
            minify:{
                removeComments:true,
                collapseWhitespace:true
            }
        })
    ]
}

<|endoftext|>


<commit_before>const INITIAL_STATE = {
  logs: null,
};

// Actions
const APPEND_LOG_MESSAGE = 'APPEND_LOG_MESSAGE';

// Action Creators
export const appendLogMessage = message => ({ type: APPEND_LOG_MESSAGE, payload: message });

// Reducer
export default function reducer(state = INITIAL_STATE, action) {
  switch (action.type) {
    case APPEND_LOG_MESSAGE:
      return {
        ...state,
        logs: state.logs ? state.logs.concat(action.payload) : action.payload,
      };
    default:
      return state;
  }
}
<commit_msg>Show last 500 log messages<commit_after>import { takeRight } from 'lodash';

const INITIAL_STATE = {
  logs: [],
};

// Actions
const APPEND_LOG_MESSAGE = 'APPEND_LOG_MESSAGE';

const exp = new RegExp(/(\{"message":.+"level":.+\})/g);

// Action Creators
export const appendLogMessage = message => ({ type: APPEND_LOG_MESSAGE, payload: message });

// Reducer
export default function reducer(state = INITIAL_STATE, action) {
  switch (action.type) {
    case APPEND_LOG_MESSAGE:
      // eslint-disable-next-line no-case-declarations
      const logs = action.payload.replace(/},{/g, '}\n{').match(exp);

      return {
        ...state,
        logs: takeRight([...state.logs, ...logs], 500),
      };
    default:
      return state;
  }
}
<|endoftext|>


<commit_before>define([], function() {

    'use strict';

	return ['$scope', '$location' ,'$routeParams', 'Rest', '$modal',
            function($scope, $location, $routeParams, Rest, $modal) {

        // if there is createable special right
        // open a popup to define special only if right not saved

        if (!$routeParams.id) {
            var specialRightsResource = Rest.admin.specialrights.getResource();
            var specialRights = specialRightsResource.query({ country: $scope.company.country, create: true });

            specialRights.$promise.then(function() {
                if (specialRights.length > 0) {

                    var modalscope = $scope.$new();
                    modalscope.specialRights = specialRights;
                    modalscope.specialselection = '';

                    $modal({
                        scope: modalscope,
                        templateUrl: 'partials/admin/modal-specialrights.html',
                        show: true
                    });
                }
            });
        }

        $scope.nameReadOnly = false;
        $scope.descriptionReadOnly = false;
        $scope.editQuantity = true;

        $scope.setSpecialRight = function(specialright) {

            if ('' === specialright) {
                $scope.nameReadOnly = false;
                $scope.descriptionReadOnly = false;
                $scope.right.special = undefined;
                $scope.right.name = '';
                return;
            }

            for (let defaultProp in specialright.default) {
                if (specialright.default[defaultProp]) {
                    $scope.right[defaultProp] = specialright.default[defaultProp];
                }
            }

            $scope.editQuantity = specialright.editQuantity;
            $scope.quantityLabel = specialright.quantityLabel;

            $scope.nameReadOnly = (null !== specialright.default.name);
            $scope.descriptionReadOnly = (null !== specialright.default.description);
        };

		$scope.right = Rest.admin.rights.getFromUrl().loadRouteId();
        $scope.types = Rest.admin.types.getResource().query();

        if ($scope.right.$promise) {
            // right modification
            $scope.right.$promise.then(function() {
                if ($scope.right.special) {
                    // TODO: get special status for namereadonly
                    $scope.nameReadOnly = true;
                }
            });
        } else {

            // right creation

            $scope.right = {
                activeFor: {
                    account:true,
                    manager: true,
                    admin: true
                },
                activeSpan: {
                    useDefault: true
                },
                consuption: 'proportion'
            };
        }

        $scope.quantityUnits = [{
            value: 'D',
            label: 'Days'
        },
        {
            value: 'H',
            label: 'Hours'
        }];

		$scope.back = function() {
			$location.path('/admin/rights');
		};

		$scope.saveRight = function() {
			$scope.right.gadaSave($scope.back);
	    };
	}];
});

<commit_msg>remove let in public<commit_after>define([], function() {

    'use strict';

	return ['$scope', '$location' ,'$routeParams', 'Rest', '$modal',
            function($scope, $location, $routeParams, Rest, $modal) {

        // if there is createable special right
        // open a popup to define special only if right not saved

        if (!$routeParams.id) {
            var specialRightsResource = Rest.admin.specialrights.getResource();
            var specialRights = specialRightsResource.query({ country: $scope.company.country, create: true });

            specialRights.$promise.then(function() {
                if (specialRights.length > 0) {

                    var modalscope = $scope.$new();
                    modalscope.specialRights = specialRights;
                    modalscope.specialselection = '';

                    $modal({
                        scope: modalscope,
                        templateUrl: 'partials/admin/modal-specialrights.html',
                        show: true
                    });
                }
            });
        }

        $scope.nameReadOnly = false;
        $scope.descriptionReadOnly = false;
        $scope.editQuantity = true;

        $scope.setSpecialRight = function(specialright) {

            if ('' === specialright) {
                $scope.nameReadOnly = false;
                $scope.descriptionReadOnly = false;
                $scope.right.special = undefined;
                $scope.right.name = '';
                return;
            }

            for (var defaultProp in specialright.default) {
                if (specialright.default[defaultProp]) {
                    $scope.right[defaultProp] = specialright.default[defaultProp];
                }
            }

            $scope.editQuantity = specialright.editQuantity;
            $scope.quantityLabel = specialright.quantityLabel;

            $scope.nameReadOnly = (null !== specialright.default.name);
            $scope.descriptionReadOnly = (null !== specialright.default.description);
        };

		$scope.right = Rest.admin.rights.getFromUrl().loadRouteId();
        $scope.types = Rest.admin.types.getResource().query();

        if ($scope.right.$promise) {
            // right modification
            $scope.right.$promise.then(function() {
                if ($scope.right.special) {
                    // TODO: get special status for namereadonly
                    $scope.nameReadOnly = true;
                }
            });
        } else {

            // right creation

            $scope.right = {
                activeFor: {
                    account:true,
                    manager: true,
                    admin: true
                },
                activeSpan: {
                    useDefault: true
                },
                consuption: 'proportion'
            };
        }

        $scope.quantityUnits = [{
            value: 'D',
            label: 'Days'
        },
        {
            value: 'H',
            label: 'Hours'
        }];

		$scope.back = function() {
			$location.path('/admin/rights');
		};

		$scope.saveRight = function() {
			$scope.right.gadaSave($scope.back);
	    };
	}];
});

<|endoftext|>


<commit_before>import { getEditorContent } from '../editor/editor';
import { map } from '../map/map';
import { getScreenshotData } from '../map/screenshot';
// import { getLocationLabel } from '../map/search'; // TODO: implement now that move to react has changed this
import { createThumbnail } from '../tools/thumbnail';

import { find } from 'lodash';
import L from 'leaflet';

const METADATA_DIR = '.tangramplay/';
const METADATA_FILEPATH = METADATA_DIR + 'metadata.json';
const THUMBNAIL_WIDTH = 144;
const THUMBNAIL_HEIGHT = 81;
const THUMBNAIL_FILEPATH = METADATA_DIR + 'thumbnail.png';
const SCENELIST_FILEPATH = 'scenelist.json';

// We don't allow this to be customized (yet) and in the future you might
// have multiple scene files anyway, so we'll do that all at once when we figure
// it out.
const SCENE_FILENAME = 'scene.yaml';

/*
    Store files like this:
        ./scene-name-slug/scene.yaml
        ./scene-name-slug/[etc]
        ./scene-name-slug/.tangramplay/metadata.json
        ./scene-name-slug/.tangramplay/thumbnail.png

    Meta files are kept separate from scene related files but in a
    .tangramplay directory in the scene directory's root. This is to keep
    url schemes simple for the end user and namespace Play-related
    files. Think .git or similar.

    Scene files may eventually contain any quantity of files.
    (e.g. textures, multiple yamls, fonts, etc)

    Tangram Play only has two scene-related meta files now, a json
    for random strings and a thumbnail image.

    Eventually Tangram Play per-user settings may be stored in the root
*/

export function saveToMapzenUserAccount (data, successCallback, errorCallback) {
    // Add timestamp to `data`
    data.timestamp = new Date().toJSON();

    // Get a scene directory by slugifying the scene name, add a trailing slash.
    const sceneDir = slugify(data.name) + '/';

    // These are Promises. Each creates its own contents and makes
    // individual upload requests. We resolve this function when all Promises
    // resolve.
    const uploadThumbnail = makeAndUploadThumbnail(sceneDir);
    const uploadMetadata = makeAndUploadMetadata(data, sceneDir);
    const uploadScene = makeAndUploadScene(data, sceneDir);

    return Promise.all([uploadThumbnail, uploadMetadata, uploadScene])
        .then((savedLocations) => {
            // Should all be urls of the saved files.
            // Returns some an object containing metadata for the success handler
            // Errors should be handled upstream as well
            return downloadAndUpdateSceneList(data, savedLocations);
        });
}

// super quickly written slugify function. only replaces whitespace with dashes.
// does not deal with funky characters. or condense spaces, all that stuff.
// should also eventually enforce a maximum length. or handle errors.
function slugify (string) {
    return string.trim().toLowerCase().replace(/\s/g, '-');
}

/**
 * Upload a file using the Mapzen upload API.
 *
 * @param {*} contents - content of the file to upload. This will be converted
 *      to a Blob object by this function.
 * @param {string} filepath - file path and file name to save to, relative
 *      to the app data directory. e.g. 'scene-name/scene.yaml'
 * @param {string} type - MIME type of contents. Defaults to 'plain/text'.
 * @returns {Promise} - fulfilled with the response of the POST request.
 */
function uploadFile (contents, filepath, type = 'plain/text') {
    const APP_NAME = 'play';
    let uploadedFileUrl;

    return window.fetch(`/api/uploads/new?app=${APP_NAME}&path=${filepath}`, {
        credentials: 'same-origin'
    }).then((response) => {
        return response.json();
    }).then((upload) => {
        const formData = new FormData();
        Object.keys(upload.fields).forEach(field => {
            formData.append(field, upload.fields[field]);
        });

        const blob = new Blob([contents], { type: type });
        formData.append('file', blob, filepath);

        // Remember the location of the file we want to save.
        uploadedFileUrl = upload.url + upload.path;

        return window.fetch(upload.url, {
            method: 'POST',
            body: formData
        });
    }).then((response) => {
        // If uploaded, return the url of the uploaded file.
        if (response.ok) {
            return uploadedFileUrl;
        }
        else {
            // Caller should catch this error.
            // For now it just passes the status code.
            throw new Error(response.status);
        }
    });
}

/**
 * Creates and uploads a thumbnail image of the current map scene.
 *
 * @params {string} sceneDir - slugified scene name to use as scene directory
 * @returns {Promise} - fulfilled with the response of the POST request.
 */
function makeAndUploadThumbnail (sceneDir) {
    // Grab a screenshot from the map and convert it to a thumbnail at a fixed
    // dimension. This makes file sizes and layout more predictable.
    return getScreenshotData()
        .then((screenshot) => {
            // At this size, thumbnail image should clock in at around ~90kb
            // to ~120kb (unoptimized, but that's the limitations of our
            // thumbnail function)
            return createThumbnail(screenshot.blob, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);
        })
        .then((thumbnail) => {
            // Make upload request
            return uploadFile(thumbnail, sceneDir + THUMBNAIL_FILEPATH, 'image/png');
        });
}

/**
 * Creates and uploads metadata information about the current scene.
 *
 * @params {Object} data - data passed to saveToMapzenUserAccount()
 * @params {string} sceneDir - slugified scene name to use as scene directory
 * @returns {Promise} - fulfilled with the response of the POST request.
 */
function makeAndUploadMetadata (data, sceneDir) {
    // Add some additional view information to the metadata.
    const metadata = Object.assign({}, data, {
        view: {
            // label: getLocationLabel(), // TODO: change now that search component is React
            label: '',
            lat: map.getCenter().lat,
            lng: map.getCenter().lng,
            zoom: map.getZoom()
        },
        versions: {
            tangram: window.Tangram.version,
            leaflet: L.version
        },
    });

    // Store metadata
    return uploadFile(JSON.stringify(metadata), sceneDir + METADATA_FILEPATH, 'application/json');
}

/**
 * Creates and uploads the current scene file. We assume one editor document
 * and one scene file.
 *
 * @params {Object} data - data passed to saveToMapzenUserAccount()
 * @params {string} sceneDir - slugified scene name to use as scene directory
 * @returns {Promise} - fulfilled with the response of the POST request.
 */
function makeAndUploadScene (data, sceneDir) {
    // This is a single YAML file for now
    const content = getEditorContent();

    // Store metadata
    return uploadFile(content, sceneDir + SCENE_FILENAME, 'application/x-yaml');
}

/**
 * Downloads the scene list (or creates a new one if not already present) and
 * appends saved scene data to it.
 *
 * @params {Object} data - data passed to saveToMapzenUserAccount()
 * @params {Array} savedLocations - array of saved files. This is created by
 *          Promise.all() and values should be in order:
 *          - savedLocations[0] - thumbnail.png
 *          - savedLocations[1] - metadata.json
 *          - savedLocations[2] - scene.yaml
 * @returns {Promise} - fulfilled with the object of scene data saved to
 *          `scenelist.json`
 */
function downloadAndUpdateSceneList (data, savedLocations) {
    const sceneData = Object.assign({}, data, {
        files: {
            thumbnail: savedLocations[0],
            metadata: savedLocations[1],
            scene: savedLocations[2]
        }
    });

    // The resolved value `sceneList` of `fetchSceneList` is current
    // scenelist.json contents or an empty array if it doesn't exist yet.
    return fetchSceneList()
        .then((sceneList) => {
            let foundExistingName = false;

            // If the scene exists already, overwrite its position in the list
            for (let i = 0; i < sceneList.length; i++) {
                if (sceneList[i].name === sceneData.name) {
                    sceneList[i] = sceneData;
                    foundExistingName = true;
                    break;
                }
            }
            // If not found, push to the end of array
            if (foundExistingName === false) {
                sceneList.push(sceneData);
            }

            return uploadFile(JSON.stringify(sceneList), SCENELIST_FILEPATH, 'application/json');
        })
        .then((sceneListUrl) => {
            return sceneData;
        });
}

/**
 * Fetches `scenelist.json` from the user account. Returns a Promise, resolved
 * with its contents in the form of an Array, or an empty Array if the file is
 * not found.
 *
 * @returns {Promise} - resolved with an array of saved scene data (or empty
 *          array if nothing is saved yet)
 */
export function fetchSceneList () {
    const fetchOpts = {
        credentials: 'same-origin'
    };

    // Checks the uploads object at `/api/uploads?app=play` to see if we have an
    // existing `scenelist.json`. This is so that we can make the check without
    // causing a 404 if it doesn't exist.
    return window.fetch('/api/uploads?app=play', fetchOpts)
        .then((response) => {
            return response.json();
        })
        .then((uploadsObj) => {
            return find(uploadsObj.uploads, (url) => {
                // TODO: Don't hardcode this URL!!
                return url.match(/https:\/\/mapzen-uploads.s3.amazonaws.com\/play\/[a-z0-9-]+\/scenelist.json/);
            });
        })
        .then((sceneListFile) => {
            // `sceneListFile` is a url
            if (sceneListFile) {
                return window.fetch(sceneListFile, fetchOpts)
                    .then((response) => {
                        return response.json();
                    });
            }
            // If not found, `sceneListFile` is null.
            // Create a new one of these
            else {
                return [];
            }
        });
}
<commit_msg>Save slug to S3 metadata<commit_after>import { getEditorContent } from '../editor/editor';
import { map } from '../map/map';
import { getScreenshotData } from '../map/screenshot';
// import { getLocationLabel } from '../map/search'; // TODO: implement now that move to react has changed this
import { createThumbnail } from '../tools/thumbnail';

import { find } from 'lodash';
import L from 'leaflet';

const METADATA_DIR = '.tangramplay/';
const METADATA_FILEPATH = METADATA_DIR + 'metadata.json';
const THUMBNAIL_WIDTH = 144;
const THUMBNAIL_HEIGHT = 81;
const THUMBNAIL_FILEPATH = METADATA_DIR + 'thumbnail.png';
const SCENELIST_FILEPATH = 'scenelist.json';

// We don't allow this to be customized (yet) and in the future you might
// have multiple scene files anyway, so we'll do that all at once when we figure
// it out.
const SCENE_FILENAME = 'scene.yaml';

/*
    Store files like this:
        ./scene-name-slug/scene.yaml
        ./scene-name-slug/[etc]
        ./scene-name-slug/.tangramplay/metadata.json
        ./scene-name-slug/.tangramplay/thumbnail.png

    Meta files are kept separate from scene related files but in a
    .tangramplay directory in the scene directory's root. This is to keep
    url schemes simple for the end user and namespace Play-related
    files. Think .git or similar.

    Scene files may eventually contain any quantity of files.
    (e.g. textures, multiple yamls, fonts, etc)

    Tangram Play only has two scene-related meta files now, a json
    for random strings and a thumbnail image.

    Eventually Tangram Play per-user settings may be stored in the root
*/

export function saveToMapzenUserAccount (data, successCallback, errorCallback) {
    // Add timestamp to `data`
    data.timestamp = new Date().toJSON();

    // Get a scene directory by slugifying the scene name, add a trailing slash.
    const slug = slugify(data.name) + '/';

    // These are Promises. Each creates its own contents and makes
    // individual upload requests. We resolve this function when all Promises
    // resolve.
    const uploadThumbnail = makeAndUploadThumbnail(slug);
    const uploadMetadata = makeAndUploadMetadata(data, slug);
    const uploadScene = makeAndUploadScene(data, slug);

    return Promise.all([uploadThumbnail, uploadMetadata, uploadScene])
        .then((savedLocations) => {
            // Should all be urls of the saved files.
            // Returns some an object containing metadata for the success handler
            // Errors should be handled upstream as well
            return downloadAndUpdateSceneList(data, savedLocations);
        });
}

// super quickly written slugify function. only replaces whitespace with dashes.
// does not deal with funky characters. or condense spaces, all that stuff.
// should also eventually enforce a maximum length. or handle errors.
function slugify (string) {
    return string.trim().toLowerCase().replace(/\s/g, '-');
}

/**
 * Upload a file using the Mapzen upload API.
 *
 * @param {*} contents - content of the file to upload. This will be converted
 *      to a Blob object by this function.
 * @param {string} filepath - file path and file name to save to, relative
 *      to the app data directory. e.g. 'scene-name/scene.yaml'
 * @param {string} type - MIME type of contents. Defaults to 'plain/text'.
 * @returns {Promise} - fulfilled with the response of the POST request.
 */
function uploadFile (contents, filepath, type = 'plain/text') {
    const APP_NAME = 'play';
    let uploadedFileUrl;

    return window.fetch(`/api/uploads/new?app=${APP_NAME}&path=${filepath}`, {
        credentials: 'same-origin'
    }).then((response) => {
        return response.json();
    }).then((upload) => {
        const formData = new FormData();
        Object.keys(upload.fields).forEach(field => {
            formData.append(field, upload.fields[field]);
        });

        const blob = new Blob([contents], { type: type });
        formData.append('file', blob, filepath);

        // Remember the location of the file we want to save.
        uploadedFileUrl = upload.url + upload.path;

        return window.fetch(upload.url, {
            method: 'POST',
            body: formData
        });
    }).then((response) => {
        // If uploaded, return the url of the uploaded file.
        if (response.ok) {
            return uploadedFileUrl;
        }
        else {
            // Caller should catch this error.
            // For now it just passes the status code.
            throw new Error(response.status);
        }
    });
}

/**
 * Creates and uploads a thumbnail image of the current map scene.
 *
 * @params {string} slug - slugified scene name to use as scene directory
 * @returns {Promise} - fulfilled with the response of the POST request.
 */
function makeAndUploadThumbnail (slug) {
    // Grab a screenshot from the map and convert it to a thumbnail at a fixed
    // dimension. This makes file sizes and layout more predictable.
    return getScreenshotData()
        .then((screenshot) => {
            // At this size, thumbnail image should clock in at around ~90kb
            // to ~120kb (unoptimized, but that's the limitations of our
            // thumbnail function)
            return createThumbnail(screenshot.blob, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);
        })
        .then((thumbnail) => {
            // Make upload request
            return uploadFile(thumbnail, slug + THUMBNAIL_FILEPATH, 'image/png');
        });
}

/**
 * Creates and uploads metadata information about the current scene.
 *
 * @params {Object} data - data passed to saveToMapzenUserAccount()
 * @params {string} slug - slugified scene name to use as scene directory
 * @returns {Promise} - fulfilled with the response of the POST request.
 */
function makeAndUploadMetadata (data, slug) {
    // Add some additional view information to the metadata.
    const metadata = Object.assign({}, data, {
        slug: slug,
        view: {
            // label: getLocationLabel(), // TODO: change now that search component is React
            label: '',
            lat: map.getCenter().lat,
            lng: map.getCenter().lng,
            zoom: map.getZoom()
        },
        versions: {
            tangram: window.Tangram.version,
            leaflet: L.version
        },
    });

    // Store metadata
    return uploadFile(JSON.stringify(metadata), slug + METADATA_FILEPATH, 'application/json');
}

/**
 * Creates and uploads the current scene file. We assume one editor document
 * and one scene file.
 *
 * @params {Object} data - data passed to saveToMapzenUserAccount()
 * @params {string} slug - slugified scene name to use as scene directory
 * @returns {Promise} - fulfilled with the response of the POST request.
 */
function makeAndUploadScene (data, slug) {
    // This is a single YAML file for now
    const content = getEditorContent();

    // Store metadata
    return uploadFile(content, slug + SCENE_FILENAME, 'application/x-yaml');
}

/**
 * Downloads the scene list (or creates a new one if not already present) and
 * appends saved scene data to it.
 *
 * @params {Object} data - data passed to saveToMapzenUserAccount()
 * @params {Array} savedLocations - array of saved files. This is created by
 *          Promise.all() and values should be in order:
 *          - savedLocations[0] - thumbnail.png
 *          - savedLocations[1] - metadata.json
 *          - savedLocations[2] - scene.yaml
 * @returns {Promise} - fulfilled with the object of scene data saved to
 *          `scenelist.json`
 */
function downloadAndUpdateSceneList (data, savedLocations) {
    const sceneData = Object.assign({}, data, {
        files: {
            thumbnail: savedLocations[0],
            metadata: savedLocations[1],
            scene: savedLocations[2]
        }
    });

    // The resolved value `sceneList` of `fetchSceneList` is current
    // scenelist.json contents or an empty array if it doesn't exist yet.
    return fetchSceneList()
        .then((sceneList) => {
            let foundExistingName = false;

            // If the scene exists already, overwrite its position in the list
            for (let i = 0; i < sceneList.length; i++) {
                if (sceneList[i].name === sceneData.name) {
                    sceneList[i] = sceneData;
                    foundExistingName = true;
                    break;
                }
            }
            // If not found, push to the end of array
            if (foundExistingName === false) {
                sceneList.push(sceneData);
            }

            return uploadFile(JSON.stringify(sceneList), SCENELIST_FILEPATH, 'application/json');
        })
        .then((sceneListUrl) => {
            return sceneData;
        });
}

/**
 * Fetches `scenelist.json` from the user account. Returns a Promise, resolved
 * with its contents in the form of an Array, or an empty Array if the file is
 * not found.
 *
 * @returns {Promise} - resolved with an array of saved scene data (or empty
 *          array if nothing is saved yet)
 */
export function fetchSceneList () {
    const fetchOpts = {
        credentials: 'same-origin'
    };

    // Checks the uploads object at `/api/uploads?app=play` to see if we have an
    // existing `scenelist.json`. This is so that we can make the check without
    // causing a 404 if it doesn't exist.
    return window.fetch('/api/uploads?app=play', fetchOpts)
        .then((response) => {
            return response.json();
        })
        .then((uploadsObj) => {
            return find(uploadsObj.uploads, (url) => {
                // TODO: Don't hardcode this URL!!
                return url.match(/https:\/\/mapzen-uploads.s3.amazonaws.com\/play\/[a-z0-9-]+\/scenelist.json/);
            });
        })
        .then((sceneListFile) => {
            // `sceneListFile` is a url
            if (sceneListFile) {
                return window.fetch(sceneListFile, fetchOpts)
                    .then((response) => {
                        return response.json();
                    });
            }
            // If not found, `sceneListFile` is null.
            // Create a new one of these
            else {
                return [];
            }
        });
}
<|endoftext|>


<commit_before>/* global chrome */

'use strict';

const ports = {};

const IS_FIREFOX = navigator.userAgent.indexOf('Firefox') >= 0;

chrome.runtime.onConnect.addListener(function(port) {
  let tab = null;
  let name = null;
  if (isNumeric(port.name)) {
    tab = port.name;
    name = 'devtools';
    installContentScript(+port.name);
  } else {
    tab = port.sender.tab.id;
    name = 'content-script';
  }

  if (!ports[tab]) {
    ports[tab] = {
      devtools: null,
      'content-script': null,
    };
  }
  ports[tab][name] = port;

  if (ports[tab].devtools && ports[tab]['content-script']) {
    doublePipe(ports[tab].devtools, ports[tab]['content-script']);
  }
});

function isNumeric(str: string): boolean {
  return +str + '' === str;
}

function installContentScript(tabId: number) {
  chrome.tabs.executeScript(
    tabId,
    {file: '/build/contentScript.js'},
    function() {},
  );
}

function doublePipe(one, two) {
  one.onMessage.addListener(lOne);
  function lOne(message) {
    two.postMessage(message);
  }
  two.onMessage.addListener(lTwo);
  function lTwo(message) {
    one.postMessage(message);
  }
  function shutdown() {
    one.onMessage.removeListener(lOne);
    two.onMessage.removeListener(lTwo);
    one.disconnect();
    two.disconnect();
  }
  one.onDisconnect.addListener(shutdown);
  two.onDisconnect.addListener(shutdown);
}

function setIconAndPopup(reactBuildType, tabId) {
  chrome.browserAction.setIcon({
    tabId: tabId,
    path: {
      '16': 'icons/16-' + reactBuildType + '.png',
      '32': 'icons/32-' + reactBuildType + '.png',
      '48': 'icons/48-' + reactBuildType + '.png',
      '128': 'icons/128-' + reactBuildType + '.png',
    },
  });
  chrome.browserAction.setPopup({
    tabId: tabId,
    popup: 'popups/' + reactBuildType + '.html',
  });
}

// Listen to URL changes on the active tab and reset the DevTools icon.
// This prevents non-disabled icons from sticking in Firefox.
// Don't listen to this event in Chrome though.
// It fires more frequently, often after onMessage() has been called.
if (IS_FIREFOX) {
  chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
    if (tab.active && changeInfo.status === 'loading') {
      setIconAndPopup('disabled', tabId);
    }
  });
}

chrome.runtime.onMessage.addListener((request, sender) => {
  if (sender.tab) {
    // This is sent from the hook content script.
    // It tells us a renderer has attached.
    if (request.hasDetectedReact) {
      // We use browserAction instead of pageAction because this lets us
      // display a custom default popup when React is *not* detected.
      // It is specified in the manifest.
      let reactBuildType = request.reactBuildType;
      if (sender.url.indexOf('facebook.github.io/react') !== -1) {
        // Cheat: We use the development version on the website because
        // it is better for interactive examples. However we're going
        // to get misguided bug reports if the extension highlights it
        // as using the dev version. We're just going to special case
        // our own documentation and cheat. It is acceptable to use dev
        // version of React in React docs, but not in any other case.
        reactBuildType = 'production';
      }

      setIconAndPopup(reactBuildType, sender.tab.id);
    }
  }
});
<commit_msg>DevTools: Remove ReactJS.org version check "cheat" (#19939)<commit_after>/* global chrome */

'use strict';

const ports = {};

const IS_FIREFOX = navigator.userAgent.indexOf('Firefox') >= 0;

chrome.runtime.onConnect.addListener(function(port) {
  let tab = null;
  let name = null;
  if (isNumeric(port.name)) {
    tab = port.name;
    name = 'devtools';
    installContentScript(+port.name);
  } else {
    tab = port.sender.tab.id;
    name = 'content-script';
  }

  if (!ports[tab]) {
    ports[tab] = {
      devtools: null,
      'content-script': null,
    };
  }
  ports[tab][name] = port;

  if (ports[tab].devtools && ports[tab]['content-script']) {
    doublePipe(ports[tab].devtools, ports[tab]['content-script']);
  }
});

function isNumeric(str: string): boolean {
  return +str + '' === str;
}

function installContentScript(tabId: number) {
  chrome.tabs.executeScript(
    tabId,
    {file: '/build/contentScript.js'},
    function() {},
  );
}

function doublePipe(one, two) {
  one.onMessage.addListener(lOne);
  function lOne(message) {
    two.postMessage(message);
  }
  two.onMessage.addListener(lTwo);
  function lTwo(message) {
    one.postMessage(message);
  }
  function shutdown() {
    one.onMessage.removeListener(lOne);
    two.onMessage.removeListener(lTwo);
    one.disconnect();
    two.disconnect();
  }
  one.onDisconnect.addListener(shutdown);
  two.onDisconnect.addListener(shutdown);
}

function setIconAndPopup(reactBuildType, tabId) {
  chrome.browserAction.setIcon({
    tabId: tabId,
    path: {
      '16': 'icons/16-' + reactBuildType + '.png',
      '32': 'icons/32-' + reactBuildType + '.png',
      '48': 'icons/48-' + reactBuildType + '.png',
      '128': 'icons/128-' + reactBuildType + '.png',
    },
  });
  chrome.browserAction.setPopup({
    tabId: tabId,
    popup: 'popups/' + reactBuildType + '.html',
  });
}

// Listen to URL changes on the active tab and reset the DevTools icon.
// This prevents non-disabled icons from sticking in Firefox.
// Don't listen to this event in Chrome though.
// It fires more frequently, often after onMessage() has been called.
if (IS_FIREFOX) {
  chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
    if (tab.active && changeInfo.status === 'loading') {
      setIconAndPopup('disabled', tabId);
    }
  });
}

chrome.runtime.onMessage.addListener((request, sender) => {
  if (sender.tab) {
    // This is sent from the hook content script.
    // It tells us a renderer has attached.
    if (request.hasDetectedReact) {
      // We use browserAction instead of pageAction because this lets us
      // display a custom default popup when React is *not* detected.
      // It is specified in the manifest.
      setIconAndPopup(request.reactBuildType, sender.tab.id);
    }
  }
});
<|endoftext|>