<commit_before>"use 6to5";

var language = require('./geck-language');

module.exports = {
  // The basic regular expression string used to find GECK functions
  geckFnRegEx: '\\b[\\w_\\-]+\\b/',

  // The base URL used for documentation reference for GECK functions
  urlBase: 'http://geck.bethsoft.com/index.php?title=',

  // Base functions page
  fnBase: 'http://geck.bethsoft.com/index.php?title=Category:Functions',

  // Language syntax
  syntax: language,

  // Grammars Added
  grammarsAdded: false,

  // Configuration
  config: {
    highlightVegas: {
      type: 'boolean',
      'default': true
    },
    highlightNVSE: {
      "type": "boolean",
      "default": true
    },
    highlightFOSE: {
      "type": "boolean",
      "default": true
    },
    highlightNX: {
      "type": "boolean",
      "default": true
    }
  },

  /**
   * This is the primary required function for an exported module. This
   * function invokes _getSelectedText with a regular expression string
   * used to determine the name of the function in question and then
   * takes that matching text and opens a web browser to that page if
   * Mark Hahn's web-browser package is installed.
   */
  activate: function(state) {
    atom.commands.add(
      'atom-text-editor',
      'language-geck:lookup',
      this.lookupSelected.bind(this)
    );

    atom.commands.add(
      'atom-text-editor',
      'language-geck:toggleFOSE',
      this.toggleConfig.bind(this, 'language-geck.highlightFOSE')
    );

    atom.commands.add(
      'atom-text-editor',
      'language-geck:toggleVegas',
      this.toggleConfig.bind(this, 'language-geck.highlightVegas')
    );

    atom.commands.add(
      'atom-text-editor',
      'language-geck:toggleNVSE',
      this.toggleConfig.bind(this, 'language-geck.highlightNVSE')
    );

    atom.commands.add(
      'atom-text-editor',
      'language-geck:toggleNX',
      this.toggleConfig.bind(this, 'language-geck.highlightNX')
    );

    atom.packages.on('activated', this.updateGrammars.bind(this));
  },

  /**
   * Command: language-geck:lookup
   *
   * This function is invoked in a manner of ways (menu, context-menu, and
   * keymap); it's purpose is to either open the selected text, hopefully
   * a function known to GECK, in a web browser or the root functions page
   * if the rest is unavailable.
   */
  lookupSelected: function() {
    console.log("[language-geck] lookup invoked!");
    this._getSelectedText(this.geckFnRegEx, (function(text) {
      var browser;

      browser = atom.packages.getActivePackage('web-browser');
      if (browser) {
        if (text.length) {
          console.log('[language-geck] Selected function -> %o', text);
          atom.workspace.open([this.urlBase, text].join(''));
        }
        else {
          console.log('[language-geck] Opening functions root');
          atom.workspace.open(this.fnBase);
        }
      }
      else {
        console.error("[language-geck] web-browser is a required package.");
      }
    }).bind(this));
  },

  toggleConfig: function(config, event) {
    var value = atom.config.get(config);
    atom.config.set(config, !value);
    this.updateGrammars();
  },

  updateGrammars: function() {
    // Obtain the highlight flags
    var cfg = atom.config.get('language-geck');

    // Supplement grammer
    var grammar = atom.grammars.grammarsByScopeName['source.geck'];

    // Remove dynamic grammars
    for (let a = grammar.rawPatterns, i = 0; i < a.length; i++) {
      if (a[i].___type && a[i].___type === 'dynamic') {
        a.splice(i, 1);
        i--;
      }
    }

    var patterns = [
      [true, 'keyword.geck-keyword', [language.keywords]],
      [true, 'keyword.control.geck.blocktype', [language.blocktypes, true]],
      [true, 'keyword.geck.baseFns', [language.geckFns]],
      [cfg.highlightFOSE, 'keyword.geck.foseFns', [language.foseFns]],
      [cfg.highlightVegas, 'keyword.geck.vegasFns', [language.vegasFns]],
      [cfg.highlightNVSE, 'keyword.geck.nvseFns', [language.nvseFns]],
      [cfg.highlightNX, 'keyword.geck.nxFns', [language.nxFns]]
    ];

    for (let set of patterns) {
      let [condition, name, getSetParams] = set;
      if (condition) {
        this.addPattern(grammar.rawPatterns, name, getSetParams);
      }
    }

    // Update everything
    if (grammar.includedGrammarScopes.indexOf(grammar.scopeName) === -1) {
      grammar.includedGrammarScopes.push(grammar.scopeName);
    }
    grammar.grammarUpdated(grammar.scopeName);
  },

  /**
   * Adds a pattern to make the grammar a bit more dynamic. The patterns
   * added by this function can be determined by the unique triple underscore
   * type property. This makes it easier to find these properties as well
   * as manage them as situations arise.
   *
   * @param patterns a reference to the grammar.rawPatterns array
   * @param name a set of class names that will be applied to the span denoting
   * any matches found by the pattern.
   * @param getSetParams an array of arguments used with the geck-language
   * getSet() function.
   * @return the same array that was passed into the function as patterns
   */
  addPattern: function(patterns, name, getSetParams) {
    patterns[patterns.length] = {
      match: language.getSet(...getSetParams),
      name: name,
      ___type: 'dynamic'
    };

    return patterns;
  },

  /**
   * The purpose of this method is to seek out the selected text,
   * determined by the regex string supplied and invoke the supplied
   * callback, executeOn, for each match. The value of the selected
   * text will be supplied as the only parameter to executeOn.
   *
   * @param regex a regular expression in string format
   * @param executeOn a function receiving a single parameter which
   * denotes the selected text in the editor.
   */
  _getSelectedText: function(regex, executeOn) {
    var body, col, cursor, editor, end, filePattern;
    var line, m, opts, range, selection, start, text, _i, _len, _ref;

    editor = atom.workspace.getActiveEditor();

    filePattern = new RegExp(regex, 'g');

    _ref = editor.getSelections();
    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
      selection = _ref[_i];
      range = selection.getBufferRange();

      if (range.isEmpty()) {
        cursor = selection.cursor;
        line = cursor.getCurrentBufferLine();
        col = cursor.getBufferColumn();
        opts = {
          wordRegex: new RegExp(regex)
        };
        start = cursor.getBeginningOfCurrentWordBufferPosition(opts);
        end = cursor.getEndOfCurrentWordBufferPosition(opts);
        range = new Range(start, end);
      }

      text = editor.getTextInBufferRange(range);
      if (text.match(/\s/)) {
        text = "";
      }

      executeOn(text);
    }
  }
};
<commit_msg>Trying on instead of both on and direct<commit_after>"use 6to5";

var language = require('./geck-language');

