<commit_before>angular.module("jarl", [])
  .controller("jarlCtrl", ["$scope", "$location", function ($scope, $location) {
    // decode the URL if it is provided
    if (typeof($location.search().url) === "undefined") {
      $scope.fullUrl = "";
    } else {
      $scope.fullUrl = decodeURIComponent($location.search().url);
    }

    $scope.$watch("fullUrl", function (newVal, oldVal) {
      if (typeof(newVal) === "undefined") {
        $scope.baseUrl = "";
        $scope.shareableUrl = "";
        $scope.params = [];
        return;
      }

      var parts = newVal.split("?");

      if (newVal === "") {
        $scope.shareableUrl = "";
      } else {
        $scope.shareableUrl = $location.absUrl().split('#?')[0] + "#?url=" + encodeURIComponent(newVal);
      }

      $scope.baseUrl = parts[0];
      var paramStrings = parts[1];

      if (paramStrings) {
        var keyvals = paramStrings.split("&");

        var params = [];
        for (var i = 0; i < keyvals.length; i++) {
          var kv = keyvals[i].split("=");
          var key = kv[0];
          var val = decodeURIComponent(kv[1]);

          // lazily and (somewhat) flexibly check if it's base64
          // note, this will give false positives because we're not domain experts
          // right now we're looking for basic decodes + email addresses
          var isBase64, decodedVal;
          try {
            decodedVal = atob(val);

            isBase64 = /^[a-zA-Z0-9@.?!,'"]+$/.exec(decodedVal) && new RegExp(val + "=*").exec(btoa(decodedVal));
          } catch (e) {
            isBase64 = false;
          }

          if (isBase64) {
            params.push({
              key: key,
              val: val,
              isBase64: isBase64,
              decoded64: decodedVal
            });
          } else {
            params.push({
              key: key,
              val: val,
              isBase64: isBase64
            });
          }

        }

        $scope.params = params;
      } else {
        $scope.params = [];
      }
    });
  }]);
<commit_msg>whitelist spaces<commit_after>angular.module("jarl", [])
  .controller("jarlCtrl", ["$scope", "$location", function ($scope, $location) {
    // decode the URL if it is provided
    if (typeof($location.search().url) === "undefined") {
      $scope.fullUrl = "";
    } else {
      $scope.fullUrl = decodeURIComponent($location.search().url);
    }

    $scope.$watch("fullUrl", function (newVal, oldVal) {
      if (typeof(newVal) === "undefined") {
        $scope.baseUrl = "";
        $scope.shareableUrl = "";
        $scope.params = [];
        return;
      }

      var parts = newVal.split("?");

      if (newVal === "") {
        $scope.shareableUrl = "";
      } else {
        $scope.shareableUrl = $location.absUrl().split('#?')[0] + "#?url=" + encodeURIComponent(newVal);
      }

      $scope.baseUrl = parts[0];
      var paramStrings = parts[1];

      if (paramStrings) {
        var keyvals = paramStrings.split("&");

        var params = [];
        for (var i = 0; i < keyvals.length; i++) {
          var kv = keyvals[i].split("=");
          var key = kv[0];
          var val = decodeURIComponent(kv[1]);

          // lazily and (somewhat) flexibly check if it's base64
          // note, this will give false positives because we're not domain experts
          // right now we're looking for basic decodes + email addresses
          var isBase64, decodedVal;
          try {
            decodedVal = atob(val);

            isBase64 = /^[a-zA-Z0-9@.?!,'"\w]+$/.exec(decodedVal) && new RegExp(val + "=*").exec(btoa(decodedVal));
          } catch (e) {
            isBase64 = false;
          }

          if (isBase64) {
            params.push({
              key: key,
              val: val,
              isBase64: isBase64,
              decoded64: decodedVal
            });
          } else {
            params.push({
              key: key,
              val: val,
              isBase64: isBase64
            });
          }

        }

        $scope.params = params;
      } else {
        $scope.params = [];
      }
    });
  }]);
<|endoftext|>


<commit_before>'use strict';
/**
 * Main Module for SockBot2.0
 * @module SockBot
 * @author Accalia
 * @license MIT
 */

exports.version = 'v2.0.0';

<commit_msg>set ignore on main metod for istanbul.<commit_after>'use strict';
/**
 * Main Module for SockBot2.0
 * @module SockBot
 * @author Accalia
 * @license MIT
 */

/*eslint-disable no-console */
exports.version = 'v2.0.0';

const config = require('./config'),
    browser = require('./browser');
/* istanbul ignore if */
if (require.main === module) {
    config.loadConfiguration(process.argv[2], () => {
        browser.login(() => console.log(arguments));
    });
}
<|endoftext|>


<commit_before>/**
 * localStorage
 * @param  storage could be window.sessionStorage/window.localStorage
 * @param  String key
 */
var Storage = function( key , storage  ){
	storage = storage || window.localStorage || {};
	var data = eval("("+storage[key]+")") || {};

	/**
	 * get all value
	 */
	this.getAll = function(){
		return data;
	};

	/**
	 * get a value
	 */
	this.get = function(key){
		return data[key];
	};

	/**
	 * set a value
	 */
	this.set = function( k , v){
		if( arguments.length == 1 ){
			data = k;
		}else{
			data[k] = v;
		}
		storage[key] = JSON.stringify(data);
	};

	/**
	 * delete a key
	 */
	this.delete = function(k){
		delete data[k];
		storage[key] = JSON.stringify(data);
	};

	/**
	 * destory this storage
	 */
	this.destory = function(){
		if( storage.removeItem ){
			storage.removeItem(key);
		}else{
			storage[key] = null;
		}
	};
};
<commit_msg>Update Storage.js<commit_after>/**
 * localStorage
 * @param  storage could be window.sessionStorage/window.localStorage
 * @param  String key
 */
var Storage = function( key , storage  ){
	storage = storage || window.localStorage || {};
	var data = eval("("+storage[key]+")") || {};

	/**
	 * get all value
	 */
	this.getAll = function(){
		return data;
	};

	/**
	 * get a value
	 */
	this.get = function(key){
		return data[key];
	};

	/**
	 * set a value
	 */
	this.set = function( k , v){
		if( arguments.length == 1 ){
			data = k;
		}else{
			data[k] = v;
		}
		storage[key] = JSON.stringify(data);
	};

	/**
	 * delete a key
	 */
	this.remove = function(k){
		delete data[k];
		storage[key] = JSON.stringify(data);
	};

	/**
	 * destory this storage
	 */
	this.destory = function(){
		if( storage.removeItem ){
			storage.removeItem(key);
		}else{
			storage[key] = null;
		}
	};
};
<|endoftext|>


<commit_before>function Game(difficulty) {
	console.log("Game loaded");

	document.addEventListener('keydown', checkKeyDown, false);
    document.addEventListener('keyup', checkKeyUp, false);

	var canvas_board 	= 	document.getElementById('game-board');
	var canvas_player 	= 	document.getElementById('game-player');
	var canvas_mirrors 	= 	document.getElementById('game-mirrors');

	var ctx_board 		= 	canvas_board.getContext('2d');
	var ctx_player 		= 	canvas_player.getContext('2d');
	var ctx_mirrors 	= 	canvas_mirrors.getContext('2d');

	var game_ended		= 	false;
	/*var frame_rate 		=   window.requestAnimationFrame ||
							window.webkitRequestAnimationFrame ||
							window.mozRequestAnimationFrame ||
							window.oRequestAnimationFrame ||
							window.msRequestAnimationFrame ||
							function(callback) {
								window.setTimeout(callback, 1000 / 60);
							};*/

	var board_width 	= canvas_board.width;
	var board_height 	= canvas_board.height;

	var desired_score 	= 15;

	var mirrors 		= [];
	var mirror_interval = null; //interval id

	var points 			= [];
	var point_interval 	= null;
	var max_points		= 2;

	var square 			= new Square(Math.floor((Math.random()*board_width/2)+1), Math.floor((Math.random()*board_height/2)+1)); //randomly place square
	this.difficulty 	= difficulty;

	var player 			= new Player(board_width/2, board_height/2, 0, this.difficulty);

	setDifficulty(player, this.difficulty);

	function init() {

	}

	Game.prototype.play = function() {
		render();
		//mirror_interval = setInterval(addMirror, 1000/difficulty);
		point_interval = setInterval(addPoint, 4000/difficulty);

	};

	Game.prototype.getDifficulty = function() {
		return this.difficulty;
	};

	Game.prototype.restart = function() {
		endGame();
	};

	function Player (x, y, radius) {
		this.ball 			= new Ball(0, 0, 9); //for collision
	    this.oX				= x;
	    this.oY 			= y;
	    this.oRadius 		= radius;

		this.score 			= 0;

		this.path_cords		= [];

		this.player_img 	= new Image();
		this.player_img.src = "./resources/images/arrow-white-left.png";

		this.x 				= x;
	    this.y 				= y;
	    this.radius 		= radius;

	    this.is_thrusting 	= true;
	    this.angle 			= 0;

	    this.is_right_key 	= false;
	    this.is_left_key 	= false;
	}

	Player.prototype.turn = function(dir) {
	    this.angle += this.turn_speed * dir;
	};

	Player.prototype.getAngle = function () {
		return this.angle;
	}

	Player.prototype.render = function (angle) {
	    drawImg(ctx_player, this.player_img, this.x, this.y, 10, 10, 20, 20, angle, 1);
	    this.ball.update(this.x, this.y);
	};

	Player.prototype.updateScore = function(points) {
    	this.score += points;
    	if (this.score == desired_score) endGame();
	};

	Player.prototype.getPath = function() {
		return this.path_cords;
	};

	Player.prototype.checkMirrorCollision = function() {

		if (distance(player.ball, square.ball)) {
			square.pickUp();
			setTimeout(function() {square.drop()}, 4000);
		}

		for (var i = points.length - 1; i >= 0; i--) {
			if (distance(points[i].ball, player.ball)) {
				points[i].pickUp();
			}
		};
	    for (var i = 0; i < mirrors.length; i++) {
	        if (distance(mirrors[i].ball, player.ball)) {
				console.log("Bum");
				endGame();
			}
	    }
	};

	function Point (x, y) {
		this.ball = new Ball(x, y, 7, "rgb(255,255,255)");
		this.x 	  = x;
		this.y 	  = y;

		this.picked_up 		= false;
		this.fade_out 	 	= false;

		this.fading_dirrection = false;
	}

	Point.prototype.pickUp = function() {
		if (!this.picked_up) player.updateScore(1);
		this.picked_up = true;

	};

	Point.prototype.render = function() {
		if (this.picked_up) {
			this.ball.update(this.ball.x - 10, this.ball.y - 10, this.ball.radius - 0.2*difficulty);

		}

		if (!this.picked_up && this.fade_out) {
			if (this.ball.radius <= 8.5 && !this.fading_dirrection) this.ball.update(this.ball.x - 10, this.ball.y - 10, this.ball.radius + 0.1*difficulty);
			if (this.ball.radius >= 8.5) this.fading_dirrection = true;
			if (this.ball.radius >= 6.5 && this.fading_dirrection) this.ball.update(this.ball.x - 10, this.ball.y - 10, this.ball.radius - 0.1*difficulty);
			if (this.ball.radius <= 6.5) this.fading_dirrection = false;

		}

		this.ball.render();
	}

	function Square (x, y) {
		this.ball = new Ball(x-2.5, y-2.5, 8, "rgb(255,0,0)");
		this.x 	  = x;
		this.y 	  = y;

		this.square_img 	= new Image();
		this.square_img.src = "./resources/images/square-white.png";

		this.picked_up 		= false;
		this.fade_out 	 	= false;

		this.opacity 			= 1;
	}

	Square.prototype.pickUp = function() {
		if (!this.picked_up) {
			this.picked_up = true;

		}
	};

	Square.prototype.drop = function() {
		if (this.picked_up) {
			this.picked_up = false;
			this.x = Math.floor((Math.random()*board_width/2)+1);
			this.y = Math.floor((Math.random()*board_height/2)+1);
			this.ball.update(this.x-2.5, this.y-2.5, this.ball.radius);
			this.opacity = 1;
		}

	};

	Square.prototype.render = function () {
		if (!this.picked_up)
	   		drawImg(ctx_board, this.square_img, this.x, this.y, 0, 0, 15, 15, 0, 1);

	    if (this.picked_up) {
	    	if (this.opacity >= 0.2){
	    		this.opacity -= 0.1*difficulty;
	    		drawImg(ctx_board, this.square_img, this.x, this.y, 0, 0, 15, 15, 0, this.opacity);
	    	}
		}

		//this.ball.render();
	};

	function Mirror (x, y, radius, path) {
		this.ball 			= new Ball(0, 0, 6); //for collision
		this.mirror_img 	= new Image();
		this.mirror_img.src = "./resources/images/arrow-black-left.png";

		this.x 				= x;
	    this.y 				= y;
	    this.radius 		= radius;
	    this.path 			= path.slice(0);

	    this.opacity 		= 1;

	    this.is_thrusting 	= true;
	    this.angle 			= 0;

	    this.destroyed 		= false;
	}

	Mirror.prototype.turn = function(dir) {
	    this.angle += this.turn_speed * dir;
	};

	Mirror.prototype.render = function (angle, opacity) {
	    drawImg(ctx_mirrors, this.mirror_img, this.x, this.y, 10, 10, 20, 20, angle, opacity);
	    this.ball.update(this.x, this.y);
	};

	Mirror.prototype.checkPlayerCollision = function() {
		if (distance(this.ball, player.ball)) {
			endGame();
		}
	};

	function Ball (x, y, radius, color) {
	    this.x = x + 10;
	    this.y = y + 10;
	    this.radius = radius;
	    this.color = color || "rgb(255,0,0)";
	}

	Ball.prototype.update = function (x, y, radius) {
	    this.x = x + 10;
	    this.y = y + 10;
	    this.radius = radius || this.radius;
	};

	Ball.prototype.render = function () {
	    ctx_board.fillStyle = this.color;
	    ctx_board.beginPath();
	    ctx_board.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
	    ctx_board.closePath();
	    ctx_board.fill();
	};

	function addMirror() {
		//console.log(player.path_cords.length);
		if (game_ended) return false;
		mirrors.push(new Mirror(player.oX, player.oY, player.oRadius, player.path_cords));
		setDifficulty(mirrors[mirrors.length - 1], difficulty);
	}

	function addPoint() {
		if (game_ended) return false;

		if (points.length >= max_points - 1) {
			points[0].fade_out = true;
		}

		if (points.length >= max_points) {
			points.shift();
		} else {
			points.push(new Point(Math.floor((Math.random()*board_width/2)+1), Math.floor((Math.random()*board_height/2)+1)));
		}

	}

	function render() {
	    if (player.is_right_key) {
	        // right arrow
	        player.is_left_key = false;
	        player.turn(1);
	    }

	    if (player.is_left_key) {
	        // left arrow
	       player.is_right_key = false;
	       player.turn(-1);
	    }

	    clearCtx(ctx_player);
	    clearCtx(ctx_mirrors);
	    clearCtx(ctx_board);

	    player.path_cords.push(player.angle); //save players path

	    update(player);
	    player.render(player.getAngle() * 180 / Math.PI);

	    //player.ball.render();
	    player.checkMirrorCollision();
	    square.render();

	    for (var i = mirrors.length - 1; i >= 0; i--) {
	    	if (mirrors[i].path.length <= 90) mirrors[i].opacity -= 0.01; //console.log(mirrors[i] + " end of path");
	    	//if (mirrors[i].path.length <= 20) mirrors[i].destroyed = true;
	    	if (mirrors[i].path.length == 0) {
	    		mirrors.shift();
	    		continue;
	    	}
	    	//mirrors[i].checkPlayerCollision();

	    	mirrors[i].angle = mirrors[i].path.shift();
	    	update(mirrors[i]);

	    	mirrors[i].render(mirrors[i].angle * 180 / Math.PI, mirrors[i].opacity);
	    	//mirrors[i].ball.render(); //drawCircle(ctx_border, mirrors[i].ball)
	    };

	    for (var i = points.length - 1; i >= 0; i--) {
	    	if (points[i].ball.radius >= 2) points[i].render();
	    };

	    if (!game_ended) {
	    	requestAnimationFrame(render); //call itself again
	    } else {
	    	changeScreen("menu");
	    }

	}

	function drawImg(canvas, img, pX, pY, oX, oY, w, h, rot, opacity) {
		canvas.save();
		canvas.translate(pX+oX, pY+oY);
		canvas.rotate(rot);
		canvas.globalAlpha = opacity;
		canvas.drawImage(img, 0, 0, w, h, -(oX), -(oY), w, h);
		canvas.restore();
	}

	function clearCtx(canvas) {
    	canvas.clearRect(0, 0, board_width, board_height);
	}

	function endGame() {
		game_ended = true;
		//write score etc

		clearInterval(mirror_interval);
		clearInterval(point_interval);
	}

	function setDifficulty(obj, difficulty) {

    	obj.thrust = 1.4*difficulty;
    	obj.turn_speed = 0.0008*difficulty;
	}

	function update( obj ) {

		var radians = obj.angle/Math.PI*180;

	    if ( obj.is_thrusting ) {

	      obj.velX = Math.cos(radians) * obj.thrust;
	      obj.velY = Math.sin(radians) * obj.thrust;

	    }

	    // bounds check
	    if (obj.x < obj.radius) {
	        obj.x = board_width;
	    }

	    if (obj.x > board_width) {
	        obj.x = obj.radius;
	    }

	    if (obj.y < obj.radius) {
	        obj.y = board_height;
	    }

	    if (obj.y > board_height) {
	        obj.y = obj.radius;
	    }

	    // apply friction
	    obj.velX *= 1;
	    obj.velY *= 1;

	    // apply velocities
	    obj.x -= obj.velX;
	    obj.y -= obj.velY;
	}

	function distance(ent1, ent2) { //for balls
	    var x = ent2.x - ent1.x,
	        y = ent2.y - ent1.y,
	        dist = Math.sqrt(x*x + y*y),
	        collision = false;

	    // check the distance against the sum of both objects radius. If its less its a hit
	    if(dist < ent1.radius + ent2.radius){
	       collision = true;
	    }
	    return collision;
	}

	function checkKeyDown(e) {

	    var keyID = e.keyCode || e.which;

	    if (keyID === 39 || keyID === 68) { //right arrow or D key
	        player.is_right_key = true;
	        e.preventDefault();
	    }

	    if (keyID === 37 || keyID === 65) { //left arrow or A key
	        player.is_left_key = true;
	        e.preventDefault();
	    }

	}

	function checkKeyUp(e) {

	    var keyID = e.keyCode || e.which;

	    if (keyID === 39 || keyID === 68) { //right arrow or D key
	        player.is_right_key = false;
	        e.preventDefault();
	    }

	    if (keyID === 37 || keyID === 65) { //left arrow or A key
	        player.is_left_key = false;
	        e.preventDefault();
	    }

	}

}

<commit_msg>Square functionality done.<commit_after>function Game(difficulty) {
	console.log("Game loaded");

	document.addEventListener('keydown', checkKeyDown, false);
    document.addEventListener('keyup', checkKeyUp, false);

	var canvas_board 	= 	document.getElementById('game-board');
	var canvas_player 	= 	document.getElementById('game-player');
	var canvas_mirrors 	= 	document.getElementById('game-mirrors');

	var ctx_board 		= 	canvas_board.getContext('2d');
	var ctx_player 		= 	canvas_player.getContext('2d');
	var ctx_mirrors 	= 	canvas_mirrors.getContext('2d');

	var game_ended		= 	false;
	/*var frame_rate 		=   window.requestAnimationFrame ||
							window.webkitRequestAnimationFrame ||
							window.mozRequestAnimationFrame ||
							window.oRequestAnimationFrame ||
							window.msRequestAnimationFrame ||
							function(callback) {
								window.setTimeout(callback, 1000 / 60);
							};*/

	var board_width 	= canvas_board.width;
	var board_height 	= canvas_board.height;

	var desired_score 	= 15;

	var mirrors 		= [];
	var mirror_interval = null; //interval id

	var points 			= [];
	var point_interval 	= null;
	var max_points		= 2;

	var square 			= new Square(Math.floor((Math.random()*board_width/2)+1), Math.floor((Math.random()*board_height/2)+1)); //randomly place square
	this.difficulty 	= difficulty;

	var player 			= new Player(board_width/2, board_height/2, 0, this.difficulty);

	setDifficulty(player, this.difficulty);

	function init() {

	}

	Game.prototype.play = function() {
		render();
		mirror_interval = setInterval(addMirror, 1000/difficulty);
		point_interval = setInterval(addPoint, 4000/difficulty);

	};

	Game.prototype.getDifficulty = function() {
		return this.difficulty;
	};

	Game.prototype.restart = function() {
		endGame();
	};

	function Player (x, y, radius) {
		this.ball 			= new Ball(0, 0, 9); //for collision
	    this.oX				= x;
	    this.oY 			= y;
	    this.oRadius 		= radius;

		this.score 			= 0;

		this.path_cords		= [];

		this.player_img 	= new Image();
		this.player_img.src = "./resources/images/arrow-white-left.png";

		this.x 				= x;
	    this.y 				= y;
	    this.radius 		= radius;

	    this.is_thrusting 	= true;
	    this.angle 			= 0;

	    this.is_right_key 	= false;
	    this.is_left_key 	= false;
	}

	Player.prototype.turn = function(dir) {
	    this.angle += this.turn_speed * dir;
	};

	Player.prototype.getAngle = function () {
		return this.angle;
	}

	Player.prototype.render = function (angle) {
	    drawImg(ctx_player, this.player_img, this.x, this.y, 10, 10, 20, 20, angle, 1);
	    this.ball.update(this.x, this.y);
	};

	Player.prototype.updateScore = function(points) {
    	this.score += points;
    	if (this.score == desired_score) endGame();
	};

	Player.prototype.getPath = function() {
		return this.path_cords;
	};

	Player.prototype.checkMirrorCollision = function() {

		if (distance(player.ball, square.ball)) {
			square.pickUp();
			setTimeout(function() {square.drop()}, 4000);
		}

		for (var i = points.length - 1; i >= 0; i--) {
			if (distance(points[i].ball, player.ball)) {
				points[i].pickUp();
			}
		};
	    for (var i = 0; i < mirrors.length; i++) {
	        if (distance(mirrors[i].ball, player.ball)) {
	        	if (square.picked_up) {
	        		mirrors[i].destroyed = true;
	        	} else {
	        		if (!mirrors[i].destroyed) {
	        			console.log("Bum");
						endGame();
	        		}

	        	}

			}

	    }

	};

	function Point (x, y) {
		this.ball = new Ball(x, y, 7, "rgb(255,255,255)");
		this.x 	  = x;
		this.y 	  = y;

		this.picked_up 		= false;
		this.fade_out 	 	= false;

		this.fading_dirrection = false;
	}

	Point.prototype.pickUp = function() {
		if (!this.picked_up) player.updateScore(1);
		this.picked_up = true;

	};

	Point.prototype.render = function() {
		if (this.picked_up) {
			this.ball.update(this.ball.x - 10, this.ball.y - 10, this.ball.radius - 0.2*difficulty);

		}

		if (!this.picked_up && this.fade_out) {
			if (this.ball.radius <= 8.5 && !this.fading_dirrection) this.ball.update(this.ball.x - 10, this.ball.y - 10, this.ball.radius + 0.1*difficulty);
			if (this.ball.radius >= 8.5) this.fading_dirrection = true;
			if (this.ball.radius >= 6.5 && this.fading_dirrection) this.ball.update(this.ball.x - 10, this.ball.y - 10, this.ball.radius - 0.1*difficulty);
			if (this.ball.radius <= 6.5) this.fading_dirrection = false;

		}

		this.ball.render();
	}

	function Square (x, y) {
		this.ball = new Ball(x-2.5, y-2.5, 8, "rgb(255,0,0)");
		this.x 	  = x;
		this.y 	  = y;

		this.square_img 	= new Image();
		this.square_img.src = "./resources/images/square-white.png";

		this.picked_up 		= false;
		this.fade_out 	 	= false;

		this.opacity 			= 1;
	}

	Square.prototype.pickUp = function() {
		if (!this.picked_up) {
			this.picked_up = true;

		}
	};

	Square.prototype.drop = function() {
		if (this.picked_up) {
			this.picked_up = false;
			this.x = Math.floor((Math.random()*board_width/2)+1);
			this.y = Math.floor((Math.random()*board_height/2)+1);
			this.ball.update(this.x-2.5, this.y-2.5, this.ball.radius);
			this.opacity = 1;
		}

	};

	Square.prototype.render = function () {
		if (!this.picked_up)
	   		drawImg(ctx_board, this.square_img, this.x, this.y, 0, 0, 15, 15, 0, 1);

	    if (this.picked_up) {
	    	if (this.opacity >= 0.2){
	    		this.opacity -= 0.1*difficulty;
	    		drawImg(ctx_board, this.square_img, this.x, this.y, 0, 0, 15, 15, 0, this.opacity);
	    	}
		}

		//this.ball.render();
	};

	function Mirror (x, y, radius, path) {
		this.ball 			= new Ball(0, 0, 6); //for collision
		this.mirror_img 	= new Image();
		this.mirror_img.src = "./resources/images/arrow-black-left.png";

		this.x 				= x;
	    this.y 				= y;
	    this.radius 		= radius;
	    this.path 			= path.slice(0);

	    this.opacity 		= 1;

	    this.is_thrusting 	= true;
	    this.angle 			= 0;

	    this.destroyed 		= false;
	}

	Mirror.prototype.turn = function(dir) {
	    this.angle += this.turn_speed * dir;
	};

	Mirror.prototype.render = function (angle, opacity) {
	    drawImg(ctx_mirrors, this.mirror_img, this.x, this.y, 10, 10, 20, 20, angle, opacity);
	    this.ball.update(this.x, this.y);
	};

	Mirror.prototype.checkPlayerCollision = function() {
		if (distance(this.ball, player.ball)) {
			endGame();
		}
	};

	function Ball (x, y, radius, color) {
	    this.x = x + 10;
	    this.y = y + 10;
	    this.radius = radius;
	    this.color = color || "rgb(255,0,0)";
	}

	Ball.prototype.update = function (x, y, radius) {
	    this.x = x + 10;
	    this.y = y + 10;
	    this.radius = radius || this.radius;
	};

	Ball.prototype.render = function () {
	    ctx_board.fillStyle = this.color;
	    ctx_board.beginPath();
	    ctx_board.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
	    ctx_board.closePath();
	    ctx_board.fill();
	};

	function addMirror() {
		//console.log(player.path_cords.length);
		if (game_ended || square.picked_up) return false;
		mirrors.push(new Mirror(player.oX, player.oY, player.oRadius, player.path_cords));
		setDifficulty(mirrors[mirrors.length - 1], difficulty);
	}

	function addPoint() {
		if (game_ended) return false;

		if (points.length >= max_points - 1) {
			points[0].fade_out = true;
		}

		if (points.length >= max_points) {
			points.shift();
		} else {
			points.push(new Point(Math.floor((Math.random()*board_width/2)+1), Math.floor((Math.random()*board_height/2)+1)));
		}

	}

	function render() {
	    if (player.is_right_key) {
	        // right arrow
	        player.is_left_key = false;
	        player.turn(1);
	    }

	    if (player.is_left_key) {
	        // left arrow
	       player.is_right_key = false;
	       player.turn(-1);
	    }

	    clearCtx(ctx_player);
	    clearCtx(ctx_mirrors);
	    clearCtx(ctx_board);

	    player.path_cords.push(player.angle); //save players path

	    update(player);
	    player.render(player.getAngle() * 180 / Math.PI);

	    //player.ball.render();
	    player.checkMirrorCollision();
	    square.render();

	    for (var i = mirrors.length - 1; i >= 0; i--) {
	    	if (mirrors[i].path.length <= 90 && !square.picked_up) mirrors[i].opacity -= 0.01; //console.log(mirrors[i] + " end of path");
	    	//if (mirrors[i].path.length <= 20) mirrors[i].destroyed = true;
	    	if (mirrors[i].path.length == 0) {
	    		mirrors.shift();
	    		continue;
	    	}

	    	if (!square.picked_up) {
	    		//if square is not picked up do updates
	    		mirrors[i].angle = mirrors[i].path.shift();
	    		update(mirrors[i]);
	    	}

	    	if (!mirrors[i].destroyed)
	    	mirrors[i].render(mirrors[i].angle * 180 / Math.PI, mirrors[i].opacity);
	    	//mirrors[i].ball.render(); //drawCircle(ctx_border, mirrors[i].ball)
	    };

	    for (var i = points.length - 1; i >= 0; i--) {
	    	if (points[i].ball.radius >= 2) points[i].render();
	    };

	    if (!game_ended) {
	    	requestAnimationFrame(render); //call itself again
	    } else {
	    	changeScreen("menu");
	    }

	}

	function drawImg(canvas, img, pX, pY, oX, oY, w, h, rot, opacity) {
		canvas.save();
		canvas.translate(pX+oX, pY+oY);
		canvas.rotate(rot);
		canvas.globalAlpha = opacity;
		canvas.drawImage(img, 0, 0, w, h, -(oX), -(oY), w, h);
		canvas.restore();
	}

	function clearCtx(canvas) {
    	canvas.clearRect(0, 0, board_width, board_height);
	}

	function endGame() {
		game_ended = true;
		//write score etc

		clearInterval(mirror_interval);
		clearInterval(point_interval);
	}

	function setDifficulty(obj, difficulty) {

    	obj.thrust = 1.4*difficulty;
    	obj.turn_speed = 0.0008*difficulty;
	}

	function update( obj ) {

		var radians = obj.angle/Math.PI*180;

	    if ( obj.is_thrusting ) {

	      obj.velX = Math.cos(radians) * obj.thrust;
	      obj.velY = Math.sin(radians) * obj.thrust;

	    }

	    // bounds check
	    if (obj.x < obj.radius) {
	        obj.x = board_width;
	    }

	    if (obj.x > board_width) {
	        obj.x = obj.radius;
	    }

	    if (obj.y < obj.radius) {
	        obj.y = board_height;
	    }

	    if (obj.y > board_height) {
	        obj.y = obj.radius;
	    }

	    // apply friction
	    obj.velX *= 1;
	    obj.velY *= 1;

	    // apply velocities
	    obj.x -= obj.velX;
	    obj.y -= obj.velY;
	}

	function distance(ent1, ent2) { //for balls
	    var x = ent2.x - ent1.x,
	        y = ent2.y - ent1.y,
	        dist = Math.sqrt(x*x + y*y),
	        collision = false;

	    // check the distance against the sum of both objects radius. If its less its a hit
	    if(dist < ent1.radius + ent2.radius){
	       collision = true;
	    }
	    return collision;
	}

	function checkKeyDown(e) {

	    var keyID = e.keyCode || e.which;

	    if (keyID === 39 || keyID === 68) { //right arrow or D key
	        player.is_right_key = true;
	        e.preventDefault();
	    }

	    if (keyID === 37 || keyID === 65) { //left arrow or A key
	        player.is_left_key = true;
	        e.preventDefault();
	    }

	}

	function checkKeyUp(e) {

	    var keyID = e.keyCode || e.which;

	    if (keyID === 39 || keyID === 68) { //right arrow or D key
	        player.is_right_key = false;
	        e.preventDefault();
	    }

	    if (keyID === 37 || keyID === 65) { //left arrow or A key
	        player.is_left_key = false;
	        e.preventDefault();
	    }

	}

}

<|endoftext|>


<commit_before>'use strict';

/* TODO the supervisor and client should agree on an identifier for every
 * server so connections and errors go to the correct instance. */
/* TODO graceful shutdown on uncaught exception, exit, etc */

// This module duck-punches the Node.js `net` module, replacing its `Server`,
// such that we can intercept all requests to start and stop servers, forward
// them to the supervisor, and receive connections through the Node.js process
// IPC channel.
//
// We considered creating wrappers for the Node.js `net`, `http`, and `https`
// modules that would intercept all attempts to create TCP connections through
// the various levels of indirection, but Node.js does not expose a way to
// override the underlying `net` module.
// We may return to explore this design direction in another iteration.

// Invariant: we must close a server before emitting an error.

var net = require('net');
var events = require('events');
var util = require('util');

var servers = {}; // Indexed by port.
// These are mutually recursive and JSHint likes them declared before use:
var tickTime, pulse;

// ## Communication with the supervisor

process.on('message', function (message, handle) {
    if (typeof message !== 'object' || message === null) {
        return;
    }
    if (message.cmd === 'CLUSTER_START') {
        handleStart(message.modulePath, message.pulse);
    } else if (message.cmd === 'CLUSTER_ACCEPT') {
        handleAccept(message.port, handle);
    } else if (message.cmd === 'CLUSTER_LISTENING') {
        handleListening(message.port, message.address);
    } else if (message.cmd === 'CLUSTER_ERROR') {
        handleError(message.port, message.message);
    }
});

// The supervisor will immediately send a request to start running a worker
// module and kick off the configured heart beat.
var started = false;
function handleStart(modulePath, givenPulse) {

    if (started) {
        throw new Error('Assertion failed: worker received a second start message over its IPC channel');
    }
    started = true;

    // Start periodic health reports.
    pulse = givenPulse;
    if (pulse) {
        tick();
    }

    // Start running the worker module.
    require(modulePath);
}

function handleAccept(port, handle) {
    var server = servers[port];
    if (!handle) {
        // We hit this case only when we receive a connection message from the cluster,
        // but the cluster is shutting down.
        return;
    }
    if (server) {
        server._accept(handle);
    } else {
        // Return to sender if the server no longer exists.
        // This might occur if a connection was sent from the cluster to the
        // worker before the cluster received a notification from the worker
        // that it had closed the corresponding server.
        process.send({cmd: 'CLUSTER_BOUNCE', port: port}, handle);
    }
}

function handleListening(port, address) {
    var server = servers[port];
    if (server) {
        // The address that a server receives is not necessarily knowable
        // based on the address or port that it requested.
        // Specifically, if you request a server on port 0, this allows the
        // operating system to assign an emphemeral port.
        // When a server advertizes that it is now accepting connections,
        // it must also allow the user to call `address()` to discover what
        // port it is listening on.
        // We capture this address in the `_address` property in a worker's
        // Server thunk.
        // Node.js core implements `address()` very differently, but we
        // allow that work to occur on the cluster leader.
        server._address = address;
        server.emit('listening', server);
    } else {
        // If a server starts listening but closes before the cluster can
        // respond, we can assume that a CLUSTER_CLOSE message has already
        // been sent to the to the supervisor and that the server will be
        // duly removed from the load balancer rotation.
        // There is nothing to do in the worker to respond to this
        // situation, but we will inform the cluster for logging purposes.
        process.send({
            cmd: 'CLUSTER_NOT_LISTENING', // Gollum.
            port: port
        });
    }
}

function handleError(port, message) {
    var server = servers[port];
    if (server) {
        // Close before calling out to user code.
        server.close();
        // Note that the following will throw synchronously if there are no
        // error handlers or if any of those handlers throw.
        server.emit('error', new Error(message));
    } else {
        process.send({
            cmd: 'CLUSTER_RETURN_ERROR',
            port: port,
            message: message
        });
    }
}

// ## Heart beat health check

// Here follows the mechanism for periodically sending health checks to the
// supervisor over the Node.js IPC channel.

// Marks the start time and schedules the next pulse.
function tick() {
    tickTime = process.hrtime();
    setTimeout(tock, pulse);
}

// Measures the actual duration of the pulse and transmits health metrics to
// the supervisor.
// The load measures the average amount of time that the event loop blocked the
// scheduled pulse.
function tock() {
    var tockPeriod = process.hrtime(tickTime);
    var tockPeriodMs = hrtimeAsMs(tockPeriod);
    process.send({
        cmd: 'CLUSTER_PULSE',
        load: tockPeriodMs - pulse,
        memoryUsage: process.memoryUsage()
    });
    tick();
}

// `hrtime` returns a [seconds, nanoseconds] tuple.
function hrtimeAsMs(duple) {
    var seconds = duple[0];
    var nanoseconds = duple[1];
    return seconds * 1e3 + nanoseconds / 1e6;
}

// ## Subverting the Node.js network stack

// The following replaces the TCP server that sits at the base of the Node.js
// networking stack, allowing the supervisor to take over the responsibility of
// distributing connections.

// This began its life as a an excerpt of the Node.js networking stack from
// version 0.12.

net.createServer = function createServer() {
  return new net.Server(arguments[0], arguments[1]);
};

function Server(/* [ options, ] listener */) {
    if (!(this instanceof Server)) return new Server(arguments[0], arguments[1]);
    events.EventEmitter.call(this);

    var self = this;

    var options;

    if (typeof arguments[0] === 'function') {
        options = {};
        self.on('connection', arguments[0]);
    } else {
        options = arguments[0] || {};
        if (typeof arguments[1] === 'function') {
            self.on('connection', arguments[1]);
        }
    }

    this._connections = [];

    this.allowHalfOpen = options.allowHalfOpen || false;
}
util.inherits(Server, events.EventEmitter);
net.Server = Server;

// Intercept all attempts to listen on this server and redirect them to the
// cluster supervisor.
Server.prototype.listen = function () {
    var self = this;

    var lastArg = arguments[arguments.length - 1];
    if (typeof lastArg === 'function') {
        self.once('listening', lastArg);
    }

    var port = toNumber(arguments[0]);
    var pipeName;

    // The third optional argument is the backlog size.
    // When the ip is omitted it can be the second argument.
    var backlog = toNumber(arguments[1]) || toNumber(arguments[2]);

    var TCP = process.binding('tcp_wrap').TCP;

    if (arguments.length === 0 || typeof arguments[0] === 'function') {
        // Bind to a random port.
        listen(self, null, 0, null, backlog);
    } else if (arguments[0] && typeof arguments[0] === 'object') {
        // Various descriptors are not supported by cluster workers.
        var h = arguments[0];
        h = h._handle || h.handle || h;
        if (h instanceof TCP) {
            throw new Error('Can\'t listen on a TCP handle in cluster mode yet');
        } else if (typeof h.fd === 'number' && h.fd >= 0) {
            throw new Error('Can\'t listen on a file descriptor in cluster mode yet');
        } else {
            // The first argument is a configuration object
            if (h.backlog)
                backlog = h.backlog;

            if (typeof h.port === 'number') {
                if (h.host) {
                    listenAfterLookup(h.port, h.host, backlog, h.exclusive);
                } else {
                    listen(self, null, h.port, 4, backlog, undefined, h.exclusive);
                }
            } else if (h.path && isPipeName(h.path)) {
                pipeName = self._pipeName = h.path;
                listen(self, pipeName, -1, -1, backlog, undefined, h.exclusive);
            } else {
                throw new Error('Invalid listen argument: ' + h);
            }
        }
    } else if (isPipeName(arguments[0])) {
        // UNIX socket or Windows pipe.
        pipeName = self._pipeName = arguments[0];
        // Since servers are addressed by port, we use the pipe name as the
        // port as well as the address.
        listen(self, pipeName, pipeName, undefined, backlog);
    } else if (
        typeof arguments[1] === 'undefined' ||
        typeof arguments[1] === 'function' ||
        typeof arguments[1] === 'number'
    ) {
        // The first argument is the port, no IP given.
        listen(self, null, port, 4, backlog);

    } else {
        // The first argument is the port, the second an IP.
        listenAfterLookup(port, arguments[1], backlog);
    }

    function listenAfterLookup(/*port, address, backlog, exclusive*/) {
        /* TODO We need to reason out whether the worker or cluster will issue the DNS request. */
        throw new Error('Can\'t listen on an interface and port in cluster mode yet');
        /*
        require('dns').lookup(address, function(err, ip, addressType) {
            if (err) {
                self.emit('error', err);
            } else {
                addressType = ip ? addressType : 4;
                listen(self, ip, port, addressType, backlog, undefined, exclusive);
            }
        });
        */
    }

    return self;
};

// This performs the actual listen when the argument forms have been
// normalized above.
/*jshint -W072 */
function listen(self, address, port, addressType, backlog, exclusive) {
    /* TODO Index servers by a self-assigned identifier and the process
     * identifier so messages intended for previous instances do not pass
     * through. */
    servers[port] = self; // Index servers by port.
    self._port = port; // Remember the requested port for close.
    process.send({
        cmd: 'CLUSTER_LISTEN',
        port: port,
        address: address, // For confirmation purposes only.
        addressType: addressType, // We allow the supervisor to infer the address type.
        backlog: backlog, // Used to confirm uniformity of worker requests.
        exclusive: exclusive // TODO use this on the other end
    });
}
/*jshint +W072 */

Server.prototype.close = function (callback) {
    if (callback) {
        this.once('close', callback);
    }
    process.send({
        cmd: 'CLUSTER_CLOSE',
        port: this._port
    });
    // This prevents any further connections from falling into this server
    // instance.
    delete servers[this._port];
    /* TODO emit 'close' event when all open connections are closed. */
    this.emit('close');
};

Server.prototype._accept = function (connection) {
    var self = this;
    function onfinish() {
        var index = self._connections.indexOf(connection);
        if (index >= 0) {
            self._connections.splice(index, 1);
        }
    }
    connection.once('finish', onfinish);
    this._connections.push(connection);
    this.emit('connection', connection);
};

Server.prototype.getConnections = function (cb) {
    function end(err, connections) {
        process.nextTick(function () {
            cb(null, connections);
        });
    }

    end(null, this._connections);
};

// There is not much we can do about ref and unref. The underlying handle is on
// the cluster and will be retained indefinitely.
// The IPC channel retains this process.

Server.prototype.unref = function () { };

Server.prototype.ref = function () { };

// We receive an address property from the cluster supervisor when it confirms
// that we are listening on the requested interface.
// This works for both the socket and pipe cases since the address() method
// returns either a pipe name string or a host port pair.
Server.prototype.address = function () {
    return this._address;
};

function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; }

function isPipeName(s) {
  return typeof s === 'string' && toNumber(s) === false;
}

<commit_msg>Allow null address argument in net thunk<commit_after>'use strict';

/* TODO the supervisor and client should agree on an identifier for every
 * server so connections and errors go to the correct instance. */
/* TODO graceful shutdown on uncaught exception, exit, etc */

// This module duck-punches the Node.js `net` module, replacing its `Server`,
// such that we can intercept all requests to start and stop servers, forward
// them to the supervisor, and receive connections through the Node.js process
// IPC channel.
//
// We considered creating wrappers for the Node.js `net`, `http`, and `https`
// modules that would intercept all attempts to create TCP connections through
// the various levels of indirection, but Node.js does not expose a way to
// override the underlying `net` module.
// We may return to explore this design direction in another iteration.

// Invariant: we must close a server before emitting an error.

var net = require('net');
var events = require('events');
var util = require('util');

var servers = {}; // Indexed by port.
// These are mutually recursive and JSHint likes them declared before use:
var tickTime, pulse;

// ## Communication with the supervisor

process.on('message', function (message, handle) {
    if (typeof message !== 'object' || message === null) {
        return;
    }
    if (message.cmd === 'CLUSTER_START') {
        handleStart(message.modulePath, message.pulse);
    } else if (message.cmd === 'CLUSTER_ACCEPT') {
        handleAccept(message.port, handle);
    } else if (message.cmd === 'CLUSTER_LISTENING') {
        handleListening(message.port, message.address);
    } else if (message.cmd === 'CLUSTER_ERROR') {
        handleError(message.port, message.message);
    }
});

// The supervisor will immediately send a request to start running a worker
// module and kick off the configured heart beat.
var started = false;
function handleStart(modulePath, givenPulse) {

    if (started) {
        throw new Error('Assertion failed: worker received a second start message over its IPC channel');
    }
    started = true;

    // Start periodic health reports.
    pulse = givenPulse;
    if (pulse) {
        tick();
    }

    // Start running the worker module.
    require(modulePath);
}

function handleAccept(port, handle) {
    var server = servers[port];
    if (!handle) {
        // We hit this case only when we receive a connection message from the cluster,
        // but the cluster is shutting down.
        return;
    }
    if (server) {
        server._accept(handle);
    } else {
        // Return to sender if the server no longer exists.
        // This might occur if a connection was sent from the cluster to the
        // worker before the cluster received a notification from the worker
        // that it had closed the corresponding server.
        process.send({cmd: 'CLUSTER_BOUNCE', port: port}, handle);
    }
}

function handleListening(port, address) {
    var server = servers[port];
    if (server) {
        // The address that a server receives is not necessarily knowable
        // based on the address or port that it requested.
        // Specifically, if you request a server on port 0, this allows the
        // operating system to assign an emphemeral port.
        // When a server advertizes that it is now accepting connections,
        // it must also allow the user to call `address()` to discover what
        // port it is listening on.
        // We capture this address in the `_address` property in a worker's
        // Server thunk.
        // Node.js core implements `address()` very differently, but we
        // allow that work to occur on the cluster leader.
        server._address = address;
        server.emit('listening', server);
    } else {
        // If a server starts listening but closes before the cluster can
        // respond, we can assume that a CLUSTER_CLOSE message has already
        // been sent to the to the supervisor and that the server will be
        // duly removed from the load balancer rotation.
        // There is nothing to do in the worker to respond to this
        // situation, but we will inform the cluster for logging purposes.
        process.send({
            cmd: 'CLUSTER_NOT_LISTENING', // Gollum.
            port: port
        });
    }
}

function handleError(port, message) {
    var server = servers[port];
    if (server) {
        // Close before calling out to user code.
        server.close();
        // Note that the following will throw synchronously if there are no
        // error handlers or if any of those handlers throw.
        server.emit('error', new Error(message));
    } else {
        process.send({
            cmd: 'CLUSTER_RETURN_ERROR',
            port: port,
            message: message
        });
    }
}

// ## Heart beat health check

// Here follows the mechanism for periodically sending health checks to the
// supervisor over the Node.js IPC channel.

// Marks the start time and schedules the next pulse.
function tick() {
    tickTime = process.hrtime();
    setTimeout(tock, pulse);
}

// Measures the actual duration of the pulse and transmits health metrics to
// the supervisor.
// The load measures the average amount of time that the event loop blocked the
// scheduled pulse.
function tock() {
    var tockPeriod = process.hrtime(tickTime);
    var tockPeriodMs = hrtimeAsMs(tockPeriod);
    process.send({
        cmd: 'CLUSTER_PULSE',
        load: tockPeriodMs - pulse,
        memoryUsage: process.memoryUsage()
    });
    tick();
}

// `hrtime` returns a [seconds, nanoseconds] tuple.
function hrtimeAsMs(duple) {
    var seconds = duple[0];
    var nanoseconds = duple[1];
    return seconds * 1e3 + nanoseconds / 1e6;
}

// ## Subverting the Node.js network stack

// The following replaces the TCP server that sits at the base of the Node.js
// networking stack, allowing the supervisor to take over the responsibility of
// distributing connections.

// This began its life as a an excerpt of the Node.js networking stack from
// version 0.12.

net.createServer = function createServer() {
  return new net.Server(arguments[0], arguments[1]);
};

function Server(/* [ options, ] listener */) {
    if (!(this instanceof Server)) return new Server(arguments[0], arguments[1]);
    events.EventEmitter.call(this);

    var self = this;

    var options;

    if (typeof arguments[0] === 'function') {
        options = {};
        self.on('connection', arguments[0]);
    } else {
        options = arguments[0] || {};
        if (typeof arguments[1] === 'function') {
            self.on('connection', arguments[1]);
        }
    }

    this._connections = [];

    this.allowHalfOpen = options.allowHalfOpen || false;
}
util.inherits(Server, events.EventEmitter);
net.Server = Server;

// Intercept all attempts to listen on this server and redirect them to the
// cluster supervisor.
Server.prototype.listen = function () {
    var self = this;

    var lastArg = arguments[arguments.length - 1];
    if (typeof lastArg === 'function') {
        self.once('listening', lastArg);
    }

    var port = toNumber(arguments[0]);
    var pipeName;

    // The third optional argument is the backlog size.
    // When the ip is omitted it can be the second argument.
    var backlog = toNumber(arguments[1]) || toNumber(arguments[2]);

    var TCP = process.binding('tcp_wrap').TCP;

    if (arguments.length === 0 || typeof arguments[0] === 'function') {
        // Bind to a random port.
        listen(self, null, 0, null, backlog);
    } else if (arguments[0] && typeof arguments[0] === 'object') {
        // Various descriptors are not supported by cluster workers.
        var h = arguments[0];
        h = h._handle || h.handle || h;
        if (h instanceof TCP) {
            throw new Error('Can\'t listen on a TCP handle in cluster mode yet');
        } else if (typeof h.fd === 'number' && h.fd >= 0) {
            throw new Error('Can\'t listen on a file descriptor in cluster mode yet');
        } else {
            // The first argument is a configuration object
            if (h.backlog)
                backlog = h.backlog;

            if (typeof h.port === 'number') {
                if (h.host) {
                    listenAfterLookup(h.port, h.host, backlog, h.exclusive);
                } else {
                    listen(self, null, h.port, 4, backlog, undefined, h.exclusive);
                }
            } else if (h.path && isPipeName(h.path)) {
                pipeName = self._pipeName = h.path;
                listen(self, pipeName, -1, -1, backlog, undefined, h.exclusive);
            } else {
                throw new Error('Invalid listen argument: ' + h);
            }
        }
    } else if (isPipeName(arguments[0])) {
        // UNIX socket or Windows pipe.
        pipeName = self._pipeName = arguments[0];
        // Since servers are addressed by port, we use the pipe name as the
        // port as well as the address.
        listen(self, pipeName, pipeName, undefined, backlog);
    } else if (
        typeof arguments[1] === 'undefined' ||
        typeof arguments[1] === 'function' ||
        typeof arguments[1] === 'number' ||
        arguments[1] === null
    ) {
        // The first argument is the port, no IP given.
        listen(self, null, port, 4, backlog);

    } else {
        // The first argument is the port, the second an IP.
        listenAfterLookup(port, arguments[1], backlog);
    }

    function listenAfterLookup(port, address/*, backlog, exclusive*/) {
        /* TODO We need to reason out whether the worker or cluster will issue the DNS request. */
        throw new Error('Can\'t listen on an interface and port in cluster mode yet: ' + address + ':' + port);
        /*
        require('dns').lookup(address, function(err, ip, addressType) {
            if (err) {
                self.emit('error', err);
            } else {
                addressType = ip ? addressType : 4;
                listen(self, ip, port, addressType, backlog, undefined, exclusive);
            }
        });
        */
    }

    return self;
};

// This performs the actual listen when the argument forms have been
// normalized above.
/*jshint -W072 */
function listen(self, address, port, addressType, backlog, exclusive) {
    /* TODO Index servers by a self-assigned identifier and the process
     * identifier so messages intended for previous instances do not pass
     * through. */
    servers[port] = self; // Index servers by port.
    self._port = port; // Remember the requested port for close.
    process.send({
        cmd: 'CLUSTER_LISTEN',
        port: port,
        address: address, // For confirmation purposes only.
        addressType: addressType, // We allow the supervisor to infer the address type.
        backlog: backlog, // Used to confirm uniformity of worker requests.
        exclusive: exclusive // TODO use this on the other end
    });
}
/*jshint +W072 */

Server.prototype.close = function (callback) {
    if (callback) {
        this.once('close', callback);
    }
    process.send({
        cmd: 'CLUSTER_CLOSE',
        port: this._port
    });
    // This prevents any further connections from falling into this server
    // instance.
    delete servers[this._port];
    /* TODO emit 'close' event when all open connections are closed. */
    this.emit('close');
};

Server.prototype._accept = function (connection) {
    var self = this;
    function onfinish() {
        var index = self._connections.indexOf(connection);
        if (index >= 0) {
            self._connections.splice(index, 1);
        }
    }
    connection.once('finish', onfinish);
    this._connections.push(connection);
    this.emit('connection', connection);
};

Server.prototype.getConnections = function (cb) {
    function end(err, connections) {
        process.nextTick(function () {
            cb(null, connections);
        });
    }

    end(null, this._connections);
};

// There is not much we can do about ref and unref. The underlying handle is on
// the cluster and will be retained indefinitely.
// The IPC channel retains this process.

Server.prototype.unref = function () { };

Server.prototype.ref = function () { };

// We receive an address property from the cluster supervisor when it confirms
// that we are listening on the requested interface.
// This works for both the socket and pipe cases since the address() method
// returns either a pipe name string or a host port pair.
Server.prototype.address = function () {
    return this._address;
};

function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; }

