<commit_before>/**
 * Declares instance variables.
 *
 * @constructor
 *
 * @class A class for helping insert an anchor. Contains code
 * common to both the button and the menu item.
 */
UI.Anchor_Helper = function()
{
	var self = this;
	Util.OOP.inherits(self, UI.Helper);

	this.init = function(loki)
	{
		this._loki = loki;
		this._anchor_masseuse = (new UI.Anchor_Masseuse()).init(this._loki);
		return this;
	};

	this.is_selected = function()
	{
		return this.get_selected_item() != null;
	};

	var _get_selected_anchor = function()
	{
		var sel = Util.Selection.get_selection(self._loki.window);
		var rng = Util.Range.create_range(sel);
		//var surrounding_node = Util.Range.get_common_ancestor(rng);

		var selected_anchor;
		// IE
		var selected_image = Util.Range.get_nearest_ancestor_element_by_tag_name(rng, 'IMG');
		if ( selected_image != null )
		{
			selected_anchor = self._anchor_masseuse.get_real_elem(selected_image);
		}
		// Gecko
		if ( selected_anchor == null )
		{
			var selected_a = Util.Range.get_nearest_ancestor_element_by_tag_name(rng, 'A');
			if ( selected_a != null )
			{
				selected_anchor = self._anchor_masseuse.get_real_elem(selected_a);
			}
		}

		return selected_anchor;
	};

	this.get_selected_item = function()
	{
		var selected_anchor = _get_selected_anchor();
		var selected_item;
		if ( selected_anchor != null )
			selected_item = { name : selected_anchor.getAttribute('name') };

		return selected_item;
	};

	this.open_dialog = function()
	{
		var selected_item = self.get_selected_item();

		if ( this._dialog == null )
			this._dialog = new UI.Anchor_Dialog;
		this._dialog.init({ base_uri : self._loki.settings.base_uri,
							submit_listener : self.insert_anchor,
							remove_listener : self.remove_anchor,
							selected_item : selected_item });
		this._dialog.open();
	};

	this.insert_anchor = function(anchor_info)
	{
		// Create the anchor
		var anchor = self._loki.document.createElement('A');
		anchor.name = anchor_info.name;

		// Create the dummy
		var dummy = self._anchor_masseuse.get_fake_elem(anchor);

		// Insert the dummy
		var sel = Util.Selection.get_selection(self._loki.window);
		Util.Selection.collapse(sel, true); // to beg
		Util.Selection.paste_node(sel, dummy);

		self._loki.window.focus();
	};

	this.remove_anchor = function()
	{
		var sel = Util.Selection.get_selection(self._loki.window);
		var rng = Util.Range.create_range(sel);
		var fake_anchor = Util.Range.get_nearest_ancestor_element_by_tag_name(rng, 'IMG');

		// Move cursor
		Util.Selection.select_node(sel, fake_anchor);
		Util.Selection.collapse(sel, false); // to end
		self._loki.window.focus();

		if ( fake_anchor.parentNode != null )
			fake_anchor.parentNode.removeChild(fake_anchor);
	};
};
<commit_msg>Fixed issue #13: anchors could not be directly edited. An attempt to edit an anchor would instead create a new anchor and the old anchor would have to be manually deleted.<commit_after>/**
 * Declares instance variables.
 *
 * @constructor
 *
 * @class A class for helping insert an anchor. Contains code
 * common to both the button and the menu item.
 */
UI.Anchor_Helper = function()
{
	var self = this;
	Util.OOP.inherits(self, UI.Helper);

	this.init = function(loki)
	{
		this._loki = loki;
		this._anchor_masseuse = (new UI.Anchor_Masseuse()).init(this._loki);
		return this;
	};

	this.is_selected = function()
	{
		return this.get_selected_item() != null;
	};

	function _get_selected_placeholder()
	{
		var sel = Util.Selection.get_selection(self._loki.window);
		var rng = Util.Range.create_range(sel);

		return Util.Range.get_nearest_ancestor_element_by_tag_name(rng, 'IMG');
	}

	var _get_selected_anchor = function()
	{
		var placeholder = _get_selected_placeholder();
		return (placeholder)
			? self._anchor_masseuse.get_real_elem(placeholder)
			: null;
	};

	this.get_selected_item = function()
	{
		var selected_anchor = _get_selected_anchor();
		var selected_item;
		if ( selected_anchor != null )
			selected_item = { name : selected_anchor.getAttribute('name') };

		return selected_item;
	};

	this.open_dialog = function()
	{
		var selected_item = self.get_selected_item();

		if ( this._dialog == null )
			this._dialog = new UI.Anchor_Dialog;
		this._dialog.init({ base_uri : self._loki.settings.base_uri,
							submit_listener : self.insert_anchor,
							remove_listener : self.remove_anchor,
							selected_item : selected_item });
		this._dialog.open();
	};

	this.insert_anchor = function(anchor_info)
	{
		var selected = _get_selected_placeholder();
		if (selected) {
			// Edit an existing anchor.
			// XXX: This code probably shouldn't be here, but it does fix
			//      issue #13.
			selected.setAttribute('loki:anchor_name', anchor_info.name);
			selected.title = anchor_info.name;
		} else {
			// Create the anchor
			var anchor = self._loki.document.createElement('A');
			anchor.name = anchor_info.name;

			// Create the dummy
			var dummy = self._anchor_masseuse.get_fake_elem(anchor);

			// Insert the dummy
			var sel = Util.Selection.get_selection(self._loki.window);
			Util.Selection.collapse(sel, true); // to beg
			Util.Selection.paste_node(sel, dummy);
		}

		self._loki.window.focus();
	};

	this.remove_anchor = function()
	{
		var sel = Util.Selection.get_selection(self._loki.window);
		var rng = Util.Range.create_range(sel);
		var fake_anchor = Util.Range.get_nearest_ancestor_element_by_tag_name(rng, 'IMG');

		// Move cursor
		Util.Selection.select_node(sel, fake_anchor);
		Util.Selection.collapse(sel, false); // to end
		self._loki.window.focus();

		if ( fake_anchor.parentNode != null )
			fake_anchor.parentNode.removeChild(fake_anchor);
	};
};
<|endoftext|>


<commit_before>/**
 * @class Provides a nicely-formatted inline error display.
 * @constructor
 * @param {HTMLElement} the element into which the message will be inserted
 */
UI.Error_Display = function(message_container)
{
	var doc = message_container.ownerDocument;
	var dh = new Util.Document(doc);

	var self = this;

	this.display = null;

	function create(message, retry)
	{
		if (!retry)
			var retry = null;

		var children = [message];
		if (retry) {
			var link = dh.create_element('a',
				{
					href: '#',
					className: 'retry',
					style: {display: 'block'}
				},
				['Retry']);

			Util.Event.add_event_listener(link, 'click', function(e) {
				if (!e)
					var e = window.event;

				try {
					retry();
				} catch (e) {
					self.show('Failed to retry: ' + (e.message || e), retry);
				} finally {
					return Util.Event.prevent_default(e);
				}
			});
			children.push(link);
		}

		self.display = dh.create_element('p', {className: 'error'}, children);
		message_container.appendChild(self.display);
	}

	function remove()
	{
		if (this.display.parentNode)
			this.display.parentNode.removeChild(this.display);
		this.display = null;
	}

	this.show = function(message, retry)
	{
		if (!retry)
			var retry = null;

		if (this.display)
			remove.call(this);

		create.call(this, message, retry);
	}

	this.clear = function()
	{
		if (this.display)
			remove.call(this);
	}
}<commit_msg>Allowing the "Retry" text in an error display to be customized, and allowing HTML in the error message.<commit_after>/**
 * @class Provides a nicely-formatted inline error display.
 * @constructor
 * @param {HTMLElement} the element into which the message will be inserted
 */
UI.Error_Display = function(message_container)
{
	var doc = message_container.ownerDocument;
	var dh = new Util.Document(doc);

	var self = this;

	this.display = null;

	function create(message, retry, retry_text)
	{
		if (!retry)
		    retry = null;
		else if (!retry_text)
		    retry_text = 'Retry';

		var link;
		if (retry) {
			link = dh.create_element('a',
				{
					href: '#',
					className: 'retry',
					style: {display: 'block'}
				},
				[retry_text]);

			Util.Event.add_event_listener(link, 'click', function(e) {
				if (!e)
					var e = window.event;

				try {
					retry();
				} catch (e) {
					self.show('Failed to retry: ' + (e.message || e), retry);
				} finally {
					return Util.Event.prevent_default(e);
				}
			});
		}

		self.display = dh.create_element('p', {className: 'error'});
		self.display.innerHTML = message;
		if (link)
		    self.display.appendChild(link);
		message_container.appendChild(self.display);
	}

	function remove()
	{
		if (this.display.parentNode)
			this.display.parentNode.removeChild(this.display);
		this.display = null;
	}

	this.show = function(message, retry, retry_text)
	{
		if (!retry)
			var retry = null;

		if (this.display)
			remove.call(this);

		create.call(this, message, retry, retry_text);
	}

	this.clear = function()
	{
		if (this.display)
			remove.call(this);
	}
}<|endoftext|>


<commit_before>'use strict';

var main = require('../../main');

var allHeuristics = require('./AllHeuristics');
var BoardAnalyser = require('./boan/BoardAnalyser');
var CONST = require('../../constants');
var Genes = require('../../Genes');
var Grid = require('../../Grid');
var ZoneFiller = require('./boan/ZoneFiller');