module.exports = {
  // The basic regular expression string used to find GECK functions
  geckFnRegEx: '\\b[\\w_\\-]+\\b/',

  // The base URL used for documentation reference for GECK functions
  urlBase: 'http://geck.bethsoft.com/index.php?title=',

  // Base functions page
  fnBase: 'http://geck.bethsoft.com/index.php?title=Category:Functions',

  // Language syntax
  syntax: language,

  // Grammars Added
  grammarsAdded: false,

  // Configuration
  config: {
    highlightVegas: {
      type: 'boolean',
      'default': true
    },
    highlightNVSE: {
      "type": "boolean",
      "default": true
    },
    highlightFOSE: {
      "type": "boolean",
      "default": true
    },
    highlightNX: {
      "type": "boolean",
      "default": true
    }
  },

  /**
   * This is the primary required function for an exported module. This
   * function invokes _getSelectedText with a regular expression string
   * used to determine the name of the function in question and then
   * takes that matching text and opens a web browser to that page if
   * Mark Hahn's web-browser package is installed.
   */
  activate: function(state) {
    atom.commands.add(
      'atom-text-editor',
      'language-geck:lookup',
      this.lookupSelected.bind(this)
    );

    atom.commands.add(
      'atom-text-editor',
      'language-geck:toggleFOSE',
      this.toggleConfig.bind(this, 'language-geck.highlightFOSE')
    );

    atom.commands.add(
      'atom-text-editor',
      'language-geck:toggleVegas',
      this.toggleConfig.bind(this, 'language-geck.highlightVegas')
    );

    atom.commands.add(
      'atom-text-editor',
      'language-geck:toggleNVSE',
      this.toggleConfig.bind(this, 'language-geck.highlightNVSE')
    );

    atom.commands.add(
      'atom-text-editor',
      'language-geck:toggleNX',
      this.toggleConfig.bind(this, 'language-geck.highlightNX')
    );

    atom.packages.on('activated', this.updateGrammars.bind(this));
    this.updateGrammars();
  },

  /**
   * Command: language-geck:lookup
   *
   * This function is invoked in a manner of ways (menu, context-menu, and
   * keymap); it's purpose is to either open the selected text, hopefully
   * a function known to GECK, in a web browser or the root functions page
   * if the rest is unavailable.
   */
  lookupSelected: function() {
    console.log("[language-geck] lookup invoked!");
    this._getSelectedText(this.geckFnRegEx, (function(text) {
      var browser;

      browser = atom.packages.getActivePackage('web-browser');
      if (browser) {
        if (text.length) {
          console.log('[language-geck] Selected function -> %o', text);
          atom.workspace.open([this.urlBase, text].join(''));
        }
        else {
          console.log('[language-geck] Opening functions root');
          atom.workspace.open(this.fnBase);
        }
      }
      else {
        console.error("[language-geck] web-browser is a required package.");
      }
    }).bind(this));
  },

  toggleConfig: function(config, event) {
    var value = atom.config.get(config);
    atom.config.set(config, !value);
    this.updateGrammars();
  },

  updateGrammars: function() {
    // Obtain the highlight flags
    var cfg = atom.config.get('language-geck');

    // Supplement grammer
    var grammar = atom.grammars.grammarsByScopeName['source.geck'];

    // Remove dynamic grammars
    for (let a = grammar.rawPatterns, i = 0; i < a.length; i++) {
      if (a[i].___type && a[i].___type === 'dynamic') {
        a.splice(i, 1);
        i--;
      }
    }

    var patterns = [
      [true, 'keyword.geck-keyword', [language.keywords]],
      [true, 'keyword.control.geck.blocktype', [language.blocktypes, true]],
      [true, 'keyword.geck.baseFns', [language.geckFns]],
      [cfg.highlightFOSE, 'keyword.geck.foseFns', [language.foseFns]],
      [cfg.highlightVegas, 'keyword.geck.vegasFns', [language.vegasFns]],
      [cfg.highlightNVSE, 'keyword.geck.nvseFns', [language.nvseFns]],
      [cfg.highlightNX, 'keyword.geck.nxFns', [language.nxFns]]
    ];

    for (let set of patterns) {
      let [condition, name, getSetParams] = set;
      if (condition) {
        this.addPattern(grammar.rawPatterns, name, getSetParams);
      }
    }

    // Update everything
    if (grammar.includedGrammarScopes.indexOf(grammar.scopeName) === -1) {
      grammar.includedGrammarScopes.push(grammar.scopeName);
    }
    grammar.grammarUpdated(grammar.scopeName);
  },

  /**
   * Adds a pattern to make the grammar a bit more dynamic. The patterns
   * added by this function can be determined by the unique triple underscore
   * type property. This makes it easier to find these properties as well
   * as manage them as situations arise.
   *
   * @param patterns a reference to the grammar.rawPatterns array
   * @param name a set of class names that will be applied to the span denoting
   * any matches found by the pattern.
   * @param getSetParams an array of arguments used with the geck-language
   * getSet() function.
   * @return the same array that was passed into the function as patterns
   */
  addPattern: function(patterns, name, getSetParams) {
    patterns[patterns.length] = {
      match: language.getSet(...getSetParams),
      name: name,
      ___type: 'dynamic'
    };

    return patterns;
  },

  /**
   * The purpose of this method is to seek out the selected text,
   * determined by the regex string supplied and invoke the supplied
   * callback, executeOn, for each match. The value of the selected
   * text will be supplied as the only parameter to executeOn.
   *
   * @param regex a regular expression in string format
   * @param executeOn a function receiving a single parameter which
   * denotes the selected text in the editor.
   */
  _getSelectedText: function(regex, executeOn) {
    var body, col, cursor, editor, end, filePattern;
    var line, m, opts, range, selection, start, text, _i, _len, _ref;

    editor = atom.workspace.getActiveEditor();

    filePattern = new RegExp(regex, 'g');

    _ref = editor.getSelections();
    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
      selection = _ref[_i];
      range = selection.getBufferRange();

      if (range.isEmpty()) {
        cursor = selection.cursor;
        line = cursor.getCurrentBufferLine();
        col = cursor.getBufferColumn();
        opts = {
          wordRegex: new RegExp(regex)
        };
        start = cursor.getBeginningOfCurrentWordBufferPosition(opts);
        end = cursor.getEndOfCurrentWordBufferPosition(opts);
        range = new Range(start, end);
      }

      text = editor.getTextInBufferRange(range);
      if (text.match(/\s/)) {
        text = "";
      }

      executeOn(text);
    }
  }
};
<|endoftext|>


<commit_before>/*global window, document*/
/*jshint laxbreak:true*/

var Util = (function() {
	var PI2 = Math.PI * 2;

	return {
		PI2: PI2,

		singleRun: function(aFunction) {
			var started = false;

			return function() {
				if (!started) {
					started = true;
					aFunction.apply(this, arguments);
				}
			};
		},

		noop: function() {
		},

		degToRad: function(aDegrees) {
			return aDegrees * Math.PI / 180;
		},

		normalizeAngle: function(aAngle) {
			while (aAngle < 0) {
				aAngle += PI2;
			}

			return aAngle % PI2;
		},

		normalizeAngleSimple: function(aAngle) {
			if (aAngle < 0) {
				return aAngle + PI2;
			}

			return (aAngle > PI2) ? (aAngle - PI2) : aAngle;
		},

		clamp: function(aX, aMin, aMax) {
			return Math.min(aMax, Math.max(aMin, aX));
		}
	};
})();

function FPSCounter(aUpdateInterval, aContext) {
	var count = 0;
	var fps = '...';
	var time = +new Date();
	var modifier = 1000 / aUpdateInterval;

	var Y = aContext.canvas.height - 10;
	var X = aContext.canvas.width - 50;

	this.update = function(aTime) {
		var diff = aTime - time;

		++count;
		if (diff > aUpdateInterval) {
			fps = 'FPS: ' + Math.floor(count * modifier);

			count = 0;
			time = aTime;
		}
	};

	this.draw = function() {
		aContext.fillStyle = 'red';
		aContext.font = '10px Verdana';
		aContext.fillText(fps, X, Y);
	};
}

function penta(aX) {
	return aX * aX * aX * aX * aX;
}

function Tunnel(aContext, aCenter) {
	var kx;
	var ky;
	var rays = 6;
	var offset = 0;
	var delta = Util.degToRad(2);
	var speed = Util.degToRad(0.5);
	var rayAngle = 2 * Math.PI / rays;

	var center = new Point(aContext.canvas.width / 2, aContext.canvas.height / 2);
	var outerRadius = Math.max(aContext.canvas.height, aContext.canvas.width);
	var innerRadius = outerRadius / 20;

	this.act = function(aKX, aKY) {
		kx = aKX;
		ky = aKY;

		offset = Util.normalizeAngleSimple(offset + speed);
	};

	this.draw = function() {
		aContext.lineWidth = 1;
		aContext.fillStyle = 'purple';

		var i;
		for (i = 0; i < rays; ++i) {
			var angle = offset + (i * rayAngle);

			var angle1Point = Point.singularCirclePoint(angle);
			var angle2Point = Point.singularCirclePoint(angle + delta);

			var outerPoint1 = angle1Point.multiply(outerRadius).translate(aCenter.x + outerRadius * kx, aCenter.y + outerRadius * ky);
			var outerPoint2 = angle2Point.multiply(outerRadius).translate(aCenter.x + outerRadius * kx, aCenter.y + outerRadius * ky);

			var innerPoint1 = angle1Point.multiply(innerRadius).translate(aCenter.x + innerRadius * kx, aCenter.y + innerRadius * ky);
			var innerPoint2 = angle2Point.multiply(innerRadius).translate(aCenter.x + innerRadius * kx, aCenter.y + innerRadius * ky);

			aContext.beginPath();

			aContext.moveTo(outerPoint1.x, outerPoint1.y);
			aContext.lineTo(innerPoint1.x, innerPoint1.y);
			aContext.lineTo(innerPoint2.x, innerPoint2.y);
			aContext.lineTo(outerPoint2.x, outerPoint2.y);

			aContext.closePath();

			aContext.fill();
		}
	};
}