function isPipeName(s) {
  return typeof s === 'string' && toNumber(s) === false;
}

<|endoftext|>


<commit_before>/*!
* jQuery Text Counter Plugin v0.7.0
* https://github.com/ractoon/jQuery-Text-Counter
*
* Copyright 2014 ractoon
* Released under the MIT license
*/
;(function($) {
    $.textcounter = function(el, options) {
        // To avoid scope issues, use 'base' instead of 'this'
        // to reference this class from internal events and functions.
        var base = this;

        // Access to jQuery and DOM versions of element
        base.$el = $(el);
        base.el = el;

        // Add a reverse reference to the DOM object
        base.$el.data('textcounter', base);

        base.init = function() {
            base.options = $.extend({}, $.textcounter.defaultOptions, options);

            // append the count element
            var counterText = base.options.countDown ? base.options.countDownText : base.options.counterText,
                counterNum = base.options.countDown ? base.options.max : 0,
                $formatted_counter_text = $('
').addClass(base.options.textCountMessageClass)
                .html(counterText.replace('%d', '' + counterNum + '')),
                $count_overflow_text = $('
').addClass(base.options.countOverflowContainerClass);

            base.hideMessage($count_overflow_text);

            base.$container = $('<' + base.options.countContainerElement + '/>')
                .addClass(base.options.countContainerClass)
                .append($formatted_counter_text)
                .append($count_overflow_text);

            base.$text_counter = base.$container.find('span');
            base.$el.after(base.$container);

            // bind input events
            base.$el.bind('keyup.textcounter click.textcounter blur.textcounter focus.textcounter change.textcounter paste.textcounter', base.checkLimits).trigger('click.textcounter');

            // TextCounter: init(el) Callback
            base.options.init(base.el);
        };

        base.checkLimits = function(e) {
            var $this = base.$el,
            $countEl = base.$container,
            $text = $this.val(),
            textCount = 0,
            textTotalCount = 0,
            eventTriggered =  e.originalEvent === undefined ? false : true;

            if (!$.isEmptyObject($text)) {
                textCount = base.textCount($text);
            }

            // if max is auto retrieve value
            if (base.options.max == 'auto') {
                var max = base.$el.attr('maxlength');

                if (typeof max !== 'undefined' && max !== false) {
                    base.options.max = max;
                }
                else {
                    base.$container.text('error: [maxlength] attribute not set');
                }
            }

            // if this is a countdown counter deduct from the max characters/words
            textTotalCount = base.options.countDown ? base.options.max - textCount : textCount;

            // set the current text count
            base.setCount(textTotalCount);

            if (base.options.min > 0 && eventTriggered) {   // if a minimum value has been set
                if (textCount < base.options.min) {
                    base.setErrors('min');

                    // TextCounter: minunder(el) Callback
                    base.options.minunder(base.el);
                }
                else if (textCount >= base.options.min) {
                    // TextCounter: mincount(el) Callback
                    base.options.mincount(base.el);

                    base.clearErrors('min');
                }
            }

            if (base.options.max !== -1) {  // if a maximum value has been set
                if (textCount === base.options.max && base.options.max !== 0) {
                    // TextCounter: maxcount(el) Callback
                    base.options.maxcount(base.el);
                    base.clearErrors('max');

                } else if (textCount > base.options.max && base.options.max !== 0) {
                    if (base.options.stopInputAtMaximum) {  // if the string should be trimmed at the maximum length
                        var trimmedString = '';

                        if (base.options.type == "word") {  // word type
                            var wordArray = $text.split(/[^\S\n]/g);
                            var i = 0;

                            // iterate over individual words
                            while (i < wordArray.length) {
                                // if over the maximum words allowed break;
                                if (i >= base.options.max) break;

                                if (wordArray[i] !== undefined) {
                                    trimmedString += wordArray[i] + ' ';
                                    i++;
                                }
                            }
                        }
                        else {  // character type
                            var maxLimit = (base.options.twoCharCarriageReturn) ?
                                base.options.max - base.twoCharCarriageReturnCount($text)
                                : base.options.max;

                            if (base.options.countSpaces) {     // if spaces should be counted
                                trimmedString = $text.substring(0, maxLimit);
                            }
                            else {
                                var charArray = $text.split(''),
                                totalCharacters = charArray.length,
                                charCount = 0,
                                i = 0;

                                while (charCount < maxLimit && i < totalCharacters) {
                                    if (charArray[i] !== ' ') charCount++;
                                    trimmedString += charArray[i++];
                                }
                            }
                        }

                        $this.val(trimmedString.trim());

                        textCount = base.textCount($this.val());
                        textTotalCount = base.options.countDown ? base.options.max - textCount : textCount;
                        base.setCount(textTotalCount);
                    } else {
                        base.setErrors('max');
                    }
                }
                else {
                    // TextCounter: maxunder(el) Callback
                    base.options.maxunder(base.el);
                    base.clearErrors('max');
                }
            }
        };

        base.textCount = function(text) {
            var textCount = 0;

            if (base.options.type == "word") {  // word count
                textCount = base.wordCount(text);
            }
            else {  // character count
                textCount = base.characterCount(text);
            }

            return textCount;
        };

        base.wordCount = function(text) {
            return text.trim().replace(/\s+/gi, ' ').split(' ').length;
        };

        base.characterCount = function(text) {
            var textCount = 0,
                carriageReturnsCount = 0;

            // count carriage returns/newlines as 2 characters
            if (base.options.twoCharCarriageReturn) {
                carriageReturnsCount = base.twoCharCarriageReturnCount(text);
            }

            if (base.options.countSpaces) { // if need to count spaces
                textCount = text.replace(/[^\S\n|\r|\r\n]/g, ' ').length;
            }
            else {
                textCount = text.replace(/\s/g, '').length;
            }

            // count extended characters (e.g. Chinese)
            if (base.options.countExtendedCharacters) {
                var extended = text.match(/[^\x00-\xff]/gi);

                if (extended == null) {
                    textCount = text.length;
                } else {
                    textCount = text.length + extended.length;
                }
            }

            if (base.options.twoCharCarriageReturn) {
                textCount += carriageReturnsCount;
            }

            return textCount;
        };

        base.twoCharCarriageReturnCount = function(text) {
            var carriageReturns = text.match(/(\r\n|\n|\r)/g),
                carriageReturnsCount = 0;

            if (carriageReturns !== null) {
                carriageReturnsCount = carriageReturns.length;
            }

            return carriageReturnsCount;
        };

        base.setCount = function(count) {
            base.$text_counter.text(count);
        };

        base.setErrors = function(type) {
            var $this = base.$el,
            $countEl = base.$container,
            errorText = '';

            $this.addClass(base.options.inputErrorClass);
            $countEl.addClass(base.options.counterErrorClass);

            switch(type) {
                case 'min':
                    errorText = base.options.minimumErrorText;
                    break;
                case 'max':
                    errorText = base.options.maximumErrorText;

                    if (base.options.countOverflow) {
                        base.setOverflowMessage();
                    }

                    break;
            }

            if (base.options.displayErrorText) {
                if (!$countEl.children('.error-text-' + type).length) {
                    $countEl.append('<' + base.options.errorTextElement + ' class="error-text error-text-' + type + '">' + errorText + '</' + base.options.errorTextElement + '>');
                }
            }
        };

        base.setOverflowMessage = function () {
            base.hideMessage(base.$container.find('.' + base.options.textCountMessageClass));

            base.removeOverflowMessage();

            var overflowText = base.options.countOverflowText
                .replace('%d', base.textCount(base.$el.val()) - base.options.max)
                .replace('%type', base.options.type + 's');

            var overflowDiv = base.$container.find('.' + base.options.countOverflowContainerClass).append(overflowText);
            base.showMessage(overflowDiv);
        },

        base.removeOverflowMessage = function () {
            base.$container.find('.' + base.options.countOverflowContainerClass).empty();
        },

        base.showMessage = function ($selector) {
            $selector.css('display', 'inline');
        },

        base.hideMessage = function ($selector) {
            $selector.css('display', 'none');
        },

        base.clearErrors = function(type) {
            var $this = base.$el,
            $countEl = base.$container;

            $countEl.children('.error-text-' + type).remove();

            if ($countEl.children('.error-text').length == 0) {
                base.removeOverflowMessage();
                base.showMessage(base.$container.find('.' + base.options.textCountMessageClass));
                $this.removeClass(base.options.inputErrorClass);
                $countEl.removeClass(base.options.counterErrorClass);
            }
        };

        // kick it off
        base.init();
    };

    $.textcounter.defaultOptions = {
        'type'                        : "character",                     // "character" or "word"
        'min'                         : 0,                               // minimum number of characters/words
        'max'                         : 200,                             // maximum number of characters/words, -1 for unlimited, 'auto' to use maxlength attribute
        'countContainerElement'       : "div",                           // HTML element to wrap the text count in
        'countContainerClass'         : "text-count-wrapper",            // class applied to the countContainerElement
        'textCountMessageClass'       : "text-count-message",            // class applied to the counter message
        'textCountClass'              : "text-count",                    // class applied to the counter length (the count number)
        'inputErrorClass'             : "error",                         // error class appended to the input element if error occurs
        'counterErrorClass'           : "error",                         // error class appended to the countContainerElement if error occurs
        'counterText'                 : "Total Count: %d",               // counter text
        'errorTextElement'            : "div",                           // error text element
        'minimumErrorText'            : "Minimum not met",               // error message for minimum not met,
        'maximumErrorText'            : "Maximum exceeded",              // error message for maximum range exceeded,
        'displayErrorText'            : true,                            // display error text messages for minimum/maximum values
        'stopInputAtMaximum'          : true,                            // stop further text input if maximum reached
        'countSpaces'                 : false,                           // count spaces as character (only for "character" type)
        'countDown'                   : false,                           // if the counter should deduct from maximum characters/words rather than counting up
        'countDownText'               : "Remaining: %d",                 // count down text
        'countExtendedCharacters'     : false,                           // count extended UTF-8 characters as 2 bytes (such as Chinese characters)
        'twoCharCarriageReturn'       : false,                           // count carriage returns/newlines as 2 characters
        'countOverflow'               : false,                           // display text overflow element
        'countOverflowText'           : "Maximum %type exceeded by %d",  // count overflow text
        'countOverflowContainerClass' : "text-count-overflow-wrapper",   // class applied to the count overflow wrapper

        // Callback API
        'maxunder'                    : function(el){},                  // Callback: function(element) - Fires when counter under max limit
        'minunder'                    : function(el){},                  // Callback: function(element) - Fires when counter under min limit
        'maxcount'                    : function(el){},                  // Callback: function(element) - Fires when the counter hits the maximum word/character count
        'mincount'                    : function(el){},                  // Callback: function(element) - Fires when the counter hits the minimum word/character count
        'init'                        : function(el){}                   // Callback: function(element) - Fires after the counter is initially setup
    };

    $.fn.textcounter = function(options) {
        return this.each(function() {
            new $.textcounter(this, options);
        });
    };

})(jQuery);
<commit_msg>README update<commit_after>/*!
* jQuery Text Counter Plugin v0.7.0
* https://github.com/ractoon/jQuery-Text-Counter
*
* Copyright 2014 ractoon
* Released under the MIT license
*/
;(function($) {
    $.textcounter = function(el, options) {
        // To avoid scope issues, use 'base' instead of 'this'
        // to reference this class from internal events and functions.
        var base = this;

        // Access to jQuery and DOM versions of element
        base.$el = $(el);
        base.el = el;

        // Add a reverse reference to the DOM object
        base.$el.data('textcounter', base);

        base.init = function() {
            base.options = $.extend({}, $.textcounter.defaultOptions, options);

            // append the count element
            var counterText = base.options.countDown ? base.options.countDownText : base.options.counterText,
                counterNum = base.options.countDown ? base.options.max : 0,
                $formatted_counter_text = $('
').addClass(base.options.textCountMessageClass)
                .html(counterText.replace('%d', '' + counterNum + '')),
                $count_overflow_text = $('
').addClass(base.options.countOverflowContainerClass);

            base.hideMessage($count_overflow_text);

            base.$container = $('<' + base.options.countContainerElement + '/>')
                .addClass(base.options.countContainerClass)
                .append($formatted_counter_text)
                .append($count_overflow_text);

            base.$text_counter = base.$container.find('span');
            base.$el.after(base.$container);

            // bind input events
            base.$el.bind('keyup.textcounter click.textcounter blur.textcounter focus.textcounter change.textcounter paste.textcounter', base.checkLimits).trigger('click.textcounter');

            // TextCounter: init(el) Callback
            base.options.init(base.el);
        };

        base.checkLimits = function(e) {
            var $this = base.$el,
            $countEl = base.$container,
            $text = $this.val(),
            textCount = 0,
            textTotalCount = 0,
            eventTriggered =  e.originalEvent === undefined ? false : true;

            if (!$.isEmptyObject($text)) {
                textCount = base.textCount($text);
            }

            // if max is auto retrieve value
            if (base.options.max == 'auto') {
                var max = base.$el.attr('maxlength');

                if (typeof max !== 'undefined' && max !== false) {
                    base.options.max = max;
                }
                else {
                    base.$container.text('error: [maxlength] attribute not set');
                }
            }
            else if (base.options.max == 'autocustom') {
                var max = base.$el.attr(base.options.autoCustomAttr);

                if (typeof max !== 'undefined' && max !== false) {
                    base.options.max = max;
                }
                else {
                    base.$container.text('error: [' + base.options.autoCustomAttr  + '] attribute not set');
                }
            }

            // if this is a countdown counter deduct from the max characters/words
            textTotalCount = base.options.countDown ? base.options.max - textCount : textCount;

            // set the current text count
            base.setCount(textTotalCount);

            if (base.options.min > 0 && eventTriggered) {   // if a minimum value has been set
                if (textCount < base.options.min) {
                    base.setErrors('min');

                    // TextCounter: minunder(el) Callback
                    base.options.minunder(base.el);
                }
                else if (textCount >= base.options.min) {
                    // TextCounter: mincount(el) Callback
                    base.options.mincount(base.el);

                    base.clearErrors('min');
                }
            }

            if (base.options.max !== -1) {  // if a maximum value has been set
                if (textCount === base.options.max && base.options.max !== 0) {
                    // TextCounter: maxcount(el) Callback
                    base.options.maxcount(base.el);
                    base.clearErrors('max');

                } else if (textCount > base.options.max && base.options.max !== 0) {
                    if (base.options.stopInputAtMaximum) {  // if the string should be trimmed at the maximum length
                        var trimmedString = '';

                        if (base.options.type == "word") {  // word type
                            var wordArray = $text.split(/[^\S\n]/g);
                            var i = 0;

                            // iterate over individual words
                            while (i < wordArray.length) {
                                // if over the maximum words allowed break;
                                if (i >= base.options.max) break;

                                if (wordArray[i] !== undefined) {
                                    trimmedString += wordArray[i] + ' ';
                                    i++;
                                }
                            }
                        }
                        else {  // character type
                            var maxLimit = (base.options.twoCharCarriageReturn) ?
                                base.options.max - base.twoCharCarriageReturnCount($text)
                                : base.options.max;

                            if (base.options.countSpaces) {     // if spaces should be counted
                                trimmedString = $text.substring(0, maxLimit);
                            }
                            else {
                                var charArray = $text.split(''),
                                totalCharacters = charArray.length,
                                charCount = 0,
                                i = 0;

                                while (charCount < maxLimit && i < totalCharacters) {
                                    if (charArray[i] !== ' ') charCount++;
                                    trimmedString += charArray[i++];
                                }
                            }
                        }

                        $this.val(trimmedString.trim());

                        textCount = base.textCount($this.val());
                        textTotalCount = base.options.countDown ? base.options.max - textCount : textCount;
                        base.setCount(textTotalCount);
                    } else {
                        base.setErrors('max');
                    }
                }
                else {
                    // TextCounter: maxunder(el) Callback
                    base.options.maxunder(base.el);
                    base.clearErrors('max');
                }
            }
        };

        base.textCount = function(text) {
            var textCount = 0;

            if (base.options.type == "word") {  // word count
                textCount = base.wordCount(text);
            }
            else {  // character count
                textCount = base.characterCount(text);
            }

            return textCount;
        };

        base.wordCount = function(text) {
            return text.trim().replace(/\s+/gi, ' ').split(' ').length;
        };

        base.characterCount = function(text) {
            var textCount = 0,
                carriageReturnsCount = 0;

            // count carriage returns/newlines as 2 characters
            if (base.options.twoCharCarriageReturn) {
                carriageReturnsCount = base.twoCharCarriageReturnCount(text);
            }

            if (base.options.countSpaces) { // if need to count spaces
                textCount = text.replace(/[^\S\n|\r|\r\n]/g, ' ').length;
            }
            else {
                textCount = text.replace(/\s/g, '').length;
            }

            // count extended characters (e.g. Chinese)
            if (base.options.countExtendedCharacters) {
                var extended = text.match(/[^\x00-\xff]/gi);

                if (extended == null) {
                    textCount = text.length;
                } else {
                    textCount = text.length + extended.length;
                }
            }

            if (base.options.twoCharCarriageReturn) {
                textCount += carriageReturnsCount;
            }

            return textCount;
        };

        base.twoCharCarriageReturnCount = function(text) {
            var carriageReturns = text.match(/(\r\n|\n|\r)/g),
                carriageReturnsCount = 0;

            if (carriageReturns !== null) {
                carriageReturnsCount = carriageReturns.length;
            }

            return carriageReturnsCount;
        };

        base.setCount = function(count) {
            base.$text_counter.text(count);
        };

        base.setErrors = function(type) {
            var $this = base.$el,
            $countEl = base.$container,
            errorText = '';

            $this.addClass(base.options.inputErrorClass);
            $countEl.addClass(base.options.counterErrorClass);

            switch(type) {
                case 'min':
                    errorText = base.options.minimumErrorText;
                    break;
                case 'max':
                    errorText = base.options.maximumErrorText;

                    if (base.options.countOverflow) {
                        base.setOverflowMessage();
                    }

                    break;
            }

            if (base.options.displayErrorText) {
                if (!$countEl.children('.error-text-' + type).length) {
                    $countEl.append('<' + base.options.errorTextElement + ' class="error-text error-text-' + type + '">' + errorText + '</' + base.options.errorTextElement + '>');
                }
            }
        };

        base.setOverflowMessage = function () {
            base.hideMessage(base.$container.find('.' + base.options.textCountMessageClass));

            base.removeOverflowMessage();

            var overflowText = base.options.countOverflowText
                .replace('%d', base.textCount(base.$el.val()) - base.options.max)
                .replace('%type', base.options.type + 's');

            var overflowDiv = base.$container.find('.' + base.options.countOverflowContainerClass).append(overflowText);
            base.showMessage(overflowDiv);
        },

        base.removeOverflowMessage = function () {
            base.$container.find('.' + base.options.countOverflowContainerClass).empty();
        },

        base.showMessage = function ($selector) {
            $selector.css('display', 'inline');
        },

        base.hideMessage = function ($selector) {
            $selector.css('display', 'none');
        },

        base.clearErrors = function(type) {
            var $this = base.$el,
            $countEl = base.$container;

            $countEl.children('.error-text-' + type).remove();

            if ($countEl.children('.error-text').length == 0) {
                base.removeOverflowMessage();
                base.showMessage(base.$container.find('.' + base.options.textCountMessageClass));
                $this.removeClass(base.options.inputErrorClass);
                $countEl.removeClass(base.options.counterErrorClass);
            }
        };

        // kick it off
        base.init();
    };

    $.textcounter.defaultOptions = {
        'type'                        : "character",                     // "character" or "word"
        'min'                         : 0,                               // minimum number of characters/words
        'max'                         : 200,                             // maximum number of characters/words, -1 for unlimited, 'auto' to use maxlength attribute, 'autocustom' to use a custom attribute for the length (must)
        'autoCustomAttr'              : "counterlimit",                  // custom attribute name with the counter limit if the max is 'autocustom'
        'countContainerElement'       : "div",                           // HTML element to wrap the text count in
        'countContainerClass'         : "text-count-wrapper",            // class applied to the countContainerElement
        'textCountMessageClass'       : "text-count-message",            // class applied to the counter message
        'textCountClass'              : "text-count",                    // class applied to the counter length (the count number)
        'inputErrorClass'             : "error",                         // error class appended to the input element if error occurs
        'counterErrorClass'           : "error",                         // error class appended to the countContainerElement if error occurs
        'counterText'                 : "Total Count: %d",               // counter text
        'errorTextElement'            : "div",                           // error text element
        'minimumErrorText'            : "Minimum not met",               // error message for minimum not met,
        'maximumErrorText'            : "Maximum exceeded",              // error message for maximum range exceeded,
        'displayErrorText'            : true,                            // display error text messages for minimum/maximum values
        'stopInputAtMaximum'          : true,                            // stop further text input if maximum reached
        'countSpaces'                 : false,                           // count spaces as character (only for "character" type)
        'countDown'                   : false,                           // if the counter should deduct from maximum characters/words rather than counting up
        'countDownText'               : "Remaining: %d",                 // count down text
        'countExtendedCharacters'     : false,                           // count extended UTF-8 characters as 2 bytes (such as Chinese characters)
        'twoCharCarriageReturn'       : false,                           // count carriage returns/newlines as 2 characters
        'countOverflow'               : false,                           // display text overflow element
        'countOverflowText'           : "Maximum %type exceeded by %d",  // count overflow text
        'countOverflowContainerClass' : "text-count-overflow-wrapper",   // class applied to the count overflow wrapper

        // Callback API
        'maxunder'                    : function(el){},                  // Callback: function(element) - Fires when counter under max limit
        'minunder'                    : function(el){},                  // Callback: function(element) - Fires when counter under min limit
        'maxcount'                    : function(el){},                  // Callback: function(element) - Fires when the counter hits the maximum word/character count
        'mincount'                    : function(el){},                  // Callback: function(element) - Fires when the counter hits the minimum word/character count
        'init'                        : function(el){}                   // Callback: function(element) - Fires after the counter is initially setup
    };

    $.fn.textcounter = function(options) {
        return this.each(function() {
            new $.textcounter(this, options);
        });
    };

})(jQuery);
<|endoftext|>