var GRID_BORDER = CONST.GRID_BORDER;
var sOK = CONST.sOK, sINVALID = CONST.sINVALID, sBLUNDER = CONST.sBLUNDER, sDEBUG = CONST.sDEBUG;

var NO_MOVE = -1; // used for i coordinate of "not yet known" best moves

var AI_VERSION = '0.1';

/** @class */
function Chuckie(game, color, genes) {
    this.name = this.constructor.name || main.funcName(this.constructor);
    this.publicName = this.name; // could be different if needed
    this.publicVersion = AI_VERSION;

    this.game = game;
    this.goban = game.goban;
    this.genes = genes || new Genes();
    this.gsize = this.goban.gsize;
    this.stateGrid = new Grid(this.gsize, GRID_BORDER);
    this.scoreGrid = new Grid(this.gsize, 0, GRID_BORDER);

    // genes need to exist before we create heuristics
    this.minimumScore = this.getGene('smallerMove', 0.03, 0.01, 0.1);

    this._createHeuristics();
    this.setColor(color);
    this.prepareGame();
}
module.exports = Chuckie;

// Used only by tests
Chuckie.BoardAnalyser = BoardAnalyser;
Chuckie.ZoneFiller = ZoneFiller;

Chuckie.prototype._createHeuristics = function () {
    this.heuristics = [];
    var heuristics = allHeuristics();
    for (var n = 0; n < heuristics.length; n++) {
        var h = new (heuristics[n])(this);
        this.heuristics.push(h);
    }
};

Chuckie.prototype.setColor = function (color) {
    this.color = color;
    for (var n = 0; n < this.heuristics.length; n++) {
        this.heuristics[n].initColor(color);
    }
};

/** Can be called from Breeder with different genes */
Chuckie.prototype.prepareGame = function (genes) {
    if (genes) this.genes = genes;
    this.numMoves = this.numRandomPicks = 0;

    this.bestScore = this.bestI = this.bestJ = 0;
    this.usedRandom = false;
    this.testI = this.testJ = NO_MOVE;
    this.debugHeuristic = null;
};

Chuckie.prototype.getHeuristic = function (heuristicName) {
    for (var n = 0; n < this.heuristics.length; n++) {
        var h = this.heuristics[n];
        if (h.name === heuristicName) return h;
    }
    return null;
};

Chuckie.prototype.getGene = function (geneName, defVal, lowLimit, highLimit) {
    if (lowLimit === undefined) lowLimit = null;
    if (highLimit === undefined) highLimit = null;
    return this.genes.get(this.name + '-' + geneName, defVal, lowLimit, highLimit);
};

Chuckie.prototype._foundBestMove = function(i, j, score) {
    this.bestScore = score;
    this.bestI = i; this.bestJ = j;
    this.usedRandom = this.numBestTwins !== 1;
};

Chuckie.prototype._keepBestMove = function(i, j, score) {
    if (score > this.bestScore) {
        this.numBestTwins = 1; // number of moves with same best score (we randomly pick one of them)
        this._foundBestMove(i, j, score);
    } else { // score === this.bestScore
        this.numBestTwins++;
        if (Math.random() * this.numBestTwins >= 1) return; // keep current twin if it does not win
        this._foundBestMove(i, j, score);
    }
};

// Returns the move chosen (e.g. c4 or pass)
Chuckie.prototype.getMove = function () {
    this.numMoves++;
    var stateYx = this.stateGrid.yx;
    var scoreYx = this.scoreGrid.yx;

    this._prepareEval();
    this._initScoringGrid(stateYx, scoreYx);
    this._runHeuristics(stateYx, scoreYx);
    var move = this._collectBestMove(stateYx, scoreYx);

    main.debug = this.debugMode;
    return move;
};

Chuckie.prototype._prepareEval = function () {
    this.bestScore = this.minimumScore - 0.001;
    this.bestI = NO_MOVE;

    this.debugMode = main.debug;
    main.debug = false;
};

/** Init grids (and mark invalid moves) */
Chuckie.prototype._initScoringGrid = function (stateYx, scoreYx) {
    for (var j = 1; j <= this.gsize; j++) {
        for (var i = 1; i <= this.gsize; i++) {
            if (!this.goban.isValidMove(i, j, this.color)) {
                stateYx[j][i] = sINVALID;
                continue;
            }
            stateYx[j][i] = sOK;
            scoreYx[j][i] = 0;
        }
    }
    if (this.testI !== NO_MOVE) {
        stateYx[this.testJ][this.testI] = sDEBUG;
    }
};

/** Do eval using each heuristic (NB: order is important) */
Chuckie.prototype._runHeuristics = function (stateYx, scoreYx) {
    for (var n = 0; n < this.heuristics.length; n++) {
        var h = this.heuristics[n];
        main.debug = this.debugMode && this.debugHeuristic === h.name;
        var t0 = Date.now();

        if (h._beforeEvalBoard) h._beforeEvalBoard();
        h.evalBoard(stateYx, scoreYx);

        var time = Date.now() - t0;
        if (time > 1 && !main.isCoverTest) {
            main.log.warn('Slowness: ' + h.name + ' took ' + time + 'ms');
        }
    }
};

/** Returns the move which got the best score */
Chuckie.prototype._collectBestMove = function (stateYx, scoreYx) {
    for (var j = 1; j <= this.gsize; j++) {
        for (var i = 1; i <= this.gsize; i++) {
            if (stateYx[j][i] < sOK || scoreYx[j][i] < this.bestScore) continue;
            this._keepBestMove(i, j, scoreYx[j][i]);
        }
    }
    if (this.bestScore < this.minimumScore) return 'pass';
    if (this.usedRandom) this.numRandomPicks++;
    return Grid.xy2move(this.bestI, this.bestJ);
};

/** Called by heuristics if they decide to stop looking further (rare cases) */
Chuckie.prototype.markMoveAsBlunder = function (i, j, reason) {
    this.stateGrid.yx[j][i] = sBLUNDER;
    main.log.debug(Grid.xy2move(i, j) + ' seen as blunder: ' + reason);
};
Chuckie.prototype.isBlunderMove = function (i, j) {
    return this.stateGrid.yx[j][i] === sBLUNDER;
};

Chuckie.prototype.guessTerritories = function () {
    this.pot.evalBoard();
    return this.pot.territory.yx;
};

Chuckie.prototype._getMoveForTest = function (i, j) {
    this.testI = i;
    this.testJ = j;

    this.getMove();

    this.testI = NO_MOVE;
};

Chuckie.prototype._getMoveSurvey = function (i, j) {
    var survey = {};
    for (var n = 0; n < this.heuristics.length; n++) {
        var h = this.heuristics[n];
        var s = h.scoreGrid.yx[j][i];
        if (s) survey[h.name] = s;
    }
    return survey;
};

/** For tests */
Chuckie.prototype.testMoveEval = function (i, j) {
    this._getMoveForTest(i, j);
    return this.scoreGrid.yx[j][i];
};

/** For tests */
Chuckie.prototype.testHeuristic = function (i, j, heuristicName) {
    this._getMoveForTest(i, j);
    var h = this.getHeuristic(heuristicName);
    return h.scoreGrid.yx[j][i];
};

/** For tests */
Chuckie.prototype.setDebugHeuristic = function (heuristicName) {
    this.debugHeuristic = heuristicName;
};

function surveySort(h1, h2) { return h2[1] - h1[1]; }

Chuckie.prototype.getMoveSurveyText = function (move, isTest) {
    var coords = this.game.oneMove2xy(move);
    if (!coords) return '';
    var i = coords[0], j = coords[1];
    if (isTest) this._getMoveForTest(i, j);
    var survey = this._getMoveSurvey(i, j);
    var score = this.scoreGrid.yx[j][i];

    var sortedSurvey = [];
    for (var h in survey) {
        if (survey[h] === 0) continue;
        sortedSurvey.push([h, survey[h]]);
    }
    sortedSurvey.sort(surveySort);

    var txt = move + ' (' + score.toFixed(2) + ')\n';
    for (var n = 0; n < sortedSurvey.length; n++) {
        txt += '- ' + sortedSurvey[n][0] + ': ' + sortedSurvey[n][1].toFixed(2) + '\n';
    }
    return txt;
};

<commit_msg>Minor: catch invalid test moves<commit_after>'use strict';

var main = require('../../main');

var allHeuristics = require('./AllHeuristics');
var BoardAnalyser = require('./boan/BoardAnalyser');
var CONST = require('../../constants');
var Genes = require('../../Genes');
var Grid = require('../../Grid');
var ZoneFiller = require('./boan/ZoneFiller');

var GRID_BORDER = CONST.GRID_BORDER;
var sOK = CONST.sOK, sINVALID = CONST.sINVALID, sBLUNDER = CONST.sBLUNDER, sDEBUG = CONST.sDEBUG;

var NO_MOVE = -1; // used for i coordinate of "not yet known" best moves

var AI_VERSION = '0.1';