function Game() {
	var rings = [];
	var canvas = document.getElementById("canvas");
	var context = canvas.getContext('2d');
	var fpsCounter = new FPSCounter(1000, context);

	var WIDTH = canvas.width;
	var HEIGHT = canvas.height;
	var center = new Point(WIDTH / 2, HEIGHT / 2);
	var mousePosition = center.translate();
	var tunnel = new Tunnel(context, center);
	var MAX_SIDE = Math.max(center.x, center.y);

	var RING_INITIAL_RADIUS = 10;

	var RING_MAX_ROTATION = 60;

	var FIELD_OF_VIEW_CONSTANT = 400;

	var INITIAL_SPEED = 60;
	var SPEED = INITIAL_SPEED;

	var INITIAL_DISTANCE = 1000;
	var ACCELERATION = 5;

	var ticker = (function() {
		return window.requestAnimationFrame
			|| window.webkitRequestAnimationFrame
			|| window.mozRequestAnimationFrame
			|| window.msRequestAnimationFrame
			|| window.oRequestAnimationFrame
			|| function(callback) {
			window.setTimeout(callback, 1000 / 60);
		};
	})();

	function spawnRing(aDistance) {
		//currently the rings always spawn far away in the distance
		rings.push(new Ring(aDistance));
	}

	function action(aDelta) {
		//first convert the elapsed time from milliseconds to seconds
		var delta = aDelta / 1000;

		//calculating dinstance travelled between the last and current seconds
		var distanceTravelled = SPEED * delta + (ACCELERATION * delta * delta ) / 2;

		var w = WIDTH / 2;
		var kx = Util.clamp((w - mousePosition.x) / w, -0.7, 0.7);

		var h = HEIGHT / 2;
		var ky = Util.clamp((h - mousePosition.y) / h, -0.7, 0.7);

		for (var i = 0, len = rings.length; i < len; i++) {
			//the rings should calculate their new position on the z axis and if necessary do collision checks + destroy + spawn
			rings[i].act(distanceTravelled, delta, kx, ky);
		}
		if (rings.length > 4) {
			rings = rings.slice(rings.length - 4, rings.length);
		}
		tunnel.act(kx, ky);

		//now that the rings have moved according to the elapsed time we can calculate the base speed for the next frame
		SPEED += ACCELERATION * delta;
	}

	function drawBgr() {
		context.rect(0, 0, WIDTH, HEIGHT);
		context.fillStyle = '#FFFFFF';
		context.fill();
	}

	function draw() {
		drawBgr();
		tunnel.draw();

		for (var i = rings.length - 1; i >= 0; i--) {
			rings[i].draw();
		}

		drawSpeed();
		fpsCounter.draw();
	}

	function drawSpeed() {
		context.fillStyle = 'red';
		context.font = '10px Verdana';
		context.fillText('SPD: ' + SPEED.toFixed(3), WIDTH - 125, HEIGHT - 10);
	}

	function Ring(aZ) {
		// the z field holds the distance between the object and the camera (camera is at 0)

		var ACUTAL_RADIUS = 20;

		this.z = aZ;
		this.radius = RING_INITIAL_RADIUS;
		this.center = center;
		this.angle = Math.random() * 360;
		this.angleIncrement = Math.random() * RING_MAX_ROTATION * (-1 + Math.round(Math.random()) * 2);

		switch (Math.floor(Math.random() * 8)) {
			case 0:
				this.type = new EmptyRing(this.radius, this.center, this.angle);
				break;
			case 1:
				this.type = new FanRing(this.radius, this.center, this.angle);
				break;
			case 2:
				this.type = new DoorLockRing(this.radius, this.center, this.angle);
				break;
			case 3:
				this.type = new RectangleRingSingle(this.radius, this.center, this.angle);
				break;
			case 4:
				this.type = new HalfRing(this.radius, this.center, this.angle);
				break;
			case 5:
				this.type = new HoleRing(this.radius, this.center, this.angle, 3, 0.3, 0.6);
				break;
			case 6:
				this.type = new HoleRing(this.radius, this.center, this.angle, 2, 0.35, 0.55);
				break;
			case 7:
				this.type = new HoleRing(this.radius, this.center, this.angle, 1, 0.4, 0.5);
				break;
		}

		this.draw = function() {
			this.type.draw(context);
		};

		this.act = function(aDistanceTravelled, aDelta, aKX, aKY) {
			//if the ring is beyond the camera - create a new ring to replace this one
			if (this.z < 0) {
				spawnRing(INITIAL_DISTANCE + this.z);
			}

			//calculate new z-position
			this.z -= aDistanceTravelled;
			this.radius = FIELD_OF_VIEW_CONSTANT * ( ACUTAL_RADIUS / Math.max(this.z, 1));
			this.angle += this.angleIncrement * aDelta;

			if (this.angle >= 360) {
				this.angle -= 360;
			} else if (this.angle < 0) {
				this.angle += 360;
			}

			this.type.radius = this.radius;
			this.type.angle = this.angle;

			this.type.center = center.translate(aKX * this.type.radius, aKY * this.type.radius);
			this.type.rescale();
		};
	}

	var lastTick = +Date.now();

	function render() {
		var now = +Date.now();

		var delta = now - lastTick;

		lastTick = now;
		fpsCounter.update(now);

		action(delta);
		draw();
	}

	var loop = function() {
		ticker(loop);
		render();
	};

	function mouseMoveListener(aEvent) {
		mousePosition.x = aEvent.clientX;
		mousePosition.y = aEvent.clientY;
	}

	function init() {
		spawnRing(750);
		spawnRing(1000);
		spawnRing(1250);
		spawnRing(1500);
	}

	this.start = Util.singleRun(function() {
		canvas.addEventListener('mousemove', mouseMoveListener, false);

		init();
		loop();
	});

	this.destroy = function() {
		canvas.removeEventListener('mousemove', mouseMoveListener, false);
		loop = Util.noop;
	};
}

new Game().start();
<commit_msg>tweaked tunnel ratios; first version of correctMousePosition() (not working);<commit_after>/*global window, document*/
/*jshint laxbreak:true*/

var Util = (function() {
	var PI2 = Math.PI * 2;

	return {
		PI2: PI2,

		singleRun: function(aFunction) {
			var started = false;

			return function() {
				if (!started) {
					started = true;
					aFunction.apply(this, arguments);
				}
			};
		},

		noop: function() {
		},

		degToRad: function(aDegrees) {
			return aDegrees * Math.PI / 180;
		},

		normalizeAngle: function(aAngle) {
			while (aAngle < 0) {
				aAngle += PI2;
			}

			return aAngle % PI2;
		},

		normalizeAngleSimple: function(aAngle) {
			if (aAngle < 0) {
				return aAngle + PI2;
			}

			return (aAngle > PI2) ? (aAngle - PI2) : aAngle;
		},

		clamp: function(aX, aMin, aMax) {
			return Math.min(aMax, Math.max(aMin, aX));
		}
	};
})();

function FPSCounter(aUpdateInterval, aContext) {
	var count = 0;
	var fps = '...';
	var time = +new Date();
	var modifier = 1000 / aUpdateInterval;

	var Y = aContext.canvas.height - 10;
	var X = aContext.canvas.width - 50;

	this.update = function(aTime) {
		var diff = aTime - time;

		++count;
		if (diff > aUpdateInterval) {
			fps = 'FPS: ' + Math.floor(count * modifier);

			count = 0;
			time = aTime;
		}
	};

	this.draw = function() {
		aContext.fillStyle = 'red';
		aContext.font = '10px Verdana';
		aContext.fillText(fps, X, Y);
	};
}

function penta(aX) {
	return aX * aX * aX * aX * aX;
}

function Tunnel(aContext, aCenter) {
	var kx;
	var ky;
	var rays = 6;
	var offset = 0;
	var delta = Util.degToRad(2);
	var speed = Util.degToRad(0.5);
	var rayAngle = 2 * Math.PI / rays;

	var center = new Point(aContext.canvas.width / 2, aContext.canvas.height / 2);
	var outerRadius = Math.max(aContext.canvas.height, aContext.canvas.width) * 2;
	var innerRadius = outerRadius / 50;

	this.act = function(aKX, aKY) {
		kx = aKX;
		ky = aKY;

		offset = Util.normalizeAngleSimple(offset + speed);
	};

	this.draw = function() {
		aContext.lineWidth = 1;
		aContext.fillStyle = 'purple';

		var i;
		for (i = 0; i < rays; ++i) {
			var angle = offset + (i * rayAngle);

			var angle1Point = Point.singularCirclePoint(angle);
			var angle2Point = Point.singularCirclePoint(angle + delta);

			var outerPoint1 = angle1Point.multiply(outerRadius).translate(aCenter.x + outerRadius * kx, aCenter.y + outerRadius * ky);
			var outerPoint2 = angle2Point.multiply(outerRadius).translate(aCenter.x + outerRadius * kx, aCenter.y + outerRadius * ky);

			var innerPoint1 = angle1Point.multiply(innerRadius).translate(aCenter.x + innerRadius * kx, aCenter.y + innerRadius * ky);
			var innerPoint2 = angle2Point.multiply(innerRadius).translate(aCenter.x + innerRadius * kx, aCenter.y + innerRadius * ky);

			aContext.beginPath();

			aContext.moveTo(outerPoint1.x, outerPoint1.y);
			aContext.lineTo(innerPoint1.x, innerPoint1.y);
			aContext.lineTo(innerPoint2.x, innerPoint2.y);
			aContext.lineTo(outerPoint2.x, outerPoint2.y);

			aContext.closePath();

			aContext.fill();
		}
	};
}

function Game() {
	var rings = [];
	var canvas = document.getElementById("canvas");
	var context = canvas.getContext('2d');
	var fpsCounter = new FPSCounter(1000, context);

	var WIDTH = canvas.width;
	var HEIGHT = canvas.height;
	var center = new Point(WIDTH / 2, HEIGHT / 2);
	var mousePosition = center.translate();
	var tunnel = new Tunnel(context, center);
	var MAX_SIDE = Math.max(center.x, center.y);

	var RING_INITIAL_RADIUS = 10;

	var RING_MAX_ROTATION = 60;

	var FIELD_OF_VIEW_CONSTANT = 400;

	var INITIAL_SPEED = 60;
	var SPEED = INITIAL_SPEED;

	var INITIAL_DISTANCE = 1000;
	var ACCELERATION = 5;

	var ticker = (function() {
		return window.requestAnimationFrame
			|| window.webkitRequestAnimationFrame
			|| window.mozRequestAnimationFrame
			|| window.msRequestAnimationFrame
			|| window.oRequestAnimationFrame
			|| function(callback) {
			window.setTimeout(callback, 1000 / 60);
		};
	})();

	function spawnRing(aDistance) {
		//currently the rings always spawn far away in the distance
		rings.push(new Ring(aDistance));
	}

	function action(aDelta) {
		//first convert the elapsed time from milliseconds to seconds
		var delta = aDelta / 1000;

		//calculating dinstance travelled between the last and current seconds
		var distanceTravelled = SPEED * delta + (ACCELERATION * delta * delta ) / 2;

		var w = WIDTH / 2;
		var kx = Util.clamp((w - mousePosition.x) / w, -0.7, 0.7);

		var h = HEIGHT / 2;
		var ky = Util.clamp((h - mousePosition.y) / h, -0.7, 0.7);

		for (var i = 0, len = rings.length; i < len; i++) {
			//the rings should calculate their new position on the z axis and if necessary do collision checks + destroy + spawn
			rings[i].act(distanceTravelled, delta, kx, ky);
		}
		if (rings.length > 4) {
			rings = rings.slice(rings.length - 4, rings.length);
		}
		tunnel.act(kx, ky);

		//now that the rings have moved according to the elapsed time we can calculate the base speed for the next frame
		SPEED += ACCELERATION * delta;
	}

	function drawBgr() {
		context.rect(0, 0, WIDTH, HEIGHT);
		context.fillStyle = '#FFFFFF';
		context.fill();
	}

	function draw() {
		drawBgr();
		tunnel.draw();

		for (var i = rings.length - 1; i >= 0; i--) {
			rings[i].draw();
		}

		drawSpeed();
		fpsCounter.draw();
	}

	function drawSpeed() {
		context.fillStyle = 'red';
		context.font = '10px Verdana';
		context.fillText('X:'+mousePosition.x.toFixed(2)+' Y: '+mousePosition.y.toFixed(2), WIDTH - 125, HEIGHT - 20);
		context.fillText('SPD: ' + SPEED.toFixed(3), WIDTH - 125, HEIGHT - 10);
	}

	function Ring(aZ) {
		// the z field holds the distance between the object and the camera (camera is at 0)

		var ACUTAL_RADIUS = 20;

		this.z = aZ;
		this.radius = RING_INITIAL_RADIUS;
		this.center = center;
		this.angle = Math.random() * 360;
		this.angleIncrement = Math.random() * RING_MAX_ROTATION * (-1 + Math.round(Math.random()) * 2);

		switch (Math.floor(Math.random() * 8)) {
			case 0:
				this.type = new EmptyRing(this.radius, this.center, this.angle);
				break;
			case 1:
				this.type = new FanRing(this.radius, this.center, this.angle);
				break;
			case 2:
				this.type = new DoorLockRing(this.radius, this.center, this.angle);
				break;
			case 3:
				this.type = new RectangleRingSingle(this.radius, this.center, this.angle);
				break;
			case 4:
				this.type = new HalfRing(this.radius, this.center, this.angle);
				break;
			case 5:
				this.type = new HoleRing(this.radius, this.center, this.angle, 3, 0.3, 0.6);
				break;
			case 6:
				this.type = new HoleRing(this.radius, this.center, this.angle, 2, 0.35, 0.55);
				break;
			case 7:
				this.type = new HoleRing(this.radius, this.center, this.angle, 1, 0.4, 0.5);
				break;
		}

		this.draw = function() {
			this.type.draw(context);
		};

		this.act = function(aDistanceTravelled, aDelta, aKX, aKY) {
			//if the ring is beyond the camera - create a new ring to replace this one
			if (this.z < 0) {
				spawnRing(INITIAL_DISTANCE + this.z);
			}

			//calculate new z-position
			this.z -= aDistanceTravelled;
			this.radius = FIELD_OF_VIEW_CONSTANT * ( ACUTAL_RADIUS / Math.max(this.z, 1));
			this.angle += this.angleIncrement * aDelta;

			if (this.angle >= 360) {
				this.angle -= 360;
			} else if (this.angle < 0) {
				this.angle += 360;
			}

			this.type.radius = this.radius;
			this.type.angle = this.angle;

			this.type.center = center.translate(aKX * this.type.radius, aKY * this.type.radius);
			this.type.rescale();
		};
	}

	var lastTick = +Date.now();

	function render() {
		var now = +Date.now();

		var delta = now - lastTick;

		lastTick = now;
		fpsCounter.update(now);

		action(delta);
		draw();
	}

	var loop = function() {
		ticker(loop);
		render();
	};

	function mouseMoveListener(aEvent) {
		mousePosition.x = aEvent.clientX;
		mousePosition.y = aEvent.clientY;
		//correctMousePosition();
	}

	function correctMousePosition() {
		var radius = Math.min(WIDTH, HEIGHT) / 2;
		var halfWidth = WIDTH / 2;
		var halfHeight = HEIGHT / 2;
		var wx, wy;
		var xapply, yapply;
		//first we insure that the mouse position is clamped in the square area around the circumference of the actual area of valid positions.
		if (WIDTH > HEIGHT) {
			//if the mouse position is in the left side
			if (mousePosition.x < halfWidth) {
				//if the position is outside of the square - to the left
				if (mousePosition.x < (halfWidth - radius)) {
					//set to farthest possible point
					mousePosition.x = halfWidth - radius;
					//if the mouse position is in the right side
				}
			} else {
				//if the position is outside of the square - to the right
				if (mousePosition.x > (halfWidth + radius)) {
					//set to farthest possible point
					mousePosition.x = halfWidth + radius;
				}
			}
		}
		else if (HEIGHT > WIDTH) {
			//if the mouse position is in the top side
			if (mousePosition.y < halfHeight) {
				//if the position is outside of the square - to the top
				if (mousePosition.y < (halfHeight - radius)) {
					//set to farthest possible point
					mousePosition.y = halfHeight - radius;
					//if the mouse position is in the bottom side
				} else {
					//if the position is outside of the square - to the right
					if (mousePosition.y > (halfHeight + radius)) {
						//set to farthest possible point
						mousePosition.y = halfHeight + radius;
					}
				}
			}
		}
		//if the position is in the left side
		if (mousePosition.x < halfWidth) {
			wx = halfWidth - mousePosition.x;
			//apply the new x point by subtracting from halfWidth
			xapply = -1;
			//if the position is in the bottom side
		} else {
			wx = mousePosition.x - halfWidth;
			//apply the new x point by adding to halfWidth
			xapply = 1;
		}
		//if the position is in the top side
		if (mousePosition.y < halfHeight) {
			wy = halfHeight - mousePosition.y;
			//apply the new y point by subtracting from halfHeight
			yapply = -1;
		} else {
			wy = mousePosition.y - halfHeight;
			//apply the new y point by adding to halfHeight
			yapply = 1;
		}

		var wsqx = wx * wx, wsqy = wy * wy, rsq = radius * radius;
		//if this point is outside of the circle
		if (wsqx + wsqy > rsq) {
			var newWx = Math.sqrt(rsq - wsqy);
			var newWy = Math.sqrt(rsq - wsqx);
			//if the x difference is smaller than the y difference...
			if ((wx - newWx) < (wy - newWy)) {
				//...we should apply the x difference for better accuracy
				mousePosition.x = halfWidth + newWx * xapply;
			} else {
				//...we should apply the y difference for better accuracy
				mousePosition.y = halfHeight + newWy * yapply;
			}
		}
	}

	function init() {
		spawnRing(750);
		spawnRing(1000);
		spawnRing(1250);
		spawnRing(1500);
	}

	this.start = Util.singleRun(function() {
		canvas.addEventListener('mousemove', mouseMoveListener, false);

		init();
		loop();
	});

	this.destroy = function() {
		canvas.removeEventListener('mousemove', mouseMoveListener, false);
		loop = Util.noop;
	};
}

new Game().start();
<|endoftext|>


<commit_before>(function () {
  "use strict";

  angular
    .module('mnHttp', ["mnPendingQueryKeeper"])
    .factory('mnHttpInterceptor', mnHttpFactory)
    .config(function ($httpProvider) {
      $httpProvider.interceptors.push('mnHttpInterceptor');
    });

  function mnHttpFactory(mnPendingQueryKeeper, $q, $httpParamSerializerJQLike, $timeout) {
    var myHttpInterceptor = {
      request: request,
      response: response
    };

    return myHttpInterceptor;

    function request(config) {
      if (config.url.indexOf(".html") !== -1 || config.doNotIntercept) {
        return config;
      } else {
        return intercept(config);
      }
    }
    function intercept(config) {
      var pendingQuery = {
        config: _.clone(config)
      };
      var mnHttpConfig = config.mnHttp || {};
      delete config.mnHttp;
      if (config.method.toLowerCase() === "post" && mnHttpConfig.cancelPrevious) {
        var queryInFly = mnPendingQueryKeeper.getQueryInFly(config);
        queryInFly && queryInFly.canceler();
      }
      var canceler = $q.defer();
      var timeoutID;
      var timeout = config.timeout;
      var isCleared;

      function clear() {
        if (isCleared) {
          return;
        }
        isCleared = true;
        timeoutID && $timeout.cancel(timeoutID);
        mnPendingQueryKeeper.removeQueryInFly(pendingQuery);
      }

      function cancel(reason) {
        return function () {
          canceler.resolve(reason);
          clear();
        };
      }

      switch (config.method.toLowerCase()) {
        case 'post':
        case 'put':
          config.headers = config.headers || {};
          if (!mnHttpConfig.isNotForm) {
            config.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
            if (!angular.isString(config.data)) {
              config.data = $httpParamSerializerJQLike(config.data);
            }
          }
        break;
      }

      config.timeout = canceler.promise;
      config.clear = clear;

      pendingQuery.canceler = cancel("cancelled");
      pendingQuery.group = mnHttpConfig.group;
      mnPendingQueryKeeper.push(pendingQuery);

      if (timeout) {
        timeoutID = $timeout(cancel("timeout"), timeout);
      }
      return config;
    }

    function response(response) {
      if (response.config.clear && angular.isFunction(response.config.clear)) {
        response.config.clear();
        delete response.config.clear;
      }
      return response;
    }
  }

})();
<commit_msg>remove unsuccessful queries from mnPendingQueryKeeper<commit_after>(function () {
  "use strict";

  angular
    .module('mnHttp', ["mnPendingQueryKeeper"])
    .factory('mnHttpInterceptor', mnHttpFactory)
    .config(function ($httpProvider) {
      $httpProvider.interceptors.push('mnHttpInterceptor');
    });

  function mnHttpFactory(mnPendingQueryKeeper, $q, $httpParamSerializerJQLike, $timeout) {
    var myHttpInterceptor = {
      request: request,
      response: response,
      responseError: responseError
    };

    return myHttpInterceptor;

    function request(config) {
      if (config.url.indexOf(".html") !== -1 || config.doNotIntercept) {
        return config;
      } else {
        return intercept(config);
      }
    }
    function intercept(config) {
      var pendingQuery = {
        config: _.clone(config)
      };
      var mnHttpConfig = config.mnHttp || {};
      delete config.mnHttp;
      if (config.method.toLowerCase() === "post" && mnHttpConfig.cancelPrevious) {
        var queryInFly = mnPendingQueryKeeper.getQueryInFly(config);
        queryInFly && queryInFly.canceler();
      }
      var canceler = $q.defer();
      var timeoutID;
      var timeout = config.timeout;
      var isCleared;

      function clear() {
        if (isCleared) {
          return;
        }
        isCleared = true;
        timeoutID && $timeout.cancel(timeoutID);
        mnPendingQueryKeeper.removeQueryInFly(pendingQuery);
      }

      function cancel(reason) {
        return function () {
          canceler.resolve(reason);
          clear();
        };
      }

      switch (config.method.toLowerCase()) {
        case 'post':
        case 'put':
          config.headers = config.headers || {};
          if (!mnHttpConfig.isNotForm) {
            config.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
            if (!angular.isString(config.data)) {
              config.data = $httpParamSerializerJQLike(config.data);
            }
          }
        break;
      }

      config.timeout = canceler.promise;
      config.clear = clear;

      pendingQuery.canceler = cancel("cancelled");
      pendingQuery.group = mnHttpConfig.group;
      mnPendingQueryKeeper.push(pendingQuery);

      if (timeout) {
        timeoutID = $timeout(cancel("timeout"), timeout);
      }
      return config;
    }

    function clearOnResponse(response) {
      if (response.config.clear && angular.isFunction(response.config.clear)) {
        response.config.clear();
        delete response.config.clear;
      }
    }

    function response(response) {
      clearOnResponse(response);
      return response;
    }
    function responseError(response) {
      clearOnResponse(response);
      return $q.reject(response);
    }
  }

})();
<|endoftext|>