/** @class */
function Chuckie(game, color, genes) {
    this.name = this.constructor.name || main.funcName(this.constructor);
    this.publicName = this.name; // could be different if needed
    this.publicVersion = AI_VERSION;

    this.game = game;
    this.goban = game.goban;
    this.genes = genes || new Genes();
    this.gsize = this.goban.gsize;
    this.stateGrid = new Grid(this.gsize, GRID_BORDER);
    this.scoreGrid = new Grid(this.gsize, 0, GRID_BORDER);

    // genes need to exist before we create heuristics
    this.minimumScore = this.getGene('smallerMove', 0.03, 0.01, 0.1);

    this._createHeuristics();
    this.setColor(color);
    this.prepareGame();
}
module.exports = Chuckie;

// Used only by tests
Chuckie.BoardAnalyser = BoardAnalyser;
Chuckie.ZoneFiller = ZoneFiller;

Chuckie.prototype._createHeuristics = function () {
    this.heuristics = [];
    var heuristics = allHeuristics();
    for (var n = 0; n < heuristics.length; n++) {
        var h = new (heuristics[n])(this);
        this.heuristics.push(h);
    }
};

Chuckie.prototype.setColor = function (color) {
    this.color = color;
    for (var n = 0; n < this.heuristics.length; n++) {
        this.heuristics[n].initColor(color);
    }
};

/** Can be called from Breeder with different genes */
Chuckie.prototype.prepareGame = function (genes) {
    if (genes) this.genes = genes;
    this.numMoves = this.numRandomPicks = 0;

    this.bestScore = this.bestI = this.bestJ = 0;
    this.usedRandom = false;
    this.testI = this.testJ = NO_MOVE;
    this.debugHeuristic = null;
};

Chuckie.prototype.getHeuristic = function (heuristicName) {
    for (var n = 0; n < this.heuristics.length; n++) {
        var h = this.heuristics[n];
        if (h.name === heuristicName) return h;
    }
    return null;
};

Chuckie.prototype.getGene = function (geneName, defVal, lowLimit, highLimit) {
    if (lowLimit === undefined) lowLimit = null;
    if (highLimit === undefined) highLimit = null;
    return this.genes.get(this.name + '-' + geneName, defVal, lowLimit, highLimit);
};

Chuckie.prototype._foundBestMove = function(i, j, score) {
    this.bestScore = score;
    this.bestI = i; this.bestJ = j;
    this.usedRandom = this.numBestTwins !== 1;
};

Chuckie.prototype._keepBestMove = function(i, j, score) {
    if (score > this.bestScore) {
        this.numBestTwins = 1; // number of moves with same best score (we randomly pick one of them)
        this._foundBestMove(i, j, score);
    } else { // score === this.bestScore
        this.numBestTwins++;
        if (Math.random() * this.numBestTwins >= 1) return; // keep current twin if it does not win
        this._foundBestMove(i, j, score);
    }
};

// Returns the move chosen (e.g. c4 or pass)
Chuckie.prototype.getMove = function () {
    this.numMoves++;
    var stateYx = this.stateGrid.yx;
    var scoreYx = this.scoreGrid.yx;

    this._prepareEval();
    this._initScoringGrid(stateYx, scoreYx);
    this._runHeuristics(stateYx, scoreYx);
    var move = this._collectBestMove(stateYx, scoreYx);

    main.debug = this.debugMode;
    return move;
};

Chuckie.prototype._prepareEval = function () {
    this.bestScore = this.minimumScore - 0.001;
    this.bestI = NO_MOVE;

    this.debugMode = main.debug;
    main.debug = false;
};

/** Init grids (and mark invalid moves) */
Chuckie.prototype._initScoringGrid = function (stateYx, scoreYx) {
    for (var j = 1; j <= this.gsize; j++) {
        for (var i = 1; i <= this.gsize; i++) {
            if (!this.goban.isValidMove(i, j, this.color)) {
                stateYx[j][i] = sINVALID;
                continue;
            }
            stateYx[j][i] = sOK;
            scoreYx[j][i] = 0;
        }
    }
    if (this.testI !== NO_MOVE) {
        if (stateYx[this.testJ][this.testI] === sINVALID)
            throw new Error('Invalid test move: ' + this.testI + ',' + this.testJ);
        stateYx[this.testJ][this.testI] = sDEBUG;
    }
};

/** Do eval using each heuristic (NB: order is important) */
Chuckie.prototype._runHeuristics = function (stateYx, scoreYx) {
    for (var n = 0; n < this.heuristics.length; n++) {
        var h = this.heuristics[n];
        main.debug = this.debugMode && this.debugHeuristic === h.name;
        var t0 = Date.now();

        if (h._beforeEvalBoard) h._beforeEvalBoard();
        h.evalBoard(stateYx, scoreYx);

        var time = Date.now() - t0;
        if (time > 1 && !main.isCoverTest) {
            main.log.warn('Slowness: ' + h.name + ' took ' + time + 'ms');
        }
    }
};

/** Returns the move which got the best score */
Chuckie.prototype._collectBestMove = function (stateYx, scoreYx) {
    for (var j = 1; j <= this.gsize; j++) {
        for (var i = 1; i <= this.gsize; i++) {
            if (stateYx[j][i] < sOK || scoreYx[j][i] < this.bestScore) continue;
            this._keepBestMove(i, j, scoreYx[j][i]);
        }
    }
    if (this.bestScore < this.minimumScore) return 'pass';
    if (this.usedRandom) this.numRandomPicks++;
    return Grid.xy2move(this.bestI, this.bestJ);
};

/** Called by heuristics if they decide to stop looking further (rare cases) */
Chuckie.prototype.markMoveAsBlunder = function (i, j, reason) {
    this.stateGrid.yx[j][i] = sBLUNDER;
    main.log.debug(Grid.xy2move(i, j) + ' seen as blunder: ' + reason);
};
Chuckie.prototype.isBlunderMove = function (i, j) {
    return this.stateGrid.yx[j][i] === sBLUNDER;
};

Chuckie.prototype.guessTerritories = function () {
    this.pot.evalBoard();
    return this.pot.territory.yx;
};

Chuckie.prototype._getMoveForTest = function (i, j) {
    this.testI = i;
    this.testJ = j;

    this.getMove();

    this.testI = NO_MOVE;
};

Chuckie.prototype._getMoveSurvey = function (i, j) {
    var survey = {};
    for (var n = 0; n < this.heuristics.length; n++) {
        var h = this.heuristics[n];
        var s = h.scoreGrid.yx[j][i];
        if (s) survey[h.name] = s;
    }
    return survey;
};

/** For tests */
Chuckie.prototype.testMoveEval = function (i, j) {
    this._getMoveForTest(i, j);
    return this.scoreGrid.yx[j][i];
};

/** For tests */
Chuckie.prototype.testHeuristic = function (i, j, heuristicName) {
    this._getMoveForTest(i, j);
    var h = this.getHeuristic(heuristicName);
    return h.scoreGrid.yx[j][i];
};

/** For tests */
Chuckie.prototype.setDebugHeuristic = function (heuristicName) {
    this.debugHeuristic = heuristicName;
};

function surveySort(h1, h2) { return h2[1] - h1[1]; }

Chuckie.prototype.getMoveSurveyText = function (move, isTest) {
    var coords = this.game.oneMove2xy(move);
    if (!coords) return '';
    var i = coords[0], j = coords[1];
    if (isTest) this._getMoveForTest(i, j);
    var survey = this._getMoveSurvey(i, j);
    var score = this.scoreGrid.yx[j][i];

    var sortedSurvey = [];
    for (var h in survey) {
        if (survey[h] === 0) continue;
        sortedSurvey.push([h, survey[h]]);
    }
    sortedSurvey.sort(surveySort);

    var txt = move + ' (' + score.toFixed(2) + ')\n';
    for (var n = 0; n < sortedSurvey.length; n++) {
        txt += '- ' + sortedSurvey[n][0] + ': ' + sortedSurvey[n][1].toFixed(2) + '\n';
    }
    return txt;
};

<|endoftext|>