<commit_before>import { Object as EmberObject, A } from '@ember/-internals/runtime';
import {
  EMBER_CUSTOM_COMPONENT_ARG_PROXY,
  EMBER_METAL_TRACKED_PROPERTIES,
} from '@ember/canary-features';
import { tracked, nativeDescDecorator as descriptor } from '@ember/-internals/metal';
import { moduleFor, RenderingTestCase, strip, runTask } from 'internal-test-helpers';
import GlimmerishComponent from '../../utils/glimmerish-component';
import { Component } from '../../utils/helpers';

if (EMBER_METAL_TRACKED_PROPERTIES) {
  moduleFor(
    'Component Tracked Properties',
    class extends RenderingTestCase {
      '@test simple test using glimmerish component'() {
        let personId = 0;
        class Person {
          @tracked first;
          @tracked last;

          constructor(first, last) {
            this.id = personId++;
            this.first = first;
            this.last = last;
          }
        }

        class PersonComponent extends GlimmerishComponent {
          get person() {
            return new Person(this.args.first, this.args.last);
          }
        }

        this.registerComponent('person-wrapper', {
          ComponentClass: PersonComponent,
          template: '{{@first}} {{@last}} | {{this.person.first}} {{this.person.last}}',
        });

        this.render('<PersonWrapper @first={{first}} @last={{last}} />', {
          first: 'robert',
          last: 'jackson',
        });

        this.assertText('robert jackson | robert jackson');

        runTask(() => this.context.set('first', 'max'));
        this.assertText('max jackson | max jackson');
      }

      '@test tracked properties that are uninitialized do not throw an error'() {
        let CountComponent = Component.extend({
          count: tracked(),

          increment() {
            if (!this.count) {
              this.count = 0;
            }
            this.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test tracked properties rerender when updated'() {
        let CountComponent = Component.extend({
          count: tracked({ value: 0 }),

          increment() {
            this.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test tracked properties rerender when updated outside of a runloop'(assert) {
        let done = assert.async();

        let CountComponent = Component.extend({
          count: tracked({ value: 0 }),

          increment() {
            setTimeout(() => {
              this.count++;
            }, 100);
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        // intentionally outside of a runTask
        this.$('button').click();

        setTimeout(() => {
          this.assertText('1');
          done();
        }, 200);
      }

      '@test nested tracked properties rerender when updated'() {
        let Counter = EmberObject.extend({
          count: tracked({ value: 0 }),
        });

        let CountComponent = Component.extend({
          counter: Counter.create(),

          increment() {
            this.counter.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.counter.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test array properties rerender when updated'() {
        let NumListComponent = Component.extend({
          numbers: tracked({ initializer: () => A([1, 2, 3]) }),

          addNumber() {
            this.numbers.pushObject(4);
          },
        });

        this.registerComponent('num-list', {
          ComponentClass: NumListComponent,
          template: strip`
            <button {{action this.addNumber}}>
              {{#each this.numbers as |num|}}{{num}}{{/each}}
            </button>
          `,
        });

        this.render('<NumList />');

        this.assertText('123');

        runTask(() => this.$('button').click());

        this.assertText('1234');
      }

      '@test getters update when dependent properties are invalidated'() {
        let CountComponent = Component.extend({
          count: tracked({ value: 0 }),

          countAlias: descriptor({
            get() {
              return this.count;
            },
          }),

          increment() {
            this.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.countAlias}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test nested getters update when dependent properties are invalidated'() {
        let Counter = EmberObject.extend({
          count: tracked({ value: 0 }),

          countAlias: descriptor({
            get() {
              return this.count;
            },
          }),
        });

        let CountComponent = Component.extend({
          counter: Counter.create(),

          increment() {
            this.counter.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.counter.countAlias}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test tracked object passed down through components updates correctly'(assert) {
        let Person = EmberObject.extend({
          first: tracked({ value: 'Rob' }),
          last: tracked({ value: 'Jackson' }),

          full: descriptor({
            get() {
              return `${this.first} ${this.last}`;
            },
          }),
        });

        let ParentComponent = Component.extend({
          person: Person.create(),
        });

        let ChildComponent = Component.extend({
          updatePerson() {
            this.person.first = 'Kris';
            this.person.last = 'Selden';
          },
        });

        this.registerComponent('parent', {
          ComponentClass: ParentComponent,
          template: strip`

{{this.person.full}}

            <Child @person={{this.person}}/>
          `,
        });

        this.registerComponent('child', {
          ComponentClass: ChildComponent,
          template: strip`

{{this.person.full}}

            <button onclick={{action this.updatePerson}}></button>
          `,
        });

        this.render('<Parent />');

        assert.equal(this.$('#parent').text(), 'Rob Jackson');
        assert.equal(this.$('#child').text(), 'Rob Jackson');

        runTask(() => this.$('button').click());

        assert.equal(this.$('#parent').text(), 'Kris Selden');
        assert.equal(this.$('#child').text(), 'Kris Selden');
      }

      '@test yielded getters update correctly'() {
        let PersonComponent = Component.extend({
          first: tracked({ value: 'Rob' }),
          last: tracked({ value: 'Jackson' }),

          full: descriptor({
            get() {
              return `${this.first} ${this.last}`;
            },
          }),

          updatePerson() {
            this.first = 'Kris';
            this.last = 'Selden';
          },
        });

        this.registerComponent('person', {
          ComponentClass: PersonComponent,
          template: strip`
            {{yield this.full (action this.updatePerson)}}
          `,
        });

        this.render(strip`
          <Person as |name update|>
            <button onclick={{update}}>
              {{name}}
            </button>
          </Person>
        `);

        this.assertText('Rob Jackson');

        runTask(() => this.$('button').click());

        this.assertText('Kris Selden');
      }

      '@test yielded nested getters update correctly'() {
        let Person = EmberObject.extend({
          first: tracked({ value: 'Rob' }),
          last: tracked({ value: 'Jackson' }),

          full: descriptor({
            get() {
              return `${this.first} ${this.last}`;
            },
          }),
        });

        let PersonComponent = Component.extend({
          person: Person.create(),

          updatePerson() {
            this.person.first = 'Kris';
            this.person.last = 'Selden';
          },
        });

        this.registerComponent('person', {
          ComponentClass: PersonComponent,
          template: strip`
            {{yield this.person (action this.updatePerson)}}
          `,
        });

        this.render(strip`
          <Person as |p update|>
            <button onclick={{update}}>
              {{p.full}}
            </button>
          </Person>
        `);

        this.assertText('Rob Jackson');

        runTask(() => this.$('button').click());

        this.assertText('Kris Selden');
      }
    }
  );

  if (EMBER_CUSTOM_COMPONENT_ARG_PROXY) {
    moduleFor(
      'Component Tracked Properties w/ Args Proxy',
      class extends RenderingTestCase {
        '@test downstream property changes do not invalidate upstream component getters/arguments'(
          assert
        ) {
          let outerRenderCount = 0;
          let innerRenderCount = 0;

          class OuterComponent extends GlimmerishComponent {
            get count() {
              outerRenderCount++;
              return this.args.count;
            }
          }

          class InnerComponent extends GlimmerishComponent {
            @tracked count = 0;

            get combinedCounts() {
              innerRenderCount++;
              return this.args.count + this.count;
            }

            updateInnerCount() {
              this.count++;
            }
          }

          this.registerComponent('outer', {
            ComponentClass: OuterComponent,
            template: '<Inner @count={{this.count}}/>',
          });

          this.registerComponent('inner', {
            ComponentClass: InnerComponent,
            template: '<button {{action this.updateInnerCount}}>{{this.combinedCounts}}</button>',
          });

          this.render('<Outer @count={{this.count}}/>', {
            count: 0,
          });

          this.assertText('0');

          assert.equal(outerRenderCount, 1);
          assert.equal(innerRenderCount, 1);

          runTask(() => this.$('button').click());

          this.assertText('1');

          assert.equal(
            outerRenderCount,
            1,
            'updating inner component does not cause outer component to rerender'
          );
          assert.equal(
            innerRenderCount,
            2,
            'updating inner component causes inner component to rerender'
          );

          runTask(() => this.context.set('count', 1));

          this.assertText('2');

          assert.equal(outerRenderCount, 2, 'outer component updates based on context');
          assert.equal(innerRenderCount, 3, 'inner component updates based on outer component');
        }
      }
    );
  }
}
<commit_msg>[CLEANUP] Adds a test for tracked/computed interop<commit_after>import { Object as EmberObject, A } from '@ember/-internals/runtime';
import {
  EMBER_CUSTOM_COMPONENT_ARG_PROXY,
  EMBER_METAL_TRACKED_PROPERTIES,
} from '@ember/canary-features';
import { computed, tracked, nativeDescDecorator as descriptor } from '@ember/-internals/metal';
import { moduleFor, RenderingTestCase, strip, runTask } from 'internal-test-helpers';
import GlimmerishComponent from '../../utils/glimmerish-component';
import { Component } from '../../utils/helpers';

if (EMBER_METAL_TRACKED_PROPERTIES) {
  moduleFor(
    'Component Tracked Properties',
    class extends RenderingTestCase {
      '@test simple test using glimmerish component'() {
        let personId = 0;
        class Person {
          @tracked first;
          @tracked last;

          constructor(first, last) {
            this.id = personId++;
            this.first = first;
            this.last = last;
          }
        }

        class PersonComponent extends GlimmerishComponent {
          get person() {
            return new Person(this.args.first, this.args.last);
          }
        }

        this.registerComponent('person-wrapper', {
          ComponentClass: PersonComponent,
          template: '{{@first}} {{@last}} | {{this.person.first}} {{this.person.last}}',
        });

        this.render('<PersonWrapper @first={{first}} @last={{last}} />', {
          first: 'robert',
          last: 'jackson',
        });

        this.assertText('robert jackson | robert jackson');

        runTask(() => this.context.set('first', 'max'));
        this.assertText('max jackson | max jackson');
      }

      '@test tracked properties that are uninitialized do not throw an error'() {
        let CountComponent = Component.extend({
          count: tracked(),

          increment() {
            if (!this.count) {
              this.count = 0;
            }
            this.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test tracked properties rerender when updated'() {
        let CountComponent = Component.extend({
          count: tracked({ value: 0 }),

          increment() {
            this.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test tracked properties rerender when updated outside of a runloop'(assert) {
        let done = assert.async();

        let CountComponent = Component.extend({
          count: tracked({ value: 0 }),

          increment() {
            setTimeout(() => {
              this.count++;
            }, 100);
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        // intentionally outside of a runTask
        this.$('button').click();

        setTimeout(() => {
          this.assertText('1');
          done();
        }, 200);
      }

      '@test nested tracked properties rerender when updated'() {
        let Counter = EmberObject.extend({
          count: tracked({ value: 0 }),
        });

        let CountComponent = Component.extend({
          counter: Counter.create(),

          increment() {
            this.counter.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.counter.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test array properties rerender when updated'() {
        let NumListComponent = Component.extend({
          numbers: tracked({ initializer: () => A([1, 2, 3]) }),

          addNumber() {
            this.numbers.pushObject(4);
          },
        });

        this.registerComponent('num-list', {
          ComponentClass: NumListComponent,
          template: strip`
            <button {{action this.addNumber}}>
              {{#each this.numbers as |num|}}{{num}}{{/each}}
            </button>
          `,
        });

        this.render('<NumList />');

        this.assertText('123');

        runTask(() => this.$('button').click());

        this.assertText('1234');
      }

      '@test getters update when dependent properties are invalidated'() {
        let CountComponent = Component.extend({
          count: tracked({ value: 0 }),

          countAlias: descriptor({
            get() {
              return this.count;
            },
          }),

          increment() {
            this.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.countAlias}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test getters update when dependent computeds are invalidated'() {
        let CountComponent = Component.extend({
          _count: 0,

          count: computed({
            get() {
              return this._count;
            },

            set(key, value) {
              return (this._count = value);
            },
          }),

          get countAlias() {
            return this.count;
          },

          increment() {
            this.set('count', this.count + 1);
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.countAlias}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test nested getters update when dependent properties are invalidated'() {
        let Counter = EmberObject.extend({
          count: tracked({ value: 0 }),

          countAlias: descriptor({
            get() {
              return this.count;
            },
          }),
        });

        let CountComponent = Component.extend({
          counter: Counter.create(),

          increment() {
            this.counter.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.counter.countAlias}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test tracked object passed down through components updates correctly'(assert) {
        let Person = EmberObject.extend({
          first: tracked({ value: 'Rob' }),
          last: tracked({ value: 'Jackson' }),

          full: descriptor({
            get() {
              return `${this.first} ${this.last}`;
            },
          }),
        });

        let ParentComponent = Component.extend({
          person: Person.create(),
        });

        let ChildComponent = Component.extend({
          updatePerson() {
            this.person.first = 'Kris';
            this.person.last = 'Selden';
          },
        });

        this.registerComponent('parent', {
          ComponentClass: ParentComponent,
          template: strip`

{{this.person.full}}

            <Child @person={{this.person}}/>
          `,
        });

        this.registerComponent('child', {
          ComponentClass: ChildComponent,
          template: strip`

{{this.person.full}}

            <button onclick={{action this.updatePerson}}></button>
          `,
        });

        this.render('<Parent />');

        assert.equal(this.$('#parent').text(), 'Rob Jackson');
        assert.equal(this.$('#child').text(), 'Rob Jackson');

        runTask(() => this.$('button').click());

        assert.equal(this.$('#parent').text(), 'Kris Selden');
        assert.equal(this.$('#child').text(), 'Kris Selden');
      }

      '@test yielded getters update correctly'() {
        let PersonComponent = Component.extend({
          first: tracked({ value: 'Rob' }),
          last: tracked({ value: 'Jackson' }),

          full: descriptor({
            get() {
              return `${this.first} ${this.last}`;
            },
          }),

          updatePerson() {
            this.first = 'Kris';
            this.last = 'Selden';
          },
        });

        this.registerComponent('person', {
          ComponentClass: PersonComponent,
          template: strip`
            {{yield this.full (action this.updatePerson)}}
          `,
        });

        this.render(strip`
          <Person as |name update|>
            <button onclick={{update}}>
              {{name}}
            </button>
          </Person>
        `);

        this.assertText('Rob Jackson');

        runTask(() => this.$('button').click());

        this.assertText('Kris Selden');
      }

      '@test yielded nested getters update correctly'() {
        let Person = EmberObject.extend({
          first: tracked({ value: 'Rob' }),
          last: tracked({ value: 'Jackson' }),

          full: descriptor({
            get() {
              return `${this.first} ${this.last}`;
            },
          }),
        });

        let PersonComponent = Component.extend({
          person: Person.create(),

          updatePerson() {
            this.person.first = 'Kris';
            this.person.last = 'Selden';
          },
        });

        this.registerComponent('person', {
          ComponentClass: PersonComponent,
          template: strip`
            {{yield this.person (action this.updatePerson)}}
          `,
        });

        this.render(strip`
          <Person as |p update|>
            <button onclick={{update}}>
              {{p.full}}
            </button>
          </Person>
        `);

        this.assertText('Rob Jackson');

        runTask(() => this.$('button').click());

        this.assertText('Kris Selden');
      }
    }
  );

  if (EMBER_CUSTOM_COMPONENT_ARG_PROXY) {
    moduleFor(
      'Component Tracked Properties w/ Args Proxy',
      class extends RenderingTestCase {
        '@test downstream property changes do not invalidate upstream component getters/arguments'(
          assert
        ) {
          let outerRenderCount = 0;
          let innerRenderCount = 0;

          class OuterComponent extends GlimmerishComponent {
            get count() {
              outerRenderCount++;
              return this.args.count;
            }
          }

          class InnerComponent extends GlimmerishComponent {
            @tracked count = 0;

            get combinedCounts() {
              innerRenderCount++;
              return this.args.count + this.count;
            }

            updateInnerCount() {
              this.count++;
            }
          }

          this.registerComponent('outer', {
            ComponentClass: OuterComponent,
            template: '<Inner @count={{this.count}}/>',
          });

          this.registerComponent('inner', {
            ComponentClass: InnerComponent,
            template: '<button {{action this.updateInnerCount}}>{{this.combinedCounts}}</button>',
          });

          this.render('<Outer @count={{this.count}}/>', {
            count: 0,
          });

          this.assertText('0');

          assert.equal(outerRenderCount, 1);
          assert.equal(innerRenderCount, 1);

          runTask(() => this.$('button').click());

          this.assertText('1');

          assert.equal(
            outerRenderCount,
            1,
            'updating inner component does not cause outer component to rerender'
          );
          assert.equal(
            innerRenderCount,
            2,
            'updating inner component causes inner component to rerender'
          );

          runTask(() => this.context.set('count', 1));

          this.assertText('2');

          assert.equal(outerRenderCount, 2, 'outer component updates based on context');
          assert.equal(innerRenderCount, 3, 'inner component updates based on outer component');
        }
      }
    );
  }
}
<|endoftext|>


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

(function(tree) {

tree.Selector = function Selector(filters, zoom, elements, attachment, conditions, index) {
    this.elements = elements || [];
    this.attachment = attachment;
    this.filters = filters || {};
    this.zoom = typeof zoom !== 'undefined' ? zoom : tree.Zoom.all;
    this.conditions = conditions;
    this.index = index;
};

tree.Selector.prototype.fid = function() {
    var filters = Object.keys(this.filters);
    filters.sort();
    return filters.join('#');
};

tree.Selector.prototype.debug = function() {
    var num = this.index < 10 ? '   ' + this.index :
                this.index < 100 ? '  ' + this.index :
                this.index < 1000 ? ' ' + this.index :
                this.index;

    var str = "[" + num + "] " + this.elements.join('');
    if (this.attachment) str += '::' + this.attachment;
    str += ': Zoom[' + tree.Zoom.toString(this.zoom) + '] ';
    var filters = Object.keys(this.filters);
    filters.sort();
    str += (filters.length ? '[' : '') + filters.join('] [') + (filters.length ? ']' : '');
    return str;
};

tree.Selector.prototype.sound = function() {
    if (!this.zoom) return false;
    return tree.Filter.sound(this.filters);
};

tree.Selector.prototype.clone = function() {
    var clone = Object.create(tree.Selector.prototype);
    if (this.elements) {
        // Do a full clone.
        clone.elements = this.elements.slice();
        if (this.attachment) clone.attachment = this.attachment;
        clone.conditions = this.conditions;
        clone.index = this.index;
    }
    clone.filters = {};
    for (var id in this.filters) { clone.filters[id] = this.filters[id]; }
    clone.zoom = this.zoom;
    return clone;
};

tree.Selector.prototype.cloneMerge = function(other) {
    var zoom = this.zoom & other.zoom;
    if (!zoom) return;

    var filters = {};
    for (var id in this.filters) filters[id] = this.filters[id];
    for (var id in other.filters) filters[id] = other.filters[id];
    if (!tree.Filter.sound(filters)) return;

    // Create a lightweight object.
    var clone = Object.create(tree.Selector.prototype);
    clone.filters = filters;
    clone.zoom = zoom;
    return clone;
};

tree.Selector.prototype.negate = function() {
    var clone = Object.create(tree.Selector.prototype);
    if (this.elements) {
        // Do a full clone.
        clone.elements = this.elements.slice();
        clone.attachment = this.attachment;
        clone.conditions = this.conditions;
        clone.index = this.index;
    }
    clone.filters = {};
    for (var id in this.filters) {
        var negated = this.filters[id].negate();
        clone.filters[negated.id] = negated;
    }
    clone.zoom = ~this.zoom;
    return clone;
};

/**
 * Determine the specificity of this selector
 * based on the specificity of its elements - calling
 * Element.specificity() in order to do so
 *
 * [ID, Class, Filters, Position in document]
 */
tree.Selector.prototype.specificity = function() {
    return this.elements.reduce(function(memo, e) {
        var spec = e.specificity();
        memo[0] += spec[0];
        memo[1] += spec[1];
        return memo;
    }, [0, 0, this.conditions, this.index]);
};

/**
 * Determine whether this selector matches a given id
 * and array of classes, by determining whether
 * all elements it contains match.
 */
tree.Selector.prototype.matches = function(id, classes) {
    for (var i = 0; i < this.elements.length; i++) {
        if (!this.elements[i].matches(id, classes)) {
            return false;
        }
    }
    return true;
};

tree.Selector.prototype.layers = function(env) {
    return this.elements.map(function(f) {
        return f.value;
    }).join(' ');
};

tree.Selector.prototype.combinedFilter = function(env) {
    var conditions = tree.Zoom.toXML(this.zoom);

    var filters = [];
    for (var id in this.filters) {
        filters.push('(' + this.filters[id].toXML(env).trim() + ')');
    }

    if (filters.length) {
        conditions.push('<Filter>' + filters.join(' and ') + '</Filter>');
    }

    if (conditions.length) {
        return conditions.join('\n    ');
    } else {
        return '';
    }
};

})(require('mess/tree'));
<commit_msg>updated debug function to accomodate for light selectors<commit_after>var assert = require('assert');

(function(tree) {

tree.Selector = function Selector(filters, zoom, elements, attachment, conditions, index) {
    this.elements = elements || [];
    this.attachment = attachment;
    this.filters = filters || {};
    this.zoom = typeof zoom !== 'undefined' ? zoom : tree.Zoom.all;
    this.conditions = conditions;
    this.index = index;
};

tree.Selector.prototype.fid = function() {
    var filters = Object.keys(this.filters);
    filters.sort();
    return filters.join('#');
};

tree.Selector.prototype.debug = function() {
    var str = 'Zoom[' + tree.Zoom.toString(this.zoom) + '] ';
    var filters = Object.keys(this.filters);
    filters.sort();
    str += (filters.length ? '[' : '') + filters.join('] [') + (filters.length ? ']' : '');
    return str;
};

tree.Selector.prototype.sound = function() {
    if (!this.zoom) return false;
    return tree.Filter.sound(this.filters);
};

tree.Selector.prototype.clone = function() {
    var clone = Object.create(tree.Selector.prototype);
    if (this.elements) {
        // Do a full clone.
        clone.elements = this.elements.slice();
        if (this.attachment) clone.attachment = this.attachment;
        clone.conditions = this.conditions;
        clone.index = this.index;
    }
    clone.filters = {};
    for (var id in this.filters) { clone.filters[id] = this.filters[id]; }
    clone.zoom = this.zoom;
    return clone;
};

tree.Selector.prototype.cloneMerge = function(other) {
    var zoom = this.zoom & other.zoom;
    if (!zoom) return;

    var filters = {};
    for (var id in this.filters) filters[id] = this.filters[id];
    for (var id in other.filters) filters[id] = other.filters[id];
    if (!tree.Filter.sound(filters)) return;

    // Create a lightweight object.
    var clone = Object.create(tree.Selector.prototype);
    clone.filters = filters;
    clone.zoom = zoom;
    return clone;
};

tree.Selector.prototype.negate = function() {
    var clone = Object.create(tree.Selector.prototype);
    if (this.elements) {
        // Do a full clone.
        clone.elements = this.elements.slice();
        clone.attachment = this.attachment;
        clone.conditions = this.conditions;
        clone.index = this.index;
    }
    clone.filters = {};
    for (var id in this.filters) {
        var negated = this.filters[id].negate();
        clone.filters[negated.id] = negated;
    }
    clone.zoom = ~this.zoom;
    return clone;
};

/**
 * Determine the specificity of this selector
 * based on the specificity of its elements - calling
 * Element.specificity() in order to do so
 *
 * [ID, Class, Filters, Position in document]
 */
tree.Selector.prototype.specificity = function() {
    return this.elements.reduce(function(memo, e) {
        var spec = e.specificity();
        memo[0] += spec[0];
        memo[1] += spec[1];
        return memo;
    }, [0, 0, this.conditions, this.index]);
};

/**
 * Determine whether this selector matches a given id
 * and array of classes, by determining whether
 * all elements it contains match.
 */
tree.Selector.prototype.matches = function(id, classes) {
    for (var i = 0; i < this.elements.length; i++) {
        if (!this.elements[i].matches(id, classes)) {
            return false;
        }
    }
    return true;
};

tree.Selector.prototype.layers = function(env) {
    return this.elements.map(function(f) {
        return f.value;
    }).join(' ');
};

tree.Selector.prototype.combinedFilter = function(env) {
    var conditions = tree.Zoom.toXML(this.zoom);

    var filters = [];
    for (var id in this.filters) {
        filters.push('(' + this.filters[id].toXML(env).trim() + ')');
    }

    if (filters.length) {
        conditions.push('<Filter>' + filters.join(' and ') + '</Filter>');
    }

    if (conditions.length) {
        return conditions.join('\n    ');
    } else {
        return '';
    }
};

})(require('mess/tree'));
<|endoftext|>


<commit_before>'use strict';

var map = require("lodash.map");
var merge = require("lodash.merge");
var pick = require("lodash.pick");
var button = require("./button");

// @param {Object} element
// @returns {Object}
function genericElement(element) {
  return element;
};

// @param {Object} element
// @returns {Object}
function listElement(element) {
  return element;
};

// @see https://docs.dialoganalytics.com/reference/message/#button-template
// @see https://developers.facebook.com/docs/messenger-platform/send-api-reference/button-template
// @param {Object} message
// @returns {Object}
function buttonTemplate(message) {
  var template = message.attachment.payload;

  return {
    message: {
      mtype: 'template',
      properties: {
        type: template.template_type,
        text: template.text,
        buttons: map(template.buttons, button)
      }
    }
  };
};

// @see https://docs.dialoganalytics.com/reference/message/#generic-template
// @see https://developers.facebook.com/docs/messenger-platform/send-api-reference/generic-template
// @param {Object} message
// @returns {Object}
function genericTemplate(message) {
  var template = message.attachment.payload;

  return {
    message: {
      mtype: 'template',
      properties: {
        type: template.template_type,
        elements: map(template.elements, genericElement)
      }
    }
  };
};

// @see https://docs.dialoganalytics.com/reference/message/#list-template
// @see https://developers.facebook.com/docs/messenger-platform/send-api-reference/list-template
// @param {Object} message
// @returns {Object}
function listTemplate(message) {
  var template = message.attachment.payload;

  return {
    message: {
      mtype: 'template',
      properties: {
        type: template.template_type,
        buttons: map(template.buttons, button),
        elements: map(template.elements, listElement)
      }
    }
  };
};

// @see https://docs.dialoganalytics.com/reference/message/#receipt-template
// @see https://developers.facebook.com/docs/messenger-platform/send-api-reference/receipt-template
// @param {Object} message
// @returns {Object}
function receiptTemplate(message) {
  var template = message.attachment.payload;

  var attributes = ['recipient_name', 'order_number', 'currency', 'payment_method',
    'order_url', 'timestamp', 'elements', 'address', 'summary', 'adjustments'];

  return {
    message: {
      mtype: 'template',
      properties: merge({ type: template.template_type }, pick(template, attributes))
    }
  };
};

// @param {Object} message
// @returns {Object}
function templateMessage(message) {
  var type = message.attachment.payload.template_type;

  if (type == 'button') {
    return buttonTemplate(message);
  } else if(type == 'generic') {
    return genericTemplate(message);
  } else if(type == 'list') {
    return listTemplate(message);
  } else if(type == 'receipt') {
    return receiptTemplate(message);
  } else {
    console.log("Unknown template type: " + type)
    return {};
  }
}

// Handles image, audio, video and file attachments
// @param {Object} message
// @returns {Object}
function attachmentMessage(message) {
  return {
    message: {
      mtype: data.message.attachment.type,
      properties: {
        url: data.message.attachment.payload.url
      }
    }
  };
}

// @param {Object} message
// @returns {Object}
function textMessage(message) {
  return {
    message: {
      mtype: 'text',
      properties: {
        text: message.text,
        quick_replies: message.quick_replies
      }
    }
  };
}

// @param {Object} data
// @returns {Object}
function senderActionMessage(data) {
  // TODO
  // return {
  //   message: {
  //     mtype: ,
  //     properties: {}
  // };
}

// Outgoing payload for Dialog API
// @param {Object} event
// @param {String} botId
// @returns {Object}
function outgoingPayload(event, botId) {
  return {
    message: {
      distinct_id: event.message_id,
      platform: 'messenger',
      provider: 'dialog-node',
      mtype: null,
      sent_at: new Date().getTime() / 1000,
      properties: {}
    },
    conversation: {
      distinct_id: event.recipient_id + '-' + botId
    },
    creator: {
      distinct_id: botId,
      type: 'bot'
    }
  };
}

// @param {Object} client
// @param {Object} data Payload sent to Facebook Send API
// @param {Object} event Response from Facebook API
// @param {Function} callback
// @returns {Object, Null}
function outgoing(client, data, event, callback) {
  var payload = null;

  if (data.message) {
    var message = data.message;

    if (message.attachment) {
      if(message.attachment.type == 'template') {
        payload = templateMessage(message);
      } else {
        payload = attachmentMessage(message);
      }
    } else if (message.text) {
      payload = textMessage(message);
    }
  } else if (data.sender_action) {
    // payload = senderActionMessage(data) TODO
  } else {
    console.log("Unknown data: ", data);
  }

  var response = null;
  if (payload) {
    response = client.track(merge(outgoingPayload(event, client.botId), payload), callback);
  }

  return response;
}

module.exports = outgoing;
<commit_msg>Fix variable<commit_after>'use strict';

var map = require("lodash.map");
var merge = require("lodash.merge");
var pick = require("lodash.pick");
var button = require("./button");

// @param {Object} element
// @returns {Object}
function genericElement(element) {
  return element;
};

// @param {Object} element
// @returns {Object}
function listElement(element) {
  return element;
};

// @see https://docs.dialoganalytics.com/reference/message/#button-template
// @see https://developers.facebook.com/docs/messenger-platform/send-api-reference/button-template
// @param {Object} message
// @returns {Object}
function buttonTemplate(message) {
  var template = message.attachment.payload;

  return {
    message: {
      mtype: 'template',
      properties: {
        type: template.template_type,
        text: template.text,
        buttons: map(template.buttons, button)
      }
    }
  };
};

// @see https://docs.dialoganalytics.com/reference/message/#generic-template
// @see https://developers.facebook.com/docs/messenger-platform/send-api-reference/generic-template
// @param {Object} message
// @returns {Object}
function genericTemplate(message) {
  var template = message.attachment.payload;

  return {
    message: {
      mtype: 'template',
      properties: {
        type: template.template_type,
        elements: map(template.elements, genericElement)
      }
    }
  };
};

// @see https://docs.dialoganalytics.com/reference/message/#list-template
// @see https://developers.facebook.com/docs/messenger-platform/send-api-reference/list-template
// @param {Object} message
// @returns {Object}
function listTemplate(message) {
  var template = message.attachment.payload;

  return {
    message: {
      mtype: 'template',
      properties: {
        type: template.template_type,
        buttons: map(template.buttons, button),
        elements: map(template.elements, listElement)
      }
    }
  };
};

// @see https://docs.dialoganalytics.com/reference/message/#receipt-template
// @see https://developers.facebook.com/docs/messenger-platform/send-api-reference/receipt-template
// @param {Object} message
// @returns {Object}
function receiptTemplate(message) {
  var template = message.attachment.payload;

  var attributes = ['recipient_name', 'order_number', 'currency', 'payment_method',
    'order_url', 'timestamp', 'elements', 'address', 'summary', 'adjustments'];

  return {
    message: {
      mtype: 'template',
      properties: merge({ type: template.template_type }, pick(template, attributes))
    }
  };
};

// @param {Object} message
// @returns {Object}
function templateMessage(message) {
  var type = message.attachment.payload.template_type;

  if (type == 'button') {
    return buttonTemplate(message);
  } else if(type == 'generic') {
    return genericTemplate(message);
  } else if(type == 'list') {
    return listTemplate(message);
  } else if(type == 'receipt') {
    return receiptTemplate(message);
  } else {
    console.log("Unknown template type: " + type)
    return {};
  }
}

// Handles image, audio, video and file attachments
// @param {Object} message
// @returns {Object}
function attachmentMessage(message) {
  return {
    message: {
      mtype: message.attachment.type,
      properties: {
        url: message.attachment.payload.url
      }
    }
  };
}

// @param {Object} message
// @returns {Object}
function textMessage(message) {
  return {
    message: {
      mtype: 'text',
      properties: {
        text: message.text,
        quick_replies: message.quick_replies
      }
    }
  };
}

// @param {Object} data
// @returns {Object}
function senderActionMessage(data) {
  // TODO
  // return {
  //   message: {
  //     mtype: ,
  //     properties: {}
  // };
}

// Outgoing payload for Dialog API
// @param {Object} event
// @param {String} botId
// @returns {Object}
function outgoingPayload(event, botId) {
  return {
    message: {
      distinct_id: event.message_id,
      platform: 'messenger',
      provider: 'dialog-node',
      mtype: null,
      sent_at: new Date().getTime() / 1000,
      properties: {}
    },
    conversation: {
      distinct_id: event.recipient_id + '-' + botId
    },
    creator: {
      distinct_id: botId,
      type: 'bot'
    }
  };
}

// @param {Object} client
// @param {Object} data Payload sent to Facebook Send API
// @param {Object} event Response from Facebook API
// @param {Function} callback
// @returns {Object, Null}
function outgoing(client, data, event, callback) {
  var payload = null;

  if (data.message) {
    var message = data.message;

    if (message.attachment) {
      if(message.attachment.type == 'template') {
        payload = templateMessage(message);
      } else {
        payload = attachmentMessage(message);
      }
    } else if (message.text) {
      payload = textMessage(message);
    }
  } else if (data.sender_action) {
    // payload = senderActionMessage(data) TODO
  } else {
    console.log("Unknown data: ", data);
  }

  var response = null;
  if (payload) {
    response = client.track(merge(outgoingPayload(event, client.botId), payload), callback);
  }

  return response;
}

module.exports = outgoing;
<|endoftext|>


<commit_before>/**
 * Created by equan on 10/13/13.
 */

var chalk = require('chalk')
    , pkg = require('../../package.json')
    , v = pkg.version;

exports.v = function(){
    return v;
}

exports.head = function(){
    console.log(chalk.red.bold("       _______      ___    _"));
    console.log(chalk.red.bold("      / ____\\ \\    / | |  | |"));
    console.log(chalk.green.bold("     | (___  \\ \\  / /| |__| |"));
    console.log(chalk.green.bold("      \\___ \\  \\ \\/ / |  __  |"));
    console.log(chalk.blue.bold("      ____) |  \\  /  | |  | |"));
    console.log(chalk.blue.bold("     |_____/    \\/   |_|  |_|"));
    console.log("");
    console.log(chalk.white.bold("             Node Serve Here"));
    console.log(chalk.white.bold("                     v ") + chalk.white(v));
}

exports.output = function (msg) {
    console.log("");
    console.log(msg);
    console.log("");
};<commit_msg>remove output() and add version() method<commit_after>/**
 * Created by equan on 10/13/13.
 */

var chalk = require('chalk');
var pkg = require('../../package.json');
var v = pkg.version;

exports.v = function(){
    return v;
}

exports.head = function(){
    console.log(chalk.red.bold("       _______      ___    _"));
    console.log(chalk.red.bold("      / ____\\ \\    / | |  | |"));
    console.log(chalk.green.bold("     | (___  \\ \\  / /| |__| |"));
    console.log(chalk.green.bold("      \\___ \\  \\ \\/ / |  __  |"));
    console.log(chalk.blue.bold("      ____) |  \\  /  | |  | |"));
    console.log(chalk.blue.bold("     |_____/    \\/   |_|  |_|"));
    console.log("");
    console.log(chalk.white.bold("             node serve here"));
    console.log(chalk.white.bold("                     v ") + chalk.white(v));
}

exports.version = function() {
    console.log(chalk.blue.bold("svh v"+v+ chalk.red.bold(' - ')+chalk.green.bold("node serve here")));
}
<|endoftext|>