<commit_before>define(function()
{
    "use strict";
    function BodyUI(body, isBump, isSpecular, callback)
    {
        InputValidator.validateNotNull("body", body);
        InputValidator.validateNotNull("isBump", isBump);
        InputValidator.validateNotNull("isSpecular", isSpecular);
        InputValidator.validateNotNull("callback", callback);

        // Start loading textures.
        var map, bumpMap, specularMap, ringMap;
        var loader = new THREE.TextureLoader();
        loader.crossOrigin = "anonymous";
        var name = body.name.toLowerCase();
        loader.load(imageBase + name + "map.jpg", onLoadMap);

        if (isBump)
        {
            loader.load(imageBase + name + "bump.jpg", onLoadBump);
        }

        if (isSpecular)
        {
            loader.load(imageBase + name + "spec.jpg", onLoadSpecular);
        }

        var isRing = body.ringInnerRadius && body.ringOuterRadius;

        if (isRing)
        {
            loader.load(imageBase + name + "ringcolor.jpg", onLoadRing);
        }

        function createGeometry(body)
        {
            var answer;
            var max;

            if (body.radiusX && body.radiusY && body.radiusZ)
            {
                max = body.maxRadius;
                answer = new THREE.SphereGeometry(max, 32, 32);
                answer.applyMatrix(new THREE.Matrix4().makeScale(body.radiusX / max, body.radiusY / max, body.radiusZ /
                        max));
            }
            else if (body.equatorialRadius && body.polarRadius)
            {
                max = body.maxRadius;
                answer = new THREE.SphereGeometry(max, 32, 32);
                answer.applyMatrix(new THREE.Matrix4().makeScale(body.equatorialRadius / max, body.polarRadius / max,
                        body.equatorialRadius / max));
            }
            else if (body.radius)
            {
                answer = new THREE.SphereGeometry(body.radius, 32, 32);
            }
            else
            {
                throw "Unknown radius for body: " + body.value;
            }

            // Rotate sphere axis to Z.
            answer.rotateX(90.0 * Math.PI / 180.0);

            return answer;
        }

        function createMaterial()
        {
            var materialProps =
            {
                map: map,
            };

            if (isBump)
            {
                materialProps.bumpMap = bumpMap;
                materialProps.bumpScale = 0.05;
            }

            if (isSpecular)
            {
                materialProps.specularMap = specularMap;
                materialProps.specular = new THREE.Color('grey');
            }

            return new THREE.MeshPhongMaterial(materialProps);
        }

        function createRing(body, ringMap)
        {
            var innerRadius = body.ringInnerRadius;
            var outerRadius = body.ringOuterRadius;
            var thetaSegments = 32;
            var phiSegments = 8;
            var thetaStart = 0;
            var thetaLength = 2.0 * Math.PI;
            var geometry = new THREE.RingGeometry2(innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart,
                    thetaLength);

            ringMap.wrapS = THREE.RepeatWrapping;
            ringMap.wrapT = THREE.RepeatWrapping;
            ringMap.repeat.set(2, 2);
            var materialProps =
            {
                map: ringMap,
                color: 0xffff00,
                side: THREE.DoubleSide,
                transparent: true,
                opacity: 0.6,
            };

            var material = new THREE.MeshPhongMaterial(materialProps);
            var mesh = new THREE.Mesh(geometry, material);

            return mesh;
        }

        function finishConstruction()
        {
            var geometry = createGeometry(body);
            var material = createMaterial();

            var mesh = new THREE.Mesh(geometry, material);
            mesh.name = body.name;
            mesh.bodyKey = body.value;

            if (isRing)
            {
                // Add ring.
                mesh.add(createRing(body, ringMap));
            }

            // FIXME: temporary.
            mesh.add(new THREE.AxisHelper(1.33 * body.maxRadius));

            callback(mesh);
        }

        function isLoaded()
        {
            return (map !== undefined) && (!isBump || bumpMap !== undefined) &&
                    (!isSpecular || specularMap !== undefined) && (!isRing || ringMap !== undefined);
        }

        function onError(xhr)
        {
            LOGGER.error("xhr = " + xhr);
            LOGGER.error("xhr.error = " + xhr.error);
            LOGGER.error("xhr.error.type = " + xhr.error.type);
        }

        function onLoadBump(texture)
        {
            bumpMap = texture;

            if (isLoaded())
            {
                finishConstruction();
            }
        }

        function onLoadMap(texture)
        {
            map = texture;

            if (isLoaded())
            {
                finishConstruction();
            }
        }

        function onLoadRing(texture)
        {
            ringMap = texture;

            if (isLoaded())
            {
                finishConstruction();
            }
        }

        function onLoadSpecular(texture)
        {
            specularMap = texture;

            if (isLoaded())
            {
                finishConstruction();
            }
        }
    }

    return BodyUI;
});
<commit_msg>Changed to make Sol appear to glow.<commit_after>define([ "Body" ], function(Body)
{
    "use strict";
    function BodyUI(body, isBump, isSpecular, callback)
    {
        InputValidator.validateNotNull("body", body);
        InputValidator.validateNotNull("isBump", isBump);
        InputValidator.validateNotNull("isSpecular", isSpecular);
        InputValidator.validateNotNull("callback", callback);

        // Start loading textures.
        var map, bumpMap, specularMap, ringMap;
        var loader = new THREE.TextureLoader();
        loader.crossOrigin = "anonymous";
        var name = body.name.toLowerCase();
        loader.load(imageBase + name + "map.jpg", onLoadMap);

        if (isBump)
        {
            loader.load(imageBase + name + "bump.jpg", onLoadBump);
        }

        if (isSpecular)
        {
            loader.load(imageBase + name + "spec.jpg", onLoadSpecular);
        }

        var isRing = body.ringInnerRadius && body.ringOuterRadius;

        if (isRing)
        {
            loader.load(imageBase + name + "ringcolor.jpg", onLoadRing);
        }

        function createGeometry(body)
        {
            var answer;
            var max;

            if (body.radiusX && body.radiusY && body.radiusZ)
            {
                max = body.maxRadius;
                answer = new THREE.SphereGeometry(max, 32, 32);
                answer.applyMatrix(new THREE.Matrix4().makeScale(body.radiusX / max, body.radiusY / max, body.radiusZ /
                        max));
            }
            else if (body.equatorialRadius && body.polarRadius)
            {
                max = body.maxRadius;
                answer = new THREE.SphereGeometry(max, 32, 32);
                answer.applyMatrix(new THREE.Matrix4().makeScale(body.equatorialRadius / max, body.polarRadius / max,
                        body.equatorialRadius / max));
            }
            else if (body.radius)
            {
                answer = new THREE.SphereGeometry(body.radius, 32, 32);
            }
            else
            {
                throw "Unknown radius for body: " + body.value;
            }

            // Rotate sphere axis to Z.
            answer.rotateX(90.0 * Math.PI / 180.0);

            return answer;
        }

        function createMaterial()
        {
            var materialProps =
            {
                map: map,
            };

            if (isBump)
            {
                materialProps.bumpMap = bumpMap;
                materialProps.bumpScale = 0.05;
            }

            if (isSpecular)
            {
                materialProps.specularMap = specularMap;
                materialProps.specular = new THREE.Color('grey');
            }

            var answer;

            if (body.value === Body.SOL)
            {
                answer = new THREE.MeshBasicMaterial(materialProps);
            }
            else
            {
                answer = new THREE.MeshPhongMaterial(materialProps);
            }

            return answer;
        }

        function createRing(body, ringMap)
        {
            var innerRadius = body.ringInnerRadius;
            var outerRadius = body.ringOuterRadius;
            var thetaSegments = 32;
            var phiSegments = 8;
            var thetaStart = 0;
            var thetaLength = 2.0 * Math.PI;
            var geometry = new THREE.RingGeometry2(innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart,
                    thetaLength);

            ringMap.wrapS = THREE.RepeatWrapping;
            ringMap.wrapT = THREE.RepeatWrapping;
            ringMap.repeat.set(2, 2);
            var materialProps =
            {
                map: ringMap,
                color: 0xffff00,
                side: THREE.DoubleSide,
                transparent: true,
                opacity: 0.6,
            };

            var material = new THREE.MeshPhongMaterial(materialProps);
            var mesh = new THREE.Mesh(geometry, material);

            return mesh;
        }

        function finishConstruction()
        {
            var geometry = createGeometry(body);
            var material = createMaterial();

            var mesh = new THREE.Mesh(geometry, material);
            mesh.name = body.name;
            mesh.bodyKey = body.value;

            if (isRing)
            {
                // Add ring.
                mesh.add(createRing(body, ringMap));
            }

            // FIXME: temporary.
            mesh.add(new THREE.AxisHelper(1.33 * body.maxRadius));

            callback(mesh);
        }

        function isLoaded()
        {
            return (map !== undefined) && (!isBump || bumpMap !== undefined) &&
                    (!isSpecular || specularMap !== undefined) && (!isRing || ringMap !== undefined);
        }

        function onError(xhr)
        {
            LOGGER.error("xhr = " + xhr);
            LOGGER.error("xhr.error = " + xhr.error);
            LOGGER.error("xhr.error.type = " + xhr.error.type);
        }

        function onLoadBump(texture)
        {
            bumpMap = texture;

            if (isLoaded())
            {
                finishConstruction();
            }
        }

        function onLoadMap(texture)
        {
            map = texture;

            if (isLoaded())
            {
                finishConstruction();
            }
        }

        function onLoadRing(texture)
        {
            ringMap = texture;

            if (isLoaded())
            {
                finishConstruction();
            }
        }

        function onLoadSpecular(texture)
        {
            specularMap = texture;

            if (isLoaded())
            {
                finishConstruction();
            }
        }
    }

    return BodyUI;
});
<|endoftext|>


<commit_before>'use strict';
let Rx = require('rx');
const ALL_PROPS = '*';
const PROPS_DRIVER_NAME = 'props';
const EVENTS_SINK_NAME = 'events';

function makeDispatchFunction(element, eventName) {
  return function dispatchCustomEvent(evData) {
    //console.log('%cdispatchCustomEvent ' + eventName,
    //  'background-color: #CCCCFF; color: black');
    let event;
    try {
      event = new Event(eventName);
    } catch (err) {
      event = document.createEvent('Event');
      event.initEvent(eventName, true, true);
    }
    event.detail = evData;
    element.dispatchEvent(event);
  };
}

function subscribeDispatchers(element) {
  let {customEvents} = element.cycleCustomElementMetadata;
  let disposables = new Rx.CompositeDisposable();
  for (let name in customEvents) { if (customEvents.hasOwnProperty(name)) {
    if (typeof customEvents[name].subscribe === 'function') {
      let disposable = customEvents[name].subscribe(
        makeDispatchFunction(element, name)
      );
      disposables.add(disposable);
    }
  }}
  return disposables;
}

function subscribeDispatchersWhenRootChanges(metadata) {
  return metadata.rootElem$
    .distinctUntilChanged(Rx.helpers.identity,
      (x, y) => (x && y && x.isEqualNode && x.isEqualNode(y))
    )
    .subscribe(function resubscribeDispatchers(rootElem) {
      if (metadata.eventDispatchingSubscription) {
        metadata.eventDispatchingSubscription.dispose();
      }
      metadata.eventDispatchingSubscription = subscribeDispatchers(rootElem);
    });
}

function subscribeEventDispatchingSink(element, widget) {
  element.cycleCustomElementMetadata.eventDispatchingSubscription =
    subscribeDispatchers(element);
  widget.disposables.add(
    element.cycleCustomElementMetadata.eventDispatchingSubscription
  );
  widget.disposables.add(
    subscribeDispatchersWhenRootChanges(element.cycleCustomElementMetadata)
  );
}

function makePropertiesDriver() {
  let propertiesDriver = {};
  let defaultComparer = Rx.helpers.defaultComparer;
  Object.defineProperty(propertiesDriver, 'type', {
    enumerable: false,
    value: 'PropertiesDriver'
  });
  Object.defineProperty(propertiesDriver, 'get', {
    enumerable: false,
    value: function get(streamKey, comparer = defaultComparer) {
      if (typeof streamKey === 'undefined') {
        throw new Error('Custom element driver `props.get()` expects an ' +
          'argument in the getter.');
      }
      if (typeof this[streamKey] === 'undefined') {
        this[streamKey] = new Rx.ReplaySubject(1);
      }
      return this[streamKey]
        .distinctUntilChanged(Rx.helpers.identity, comparer);
    }
  });
  Object.defineProperty(propertiesDriver, 'getAll', {
    enumerable: false,
    value: function getAll() {
      return this.get('*');
    }
  });
  return propertiesDriver;
}

function createContainerElement(tagName, vtreeProperties) {
  let element = document.createElement('div');
  element.id = vtreeProperties.id || '';
  element.className = vtreeProperties.className || '';
  element.className += ' cycleCustomElement-' + tagName.toUpperCase();
  return element;
}

function warnIfVTreeHasNoKey(vtree) {
  if (typeof vtree.key === 'undefined') {
    console.warn('Missing `key` property for Cycle custom element ' +
      vtree.tagName);
  }
}

function throwIfVTreeHasPropertyChildren(vtree) {
  if (typeof vtree.properties.children !== 'undefined') {
    throw new Error('Custom element should not have property `children`. ' +
      'It is reserved for children elements nested into this custom element.');
  }
}

function makeCustomElementInput(domOutput, propertiesDriver, domDriverName) {
  return {
    [domDriverName]: domOutput,
    [PROPS_DRIVER_NAME]: propertiesDriver
  };
}

function makeConstructor() {
  return function customElementConstructor(vtree, CERegistry, driverName) {
    //console.log('%cnew (constructor) custom element ' + vtree.tagName,
    //  'color: #880088');
    warnIfVTreeHasNoKey(vtree);
    throwIfVTreeHasPropertyChildren(vtree);
    this.type = 'Widget';
    this.properties = vtree.properties;
    this.properties.children = vtree.children;
    this.key = vtree.key;
    this.isCustomElementWidget = true;
    this.customElementsRegistry = CERegistry;
    this.driverName = driverName;
    this.firstRootElem$ = new Rx.ReplaySubject(1);
    this.disposables = new Rx.CompositeDisposable();
  };
}

function validateDefFnOutput(defFnOutput, domDriverName, tagName) {
  if (typeof defFnOutput !== 'object') {
    throw new Error(`Custom element definition function for '${tagName}' ` +
      ' should output an object.');
  }
  if (typeof defFnOutput[domDriverName] === 'undefined') {
    throw new Error(`Custom element definition function for '${tagName}' ` +
      `should output an object containing '${domDriverName}'.`);
  }
  if (typeof defFnOutput[domDriverName].subscribe !== 'function') {
    throw new Error(`Custom element definition function for '${tagName}' ` +
      `should output an object containing an Observable of VTree, named ` +
      `'${domDriverName}'.`);
  }
  for (let name in defFnOutput) { if (defFnOutput.hasOwnProperty(name)) {
    if (name !== domDriverName && name !== EVENTS_SINK_NAME) {
      throw new Error(`Unknown '${name}' found on custom element ` +
        `'${tagName}'s definition function's output.`);
    }
  }}
}

function makeInit(tagName, definitionFn) {
  let {makeDOMDriverWithRegistry} = require('./render-dom');
  return function initCustomElement() {
    //console.log('%cInit() custom element ' + tagName, 'color: #880088');
    let widget = this;
    let driverName = widget.driverName;
    let registry = widget.customElementsRegistry;
    let element = createContainerElement(tagName, widget.properties);
    let proxyVTree$$ = new Rx.AsyncSubject();
    let domDriver = makeDOMDriverWithRegistry(element, registry);
    let propertiesDriver = makePropertiesDriver();
    let domResponse = domDriver(proxyVTree$$.mergeAll(), driverName);
    let rootElem$ = domResponse.get(':root');
    let defFnInput = makeCustomElementInput(
      domResponse, propertiesDriver, driverName
    );
    let requests = definitionFn(defFnInput);
    validateDefFnOutput(requests, driverName, tagName);
    proxyVTree$$.onNext(requests[driverName].shareReplay(1));
    proxyVTree$$.onCompleted();
    rootElem$.subscribe(widget.firstRootElem$.asObserver());
    element.cycleCustomElementMetadata = {
      propertiesDriver,
      rootElem$,
      customEvents: requests.events,
      eventDispatchingSubscription: false
    };
    subscribeEventDispatchingSink(element, widget);
    widget.disposables.add(widget.firstRootElem$);
    widget.disposables.add(proxyVTree$$);
    widget.disposables.add(domResponse);
    widget.update(null, element);
    return element;
  };
}

function validatePropertiesDriverInMetadata(element, fnName) {
  if (!element) {
    throw new Error(`Missing DOM element when calling ${fnName} on custom ` +
      'element Widget.');
  }
  if (!element.cycleCustomElementMetadata) {
    throw new Error('Missing custom element metadata on DOM element when ' +
      'calling ' + fnName + ' on custom element Widget.');
  }
  let metadata = element.cycleCustomElementMetadata;
  if (metadata.propertiesDriver.type !== 'PropertiesDriver') {
    throw new Error('Custom element metadata\'s propertiesDriver type is ' +
      'invalid: ' + metadata.propertiesDriver.type + '.');
  }
}

function updateCustomElement(previous, element) {
  if (previous) {
    this.disposables = previous.disposables;
    this.firstRootElem$.onNext(0);
    this.firstRootElem$.onCompleted();
  }
  validatePropertiesDriverInMetadata(element, 'update()');

  //console.log(`%cupdate() ${element.className}`, 'color: #880088');
  let propsDriver = element.cycleCustomElementMetadata.propertiesDriver;
  if (propsDriver.hasOwnProperty(ALL_PROPS)) {
    propsDriver[ALL_PROPS].onNext(this.properties);
  }
  for (let prop in propsDriver) { if (propsDriver.hasOwnProperty(prop)) {
    if (this.properties.hasOwnProperty(prop)) {
      propsDriver[prop].onNext(this.properties[prop]);
    }
  }}
}

function destroyCustomElement(element) {
  //console.log(`%cdestroy() custom el ${element.className}`, 'color: #808');
  // Dispose propertiesDriver
  let propsDriver = element.cycleCustomElementMetadata.propertiesDriver;
  for (let prop in propsDriver) { if (propsDriver.hasOwnProperty(prop)) {
    this.disposables.add(propsDriver[prop]);
  }}
  if (element.cycleCustomElementMetadata.eventDispatchingSubscription) {
    // This subscription has to be disposed.
    // Because disposing subscribeDispatchersWhenRootChanges only
    // is not enough.
    this.disposables.add(
      element.cycleCustomElementMetadata.eventDispatchingSubscription
    );
  }
  this.disposables.dispose();
}

function makeWidgetClass(tagName, definitionFn) {
  if (typeof definitionFn !== 'function') {
    throw new Error('A custom element definition given to the DOM driver ' +
      'should be a function.');
  }

  let WidgetClass = makeConstructor();
  WidgetClass.definitionFn = definitionFn; // needed by renderAsHTML
  WidgetClass.prototype.init = makeInit(tagName, definitionFn);
  WidgetClass.prototype.update = updateCustomElement;
  WidgetClass.prototype.destroy = destroyCustomElement;
  return WidgetClass;
}

module.exports = {
  makeDispatchFunction,
  subscribeDispatchers,
  subscribeDispatchersWhenRootChanges,
  makePropertiesDriver,
  createContainerElement,
  warnIfVTreeHasNoKey,
  throwIfVTreeHasPropertyChildren,
  makeConstructor,
  makeInit,
  updateCustomElement,
  destroyCustomElement,

  ALL_PROPS,
  makeCustomElementInput,
  makeWidgetClass
};
<commit_msg>Change props.getAll() implementation to use ALL_PROPS const<commit_after>'use strict';
let Rx = require('rx');
const ALL_PROPS = '*';
const PROPS_DRIVER_NAME = 'props';
const EVENTS_SINK_NAME = 'events';

function makeDispatchFunction(element, eventName) {
  return function dispatchCustomEvent(evData) {
    //console.log('%cdispatchCustomEvent ' + eventName,
    //  'background-color: #CCCCFF; color: black');
    let event;
    try {
      event = new Event(eventName);
    } catch (err) {
      event = document.createEvent('Event');
      event.initEvent(eventName, true, true);
    }
    event.detail = evData;
    element.dispatchEvent(event);
  };
}

function subscribeDispatchers(element) {
  let {customEvents} = element.cycleCustomElementMetadata;
  let disposables = new Rx.CompositeDisposable();
  for (let name in customEvents) { if (customEvents.hasOwnProperty(name)) {
    if (typeof customEvents[name].subscribe === 'function') {
      let disposable = customEvents[name].subscribe(
        makeDispatchFunction(element, name)
      );
      disposables.add(disposable);
    }
  }}
  return disposables;
}

function subscribeDispatchersWhenRootChanges(metadata) {
  return metadata.rootElem$
    .distinctUntilChanged(Rx.helpers.identity,
      (x, y) => (x && y && x.isEqualNode && x.isEqualNode(y))
    )
    .subscribe(function resubscribeDispatchers(rootElem) {
      if (metadata.eventDispatchingSubscription) {
        metadata.eventDispatchingSubscription.dispose();
      }
      metadata.eventDispatchingSubscription = subscribeDispatchers(rootElem);
    });
}

function subscribeEventDispatchingSink(element, widget) {
  element.cycleCustomElementMetadata.eventDispatchingSubscription =
    subscribeDispatchers(element);
  widget.disposables.add(
    element.cycleCustomElementMetadata.eventDispatchingSubscription
  );
  widget.disposables.add(
    subscribeDispatchersWhenRootChanges(element.cycleCustomElementMetadata)
  );
}

function makePropertiesDriver() {
  let propertiesDriver = {};
  let defaultComparer = Rx.helpers.defaultComparer;
  Object.defineProperty(propertiesDriver, 'type', {
    enumerable: false,
    value: 'PropertiesDriver'
  });
  Object.defineProperty(propertiesDriver, 'get', {
    enumerable: false,
    value: function get(streamKey, comparer = defaultComparer) {
      if (typeof streamKey === 'undefined') {
        throw new Error('Custom element driver `props.get()` expects an ' +
          'argument in the getter.');
      }
      if (typeof this[streamKey] === 'undefined') {
        this[streamKey] = new Rx.ReplaySubject(1);
      }
      return this[streamKey]
        .distinctUntilChanged(Rx.helpers.identity, comparer);
    }
  });
  Object.defineProperty(propertiesDriver, 'getAll', {
    enumerable: false,
    value: function getAll() {
      return this.get(ALL_PROPS);
    }
  });
  return propertiesDriver;
}

function createContainerElement(tagName, vtreeProperties) {
  let element = document.createElement('div');
  element.id = vtreeProperties.id || '';
  element.className = vtreeProperties.className || '';
  element.className += ' cycleCustomElement-' + tagName.toUpperCase();
  return element;
}

function warnIfVTreeHasNoKey(vtree) {
  if (typeof vtree.key === 'undefined') {
    console.warn('Missing `key` property for Cycle custom element ' +
      vtree.tagName);
  }
}

function throwIfVTreeHasPropertyChildren(vtree) {
  if (typeof vtree.properties.children !== 'undefined') {
    throw new Error('Custom element should not have property `children`. ' +
      'It is reserved for children elements nested into this custom element.');
  }
}

function makeCustomElementInput(domOutput, propertiesDriver, domDriverName) {
  return {
    [domDriverName]: domOutput,
    [PROPS_DRIVER_NAME]: propertiesDriver
  };
}

function makeConstructor() {
  return function customElementConstructor(vtree, CERegistry, driverName) {
    //console.log('%cnew (constructor) custom element ' + vtree.tagName,
    //  'color: #880088');
    warnIfVTreeHasNoKey(vtree);
    throwIfVTreeHasPropertyChildren(vtree);
    this.type = 'Widget';
    this.properties = vtree.properties;
    this.properties.children = vtree.children;
    this.key = vtree.key;
    this.isCustomElementWidget = true;
    this.customElementsRegistry = CERegistry;
    this.driverName = driverName;
    this.firstRootElem$ = new Rx.ReplaySubject(1);
    this.disposables = new Rx.CompositeDisposable();
  };
}

function validateDefFnOutput(defFnOutput, domDriverName, tagName) {
  if (typeof defFnOutput !== 'object') {
    throw new Error(`Custom element definition function for '${tagName}' ` +
      ' should output an object.');
  }
  if (typeof defFnOutput[domDriverName] === 'undefined') {
    throw new Error(`Custom element definition function for '${tagName}' ` +
      `should output an object containing '${domDriverName}'.`);
  }
  if (typeof defFnOutput[domDriverName].subscribe !== 'function') {
    throw new Error(`Custom element definition function for '${tagName}' ` +
      `should output an object containing an Observable of VTree, named ` +
      `'${domDriverName}'.`);
  }
  for (let name in defFnOutput) { if (defFnOutput.hasOwnProperty(name)) {
    if (name !== domDriverName && name !== EVENTS_SINK_NAME) {
      throw new Error(`Unknown '${name}' found on custom element ` +
        `'${tagName}'s definition function's output.`);
    }
  }}
}

function makeInit(tagName, definitionFn) {
  let {makeDOMDriverWithRegistry} = require('./render-dom');
  return function initCustomElement() {
    //console.log('%cInit() custom element ' + tagName, 'color: #880088');
    let widget = this;
    let driverName = widget.driverName;
    let registry = widget.customElementsRegistry;
    let element = createContainerElement(tagName, widget.properties);
    let proxyVTree$$ = new Rx.AsyncSubject();
    let domDriver = makeDOMDriverWithRegistry(element, registry);
    let propertiesDriver = makePropertiesDriver();
    let domResponse = domDriver(proxyVTree$$.mergeAll(), driverName);
    let rootElem$ = domResponse.get(':root');
    let defFnInput = makeCustomElementInput(
      domResponse, propertiesDriver, driverName
    );
    let requests = definitionFn(defFnInput);
    validateDefFnOutput(requests, driverName, tagName);
    proxyVTree$$.onNext(requests[driverName].shareReplay(1));
    proxyVTree$$.onCompleted();
    rootElem$.subscribe(widget.firstRootElem$.asObserver());
    element.cycleCustomElementMetadata = {
      propertiesDriver,
      rootElem$,
      customEvents: requests.events,
      eventDispatchingSubscription: false
    };
    subscribeEventDispatchingSink(element, widget);
    widget.disposables.add(widget.firstRootElem$);
    widget.disposables.add(proxyVTree$$);
    widget.disposables.add(domResponse);
    widget.update(null, element);
    return element;
  };
}

function validatePropertiesDriverInMetadata(element, fnName) {
  if (!element) {
    throw new Error(`Missing DOM element when calling ${fnName} on custom ` +
      'element Widget.');
  }
  if (!element.cycleCustomElementMetadata) {
    throw new Error('Missing custom element metadata on DOM element when ' +
      'calling ' + fnName + ' on custom element Widget.');
  }
  let metadata = element.cycleCustomElementMetadata;
  if (metadata.propertiesDriver.type !== 'PropertiesDriver') {
    throw new Error('Custom element metadata\'s propertiesDriver type is ' +
      'invalid: ' + metadata.propertiesDriver.type + '.');
  }
}

function updateCustomElement(previous, element) {
  if (previous) {
    this.disposables = previous.disposables;
    this.firstRootElem$.onNext(0);
    this.firstRootElem$.onCompleted();
  }
  validatePropertiesDriverInMetadata(element, 'update()');

  //console.log(`%cupdate() ${element.className}`, 'color: #880088');
  let propsDriver = element.cycleCustomElementMetadata.propertiesDriver;
  if (propsDriver.hasOwnProperty(ALL_PROPS)) {
    propsDriver[ALL_PROPS].onNext(this.properties);
  }
  for (let prop in propsDriver) { if (propsDriver.hasOwnProperty(prop)) {
    if (this.properties.hasOwnProperty(prop)) {
      propsDriver[prop].onNext(this.properties[prop]);
    }
  }}
}

function destroyCustomElement(element) {
  //console.log(`%cdestroy() custom el ${element.className}`, 'color: #808');
  // Dispose propertiesDriver
  let propsDriver = element.cycleCustomElementMetadata.propertiesDriver;
  for (let prop in propsDriver) { if (propsDriver.hasOwnProperty(prop)) {
    this.disposables.add(propsDriver[prop]);
  }}
  if (element.cycleCustomElementMetadata.eventDispatchingSubscription) {
    // This subscription has to be disposed.
    // Because disposing subscribeDispatchersWhenRootChanges only
    // is not enough.
    this.disposables.add(
      element.cycleCustomElementMetadata.eventDispatchingSubscription
    );
  }
  this.disposables.dispose();
}

function makeWidgetClass(tagName, definitionFn) {
  if (typeof definitionFn !== 'function') {
    throw new Error('A custom element definition given to the DOM driver ' +
      'should be a function.');
  }

  let WidgetClass = makeConstructor();
  WidgetClass.definitionFn = definitionFn; // needed by renderAsHTML
  WidgetClass.prototype.init = makeInit(tagName, definitionFn);
  WidgetClass.prototype.update = updateCustomElement;
  WidgetClass.prototype.destroy = destroyCustomElement;
  return WidgetClass;
}

module.exports = {
  makeDispatchFunction,
  subscribeDispatchers,
  subscribeDispatchersWhenRootChanges,
  makePropertiesDriver,
  createContainerElement,
  warnIfVTreeHasNoKey,
  throwIfVTreeHasPropertyChildren,
  makeConstructor,
  makeInit,
  updateCustomElement,
  destroyCustomElement,

  ALL_PROPS,
  makeCustomElementInput,
  makeWidgetClass
};
<|endoftext|>


<commit_before>/* ==========================================================
 * bootstrap-twipsy.js v1.3.0
 * http://twitter.github.com/bootstrap/javascript.html#twipsy
 * Adapted from the original jQuery.tipsy by Jason Frame
 * ==========================================================
 * Copyright 2011 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */

/*
 * Ender support requires: qwery (or other selector engine) domready bowser bonzo bean valentine
 * Note that bean does not currently support the 'live' option.
 */

!function( $ ) {

 /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
  * ======================================================= */

  var transitionEnd

  !$.support && ($.support = {})

  $(document).ready(function () {

    $.support.transition = (function () {
      var thisBody = document.body || document.documentElement
        , thisStyle = thisBody.style
        , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
      return support
    })()

    // set CSS transition event type
    if ( $.support.transition ) {
      transitionEnd = "TransitionEnd"
      if ( $.browser.webkit ) {
      	transitionEnd = "webkitTransitionEnd"
      } else if ( $.browser.mozilla ) {
      	transitionEnd = "transitionend"
      } else if ( $.browser.opera ) {
      	transitionEnd = "oTransitionEnd"
      }
    }

  })

 /* TWIPSY PUBLIC CLASS DEFINITION
  * ============================== */

  var Twipsy = function ( element, options ) {
    this.$element = $(element)
    this.options = options
    this.enabled = true
    this.fixTitle()
  }

  Twipsy.prototype = {

    show: function() {
      var pos
        , actualWidth
        , actualHeight
        , placement
        , $tip
        , tp

      if (this.getTitle() && this.enabled) {
        $tip = this.tip()
        this.setContent()

        if (this.options.animate) {
          $tip.addClass('fade')
        }

        $tip
          .remove()
          .css({ top: 0, left: 0, display: 'block' })
          .prependTo(document.body)

        pos = $.extend({}, this.$element.offset(), {
          width: this.$element[0].offsetWidth
        , height: this.$element[0].offsetHeight
        })

        actualWidth = $tip[0].offsetWidth
        actualHeight = $tip[0].offsetHeight
        placement = _.maybeCall(this.options.placement, this.$element[0])

        switch (placement) {
          case 'below':
            tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
            break
          case 'above':
            tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
            break
          case 'left':
            tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}
            break
          case 'right':
            tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}
            break
        }

        $tip
          .css(tp)
          .addClass(placement)
          .addClass('in')
      }
    }

  , setContent: function () {
      var $tip = this.tip()
      $tip.find('.twipsy-inner')[this.options.html ? 'html' : 'text'](this.getTitle())
      $tip[0].className = 'twipsy'
    }

  , hide: function() {
      var that = this
        , $tip = this.tip()

      $tip.removeClass('in')

      function removeElement () {
    	$tip.unbind(transitionEnd, removeElement)
        $tip.remove()
      }

      $.support.transition && this.$tip.hasClass('fade') ?
        $tip.bind(transitionEnd, removeElement) :
        removeElement()
    }

  , fixTitle: function() {
      var $e = this.$element
      if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
        $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
      }
    }

  , getTitle: function() {
      var title
        , $e = this.$element
        , o = this.options

        this.fixTitle()

        if (typeof o.title == 'string') {
          title = $e.attr(o.title == 'title' ? 'data-original-title' : o.title)
        } else if (typeof o.title == 'function') {
          title = o.title.call($e[0])
        }

        title = ('' + title).replace(/(^\s*|\s*$)/, "")

        return title || o.fallback
    }

  , tip: function() {
      if (!this.$tip) {
        this.$tip = $('
').html('

')
      }
      return this.$tip
    }

  , validate: function() {
      if (!this.$element[0].parentNode) {
        this.hide()
        this.$element = null
        this.options = null
      }
    }

  , enable: function() {
      this.enabled = true
    }

  , disable: function() {
      this.enabled = false
    }

  , toggleEnabled: function() {
      this.enabled = !this.enabled
    }

  }

 /* TWIPSY PRIVATE METHODS
  * ====================== */

   var _ = {

     maybeCall: function ( thing, ctx ) {
       return (typeof thing == 'function') ? (thing.call(ctx)) : thing
     }

   }

 /* TWIPSY PLUGIN DEFINITION
  * ======================== */

  $.fn.twipsy = function (options) {
    $.fn.twipsy.initWith.call(this, options, Twipsy, 'twipsy')
    return this
  }

  $.fn.twipsy.initWith = function (options, Constructor, name) {
    var twipsy
      , binder
      , eventIn
      , eventOut

    if (options === true) {
      return this.data(name)
    } else if (typeof options == 'string') {
      twipsy = this.data(name)
      if (twipsy) {
        twipsy[options]()
      }
      return this
    }

    options = $.extend({}, $.fn[name].defaults, options)

    function get(ele) {
      var twipsy = $(ele).data(name)

      if (!twipsy) {
        twipsy = new Constructor(ele, $.fn.twipsy.elementOptions(ele, options))
        $(ele).data(name, twipsy)
      }

      return twipsy
    }

    function enter() {
      var twipsy = get(this)
      twipsy.hoverState = 'in'

      if (options.delayIn == 0) {
        twipsy.show()
      } else {
        twipsy.fixTitle()
        setTimeout(function() {
          if (twipsy.hoverState == 'in') {
            twipsy.show()
          }
        }, options.delayIn)
      }
    }

    function leave() {
      var twipsy = get(this)
      twipsy.hoverState = 'out'
      if (options.delayOut == 0) {
        twipsy.hide()
      } else {
        setTimeout(function() {
          if (twipsy.hoverState == 'out') {
            twipsy.hide()
          }
        }, options.delayOut)
      }
    }

    if (!options.live) {
      this.each(function() {
        get(this)
      })
    }

    if (options.trigger != 'manual') {
      binder   = options.live ? 'live' : 'bind'
      eventIn  = options.trigger == 'hover' ? 'mouseenter' : 'focus'
      eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'
      this[binder](eventIn, enter)[binder](eventOut, leave)
    }

    return this
  }

  $.fn.twipsy.Twipsy = Twipsy

  $.fn.twipsy.defaults = {
    animate: true
  , delayIn: 0
  , delayOut: 0
  , fallback: ''
  , placement: 'above'
  , html: false
  , live: false
  , offset: 0
  , title: 'title'
  , trigger: 'hover'
  }

  $.fn.twipsy.elementOptions = function(ele, options) {
    return $.metadata ? $.extend({}, options, $(ele).metadata()) : options
  }

}( window.jQuery || window.ender );
window.ender && ender.ender(ender.fn, true);
	<commit_msg>less major changes<commit_after>/* ==========================================================
 * bootstrap-twipsy.js v1.3.0
 * http://twitter.github.com/bootstrap/javascript.html#twipsy
 * Adapted from the original jQuery.tipsy by Jason Frame
 * ==========================================================
 * Copyright 2011 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */

!function( $ ) {

 /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
  * ======================================================= */

  var transitionEnd

  !$.support && ($.support = {})

  $(document).ready(function () {

    $.support.transition = (function () {
      var thisBody = document.body || document.documentElement
        , thisStyle = thisBody.style
        , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
      return support
    })()

    // set CSS transition event type
    if ( $.support.transition ) {
      transitionEnd = "TransitionEnd"
      if ( $.browser.webkit ) {
      	transitionEnd = "webkitTransitionEnd"
      } else if ( $.browser.mozilla ) {
      	transitionEnd = "transitionend"
      } else if ( $.browser.opera ) {
      	transitionEnd = "oTransitionEnd"
      }
    }

  })

 /* TWIPSY PUBLIC CLASS DEFINITION
  * ============================== */

  var Twipsy = function ( element, options ) {
    this.$element = $(element)
    this.options = options
    this.enabled = true
    this.fixTitle()
  }

  Twipsy.prototype = {

    show: function() {
      var pos
        , actualWidth
        , actualHeight
        , placement
        , $tip
        , tp

      if (this.getTitle() && this.enabled) {
        $tip = this.tip()
        this.setContent()

        if (this.options.animate) {
          $tip.addClass('fade')
        }

        $tip
          .remove()
          .css({ top: 0, left: 0, display: 'block' })
          .prependTo(document.body)

        pos = $.extend({}, this.$element.offset(), {
          width: this.$element[0].offsetWidth
        , height: this.$element[0].offsetHeight
        })

        actualWidth = $tip[0].offsetWidth
        actualHeight = $tip[0].offsetHeight
        placement = _.maybeCall(this.options.placement, this.$element[0])

        switch (placement) {
          case 'below':
            tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
            break
          case 'above':
            tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
            break
          case 'left':
            tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}
            break
          case 'right':
            tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}
            break
        }

        $tip
          .css(tp)
          .addClass(placement)
          .addClass('in')
      }
    }

  , setContent: function () {
      var $tip = this.tip()
      $tip.find('.twipsy-inner')[this.options.html ? 'html' : 'text'](this.getTitle())
      $tip[0].className = 'twipsy'
    }

  , hide: function() {
      var that = this
        , $tip = this.tip()

      $tip.removeClass('in')

      function removeElement () {
        $tip.unbind(transitionEnd, removeElement)
        $tip.remove()
      }

      $.support.transition && this.$tip.hasClass('fade') ?
        $tip.bind(transitionEnd, removeElement) :
        removeElement()
    }

  , fixTitle: function() {
      var $e = this.$element
      if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
        $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
      }
    }

  , getTitle: function() {
      var title
        , $e = this.$element
        , o = this.options

        this.fixTitle()

        if (typeof o.title == 'string') {
          title = $e.attr(o.title == 'title' ? 'data-original-title' : o.title)
        } else if (typeof o.title == 'function') {
          title = o.title.call($e[0])
        }

        title = ('' + title).replace(/(^\s*|\s*$)/, "")

        return title || o.fallback
    }

  , tip: function() {
      if (!this.$tip) {
        this.$tip = $('
').html('

')
      }
      return this.$tip
    }

  , validate: function() {
      if (!this.$element[0].parentNode) {
        this.hide()
        this.$element = null
        this.options = null
      }
    }

  , enable: function() {
      this.enabled = true
    }

  , disable: function() {
      this.enabled = false
    }

  , toggleEnabled: function() {
      this.enabled = !this.enabled
    }

  }

 /* TWIPSY PRIVATE METHODS
  * ====================== */

   var _ = {

     maybeCall: function ( thing, ctx ) {
       return (typeof thing == 'function') ? (thing.call(ctx)) : thing
     }

   }

 /* TWIPSY PLUGIN DEFINITION
  * ======================== */

  $.fn.twipsy = function (options) {
    $.fn.twipsy.initWith.call(this, options, Twipsy, 'twipsy')
    return this
  }

  $.fn.twipsy.initWith = function (options, Constructor, name) {
    var twipsy
      , binder
      , eventIn
      , eventOut

    if (options === true) {
      return this.data(name)
    } else if (typeof options == 'string') {
      twipsy = this.data(name)
      if (twipsy) {
        twipsy[options]()
      }
      return this
    }

    options = $.extend({}, $.fn[name].defaults, options)

    function get(ele) {
      var twipsy = $(ele).data(name)

      if (!twipsy) {
        twipsy = new Constructor(ele, $.fn.twipsy.elementOptions(ele, options))
        $(ele).data(name, twipsy)
      }

      return twipsy
    }

    function enter() {
      var twipsy = get(this)
      twipsy.hoverState = 'in'

      if (options.delayIn == 0) {
        twipsy.show()
      } else {
        twipsy.fixTitle()
        setTimeout(function() {
          if (twipsy.hoverState == 'in') {
            twipsy.show()
          }
        }, options.delayIn)
      }
    }

    function leave() {
      var twipsy = get(this)
      twipsy.hoverState = 'out'
      if (options.delayOut == 0) {
        twipsy.hide()
      } else {
        setTimeout(function() {
          if (twipsy.hoverState == 'out') {
            twipsy.hide()
          }
        }, options.delayOut)
      }
    }

    if (!options.live) {
      this.each(function() {
        get(this)
      })
    }

    if (options.trigger != 'manual') {
      binder   = options.live ? 'live' : 'bind'
      eventIn  = options.trigger == 'hover' ? 'mouseenter' : 'focus'
      eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'
      this[binder](eventIn, enter)[binder](eventOut, leave)
    }

    return this
  }

  $.fn.twipsy.Twipsy = Twipsy

  $.fn.twipsy.defaults = {
    animate: true
  , delayIn: 0
  , delayOut: 0
  , fallback: ''
  , placement: 'above'
  , html: false
  , live: false
  , offset: 0
  , title: 'title'
  , trigger: 'hover'
  }

  $.fn.twipsy.elementOptions = function(ele, options) {
    return $.metadata ? $.extend({}, options, $(ele).metadata()) : options
  }

}( window.jQuery || window.ender );
<|endoftext|>


<commit_before>var util = require('util');
var _ = require('underscore');

var amablaUtils = require('./utils');

module.exports = function(config) {
    return new handler(config);
}

handler = function(config) {
    this.config = config;
    return this;
}

handler.prototype.handleError = function(e, req, res, next) {

    if (util.isError(e)) {

        // Better to use EventEmitter no ?
        if (this.config.debug == true) {
            console.log(e.stack)
        }

        var errorDisplayed = {error: amablaUtils.errorsToString(e)};

        if (this.config.debug) {
            errorDisplayed.stack = e.stack;
        }

        // Handle default Javascript Error as Internal Error
        if (e instanceof TypeError || e instanceof ReferenceError) {
            return res.status(500).json(errorDisplayed);
        }

        // Handle our amabla error
        switch (e.name) {
            case 'NotFoundError':
                return res.status(404).json(errorDisplayed);
                break;
            case 'AccessDeniedError':
                return res.status(403).json(errorDisplayed);
                break;
        }

        // If you emit custom error with statusCode
        if (e.statusCode != undefined) {
            return res.status(e.statusCode).json(errorDisplayed);
        }

    // bad use of amabla-core
    } else if (_.isString(e)) {
        return res.status(500).json({error: e});
    }

    return res.status(400).json({error: amablaUtils.errorsToString(e)});
}

handler.prototype.NotFoundError = function(message, type, id)
{
    var self = new Error();
    self.type = type;
    self.message = "NOT_FOUND";
    self.item_id = id;
    if (message != "") {
        self.message = message;
    }

    if (this.config != undefined && this.config.debug) {
        self.message += "  debug:  " + type + " with id: " + id;
    }

    self.name = 'NotFoundError';
    self._proto_ = handler.prototype;
    return self;
}

handler.prototype.AccessDeniedError = function(message, reason)
{
    var self = new Error();

    self.message = "NOT_ALLOWED";
    if (message != "") {
        self.message = message;
    }

    if (this.config != undefined && this.config.debug) {
        self.message += "   debug: " + reason;
    }

    self.name = 'AccessDeniedError';
    self._proto_ = handler.prototype;
    return self;
}

handler.prototype.MissingParametersError = function(message, fields)
{
    var self = new Error();

    self.message = "MISSING_PARAMETERS";
    if (message != "") {
        self.message = message;
    }

    if (this.config != undefined && this.config.debug) {
        self.message += "   debug: " + fields;
    }

    self.name = 'MissingParametersError';
    self._proto_ = handler.prototype;
    return self;
}<commit_msg>fix error handling. Make error as catchable by bluebird<commit_after>var util = require('util');
var _ = require('underscore');

var amablaUtils = require('./utils');

module.exports = function(config) {
    return new handler(config);
}

handler = function(config) {
    this.config = config;

    this.NotFoundError.prototype = Object.create(Error.prototype);
    this.AccessDeniedError.prototype = Object.create(Error.prototype);
    this.MissingParametersError.prototype = Object.create(Error.prototype);

    return this;
}

handler.prototype.handleError = function(e, req, res, next) {

    if (util.isError(e)) {

        // Better to use EventEmitter no ?
        if (this.config.debug == true) {
            console.log(e.stack)
        }

        var errorDisplayed = {error: amablaUtils.errorsToString(e)};

        if (this.config.debug) {
            errorDisplayed.stack = e.stack;
        }

        // Handle default Javascript Error as Internal Error
        if (e instanceof TypeError || e instanceof ReferenceError) {
            return res.status(500).json(errorDisplayed);
        }

        // Handle our amabla error
        switch (e.name) {
            case 'NotFoundError':
                return res.status(404).json(errorDisplayed);
                break;

            case 'MissingParametersError':
                return res.status(400).json(errorDisplayed);
                break;
            case 'AccessDeniedError':
                return res.status(403).json(errorDisplayed);
                break;
        }

        // If you emit custom error with statusCode
        if (e.statusCode != undefined) {
            return res.status(e.statusCode).json(errorDisplayed);
        }

        // bad use of amabla-core
    } else if (_.isString(e)) {
        return res.status(500).json({error: e});
    }

    return res.status(400).json({error: amablaUtils.errorsToString(e)});
}

handler.prototype.NotFoundError = function(message, type, id)
{
    var self = this;

    self.type = type;
    self.message = "NOT_FOUND";
    self.item_id = id;
    if (message != "") {
        self.message = message;
    }

    if (this.config != undefined && this.config.debug) {
        self.message += "  debug:  " + type + " with id: " + id;
    }

    self.name = 'NotFoundError';
    Error.captureStackTrace(this, handler.prototype.NotFoundError);
    return self;
}

handler.prototype.AccessDeniedError = function(message, reason)
{
    var self = this;

    self.message = "NOT_ALLOWED";
    if (message != "") {
        self.message = message;
    }

    if (this.config != undefined && this.config.debug) {
        self.message += "   debug: " + reason;
    }

    self.name = 'AccessDeniedError';
    Error.captureStackTrace(this, handler.prototype.AccessDeniedError);
    return self;
}

handler.prototype.MissingParametersError = function(message, fields)
{
    var self = this;

    self.message = "MISSING_PARAMETERS";
    if (message != "") {
        self.message = message;
    }

    if (this.config != undefined && this.config.debug) {
        self.message += "   debug: " + fields;
    }

    self.name = 'MissingParametersError';

    Error.captureStackTrace(this, handler.prototype.MissingParametersError);
    return self;
}<|endoftext|>