<commit_before>var Square = function(row, col, value, revealed){
	this.mine = -1;
	this.revealed = revealed
	this.row = row;
	this.col = col;
	this.value = (value >= -1 && value <= 8)? value : 0;
}

Square.prototype.getValue = function(){
	return this.value;
};

Square.prototype.setValue = function(n){
	this.value = (n >= -1 && n <= 8)? n : 0;
};

Square.prototype.setMine = function(){
	this.value = this.mine;
};

Square.prototype.isMine = function(){
	return this.value == -1;
};

Square.prototype.isZero = function(){
	return this.value == 0;
};

Square.prototype.reveal = function(){
	this.revealed = true;
};

Square.prototype.isRevealed = function(){
	return this.revealed;
}

//---------------------------------------------

var MinesweeperBoard = function(rows, cols, mines){
	this.mines = mines
	this.rows = rows;
	this.cols = cols;

	this.createGrid();
	this.fillMines();
	this.fillNumbers();
};

MinesweeperBoard.prototype.createGrid = function(){
	this.grid = new Array(this.rows);

	for (var i = 0; i < this.rows; i++){
		this.grid[i] = new Array(this.cols);
		for (var j = 0; j < this.cols; j++){
			this.grid[i][j] = new Square(i,j,0,false);
		}
	}
};

MinesweeperBoard.prototype.fillMines = function(){
	if (this.grid != null){
  	var n = this.mines
    while (n > 0){
    	var maxTries = 10000;
			while(true){
				var row = this.getRandomInt(this.rows);
				var col = this.getRandomInt(this.cols);
				if (!this.grid[row][col].isMine()){
					break;
				}
				maxTries--;
			}
			this.grid[row][col].setMine();
			n--;
		}
	}
};

MinesweeperBoard.prototype.getRandomInt = function(max){
    return Math.floor(Math.random() * (max));
};

MinesweeperBoard.prototype.fillNumbers = function(){
	if (this.grid != null){
		for (var i = 0; i < this.rows; i++){
			for (var j = 0; j < this.cols; j++){
				if (!this.grid[i][j].isMine()){
					this.grid[i][j].setValue(this.getDangerValue(i, j));
				}
			}
		}
	}
};

MinesweeperBoard.prototype.getDangerValue = function(i, j){
	var result = false;
	if (i >= 0 && i < this.rows && j >= 0 && j < this.cols){
		result = 0;
		result += this.isMine(i-1, j);
		result += this.isMine(i-1, j+1);
		result += this.isMine(i, j+1);
		result += this.isMine(i+1, j+1);
		result += this.isMine(i+1, j);
		result += this.isMine(i+1, j-1);
		result += this.isMine(i, j-1);
		result += this.isMine(i-1, j-1);
	}
	return result;
};

MinesweeperBoard.prototype.revealSquare = function(i,j){
	console.log("rev "+ i + ", " + j);this.grid[i][j].reveal();
};

MinesweeperBoard.prototype.isRevealed = function(i,j){
    if (i >= 0 && i < this.rows && j >= 0 && j < this.cols){
	return this.grid[i][j].isRevealed();
    }else{
	return true;
    }
};

MinesweeperBoard.prototype.getSquareValues = function(i,j){
	var squares = [];
	squares.push(this.grid[i][j]);
	this.revealSquare(i,j);
	if (this.grid[i][j].isZero()){
		squares = this.getSurroundingSquares(squares, i, j);
	}
	return squares;
};

MinesweeperBoard.prototype.getSurroundingSquares = function(squares, i, j){
	var zeros = [];
	if (this.getDangerValue(i-1,j) !== false && !this.isRevealed(i-1,j)){
		this.revealSquare(i-1,j);
		if ((this.grid[i-1][j]).isZero()){
			zeros.push(this.grid[i-1][j]);
		}
		squares.push(this.grid[i-1][j]);
	}

	if (this.getDangerValue(i-1,j+1) !== false && !this.isRevealed(i-1,j+1)){
		this.revealSquare(i-1,j+1);
		if ((this.grid[i-1][j+1]).isZero()){
			zeros.push(this.grid[i-1][j+1]);
		}
		squares.push(this.grid[i-1][j+1]);
	}

	if (this.getDangerValue(i,j+1) !== false && !this.isRevealed(i,j+1)){
		this.revealSquare(i,j+1);
		if ((this.grid[i][j+1]).isZero()){
			zeros.push(this.grid[i][j+1]);
		}
		squares.push(this.grid[i][j+1]);
	}

	if (this.getDangerValue(i+1,j+1) !== false && !this.isRevealed(i+1,j+1)){
		this.revealSquare(i+1,j+1);
		if ((this.grid[i+1][j+1]).isZero()){
			zeros.push(this.grid[i+1][j+1]);
		}
		squares.push(this.grid[i+1][j+1]);
	}

	if (this.getDangerValue(i+1,j) !== false && !this.isRevealed(i+1,j)){
		this.revealSquare(i+1,j);
		if ((this.grid[i+1][j]).isZero()){
			zeros.push(this.grid[i+1][j]);
		}
		squares.push(this.grid[i+1][j]);
	}

	if (this.getDangerValue(i+1,j-1) !== false && !this.isRevealed(i+1,j-1)){
		this.revealSquare(i+1,j-1);
		if ((this.grid[i+1][j-1]).isZero()){
			zeros.push(this.grid[i+1][j-1]);
		}
		squares.push(this.grid[i+1][j-1]);
	}

	if (this.getDangerValue(i,j-1) !== false && !this.isRevealed(i,j-1)){
		this.revealSquare(i,j-1);
		if ((this.grid[i][j-1]).isZero()){
			zeros.push(this.grid[i][j-1]);
		}
		squares.push(this.grid[i][j-1]);
	}

	if (this.getDangerValue(i-1,j-1) !== false && !this.isRevealed(i-1,j-1)){
		this.revealSquare(i-1,j-1);
		if ((this.grid[i-1][j-1]).isZero()){
			zeros.push(this.grid[i-1][j-1]);
		}
		squares.push(this.grid[i-1][j-1]);
	}

	for (var i = 0; i < zeros.length; i++){
		var zero = zeros[i];
		if (zero.row != null && zero.col != null){
			var array = this.getSurroundingSquares(squares, zero.row, zero.col);
			for (var j = 0; j < array.length; j++){
				var found  = false;
				for (var k = 0; k < squares.length; k++){
					if (array[j] == squares[k]){
						found = true;
					}
				}
				if (!found){
					squares.push(array[i]);
				}
			}
		}
	}

	return squares;
};

MinesweeperBoard.prototype.minesLeft = function(){
	return this.mines;
};

MinesweeperBoard.prototype.isMine = function(i, j){
	if (i >= 0 && i < this.rows && j >= 0 && j < this.cols){
		return this.grid[i][j].isMine();
	}
	return 0;
};

MinesweeperBoard.prototype.isZero = function(i, j){
	if (i >= 0 && i < this.rows && j >= 0 && j < this.cols){
		return this.grid[i][j].isZero();
	}
	return true;
};

//--------------------------------------------------

var MinesweeperGame = function(room, player1, player2, a1, a2, callback){
    this.room = room;
	this.turn = 0;
	this.players = [player1, player2];
    this.alias = [a1, a2];
	this.playerScores = [0,0];

	this.board = new MinesweeperBoard(20,15,60);
	this.timeLeft = 15;

	var me = this;
    this.timer = setInterval(function() {
        callback(me.timeLeft, me.turn);
        me.timeLeft--;
	if (me.timeLeft == -1){
	   me.stopInterval();
	}
    }, 1000);
}

MinesweeperGame.prototype.stopInterval = function(){
   clearInterval(this.timer);
};

MinesweeperGame.prototype.playerWon = function(){
	var difference = Math.abs(this.playerScores[0], this.playerScores[1]);
	if (difference > this.board.minesLeft() || this.board.minesLeft() == 0){
        clearInterval(this.timer);
		if (this.playerScores[0] > this.playerScores[1]){
			return players[0];
		}else if (this.playerScores[1] > this.playerScores[0]){
			return players[1];
		}else{
			return true;
		}
	}else{
		return false;
	}
};

MinesweeperGame.prototype.takeTurn = function(i,j){
	if (!this.board.isRevealed(i,j)){
		if (this.board.isMine(i,j)){
			this.playerScores[this.turn]++;
		}else{
			this.swapTurn();
		    this.timeLeft = 15;
		}
		return this.board.getSquareValues(i,j);
	}
	return false;
};

MinesweeperGame.prototype.getTurnWithAlias = function(){
	return this.alias[this.turn];
};

MinesweeperGame.prototype.getTurn = function(){
	return this.players[this.turn];
};

MinesweeperGame.prototype.swapTurn = function(){
	if (this.turn == 0){
		this.turn = 1;
	}else if(this.turn == 1){
		this.turn = 0;
	}
    console.log("Turn: "+ this.turn);
};

module.exports = MinesweeperGame;

/*
var game = new MinesweeperGame("nick", "andy");
for (var i = 0; i < game.board.grid.length; i++){
	var s = "";
  for (var j = 0; j < game.board.grid[i].length; j++){
  	if (game.board.grid[i][j].isMine()){
    	s += "* ";
    }else{
  		s += game.board.grid[i][j].getValue() + " ";
    }
  }
  console.log(s);
}

console.log(game.takeTurn(0,0));
console.log(game.takeTurn(0,29));
console.log(game.takeTurn(15,29));
console.log(game.takeTurn(15,0));
console.log(game.takeTurn(7,15));

console.log(game);
*/

/*
var square = new Square(0, false);
console.log(square);
square.setValue(10);
console.log(square);
square.setMine();
console.log(square);
square.reveal(square);
console.log(square);
*/

/*
var board = new MinesweeperBoard(10,20,50);
console.log(board);

for (var i = 0; i < board.grid.length; i++){
	var s = "";
  for (var j = 0; j < board.grid[i].length; j++){
  	if (board.grid[i][j].isMine()){
    	s += "* ";
    }else{
  		s += board.grid[i][j].getValue() + " ";
    }
  }
  console.log(s);
}
*/
<commit_msg>made constants for rows, cols, mines and time<commit_after>const ROWS = 20;
const COLS = 15;
const MINES = 10;
const TIME = 15;

var Square = function(row, col, value, revealed){
	this.mine = -1;
	this.revealed = revealed
	this.row = row;
	this.col = col;
	this.value = (value >= -1 && value <= 8)? value : 0;
}

Square.prototype.getValue = function(){
	return this.value;
};

Square.prototype.setValue = function(n){
	this.value = (n >= -1 && n <= 8)? n : 0;
};

Square.prototype.setMine = function(){
	this.value = this.mine;
};

Square.prototype.isMine = function(){
	return this.value == -1;
};

Square.prototype.isZero = function(){
	return this.value == 0;
};

Square.prototype.reveal = function(){
	this.revealed = true;
};

Square.prototype.isRevealed = function(){
	return this.revealed;
}

//---------------------------------------------

var MinesweeperBoard = function(rows, cols, mines){
	this.mines = mines
	this.rows = rows;
	this.cols = cols;

	this.createGrid();
	this.fillMines();
	this.fillNumbers();
};

MinesweeperBoard.prototype.createGrid = function(){
	this.grid = new Array(this.rows);

	for (var i = 0; i < this.rows; i++){
		this.grid[i] = new Array(this.cols);
		for (var j = 0; j < this.cols; j++){
			this.grid[i][j] = new Square(i,j,0,false);
		}
	}
};

MinesweeperBoard.prototype.fillMines = function(){
	if (this.grid != null){
  	var n = this.mines
    while (n > 0){
    	var maxTries = 10000;
			while(true){
				var row = this.getRandomInt(this.rows);
				var col = this.getRandomInt(this.cols);
				if (!this.grid[row][col].isMine()){
					break;
				}
				maxTries--;
			}
			this.grid[row][col].setMine();
			n--;
		}
	}
};

MinesweeperBoard.prototype.getRandomInt = function(max){
    return Math.floor(Math.random() * (max));
};

MinesweeperBoard.prototype.fillNumbers = function(){
	if (this.grid != null){
		for (var i = 0; i < this.rows; i++){
			for (var j = 0; j < this.cols; j++){
				if (!this.grid[i][j].isMine()){
					this.grid[i][j].setValue(this.getDangerValue(i, j));
				}
			}
		}
	}
};

MinesweeperBoard.prototype.getDangerValue = function(i, j){
	var result = false;
	if (i >= 0 && i < this.rows && j >= 0 && j < this.cols){
		result = 0;
		result += this.isMine(i-1, j);
		result += this.isMine(i-1, j+1);
		result += this.isMine(i, j+1);
		result += this.isMine(i+1, j+1);
		result += this.isMine(i+1, j);
		result += this.isMine(i+1, j-1);
		result += this.isMine(i, j-1);
		result += this.isMine(i-1, j-1);
	}
	return result;
};

MinesweeperBoard.prototype.revealSquare = function(i,j){
	this.grid[i][j].reveal();
};

MinesweeperBoard.prototype.isRevealed = function(i,j){
    if (i >= 0 && i < this.rows && j >= 0 && j < this.cols){
	return this.grid[i][j].isRevealed();
    }else{
	return true;
    }
};

MinesweeperBoard.prototype.getSquareValues = function(i,j){
	var squares = [];
	squares.push(this.grid[i][j]);
	this.revealSquare(i,j);
	if (this.grid[i][j].isZero()){
		squares = this.getSurroundingSquares(squares, i, j);
	}
	return squares;
};

MinesweeperBoard.prototype.getSurroundingSquares = function(squares, i, j){
	var zeros = [];
	if (this.getDangerValue(i-1,j) !== false && !this.isRevealed(i-1,j)){
		this.revealSquare(i-1,j);
		if ((this.grid[i-1][j]).isZero()){
			zeros.push(this.grid[i-1][j]);
		}
		squares.push(this.grid[i-1][j]);
	}

	if (this.getDangerValue(i-1,j+1) !== false && !this.isRevealed(i-1,j+1)){
		this.revealSquare(i-1,j+1);
		if ((this.grid[i-1][j+1]).isZero()){
			zeros.push(this.grid[i-1][j+1]);
		}
		squares.push(this.grid[i-1][j+1]);
	}

	if (this.getDangerValue(i,j+1) !== false && !this.isRevealed(i,j+1)){
		this.revealSquare(i,j+1);
		if ((this.grid[i][j+1]).isZero()){
			zeros.push(this.grid[i][j+1]);
		}
		squares.push(this.grid[i][j+1]);
	}

	if (this.getDangerValue(i+1,j+1) !== false && !this.isRevealed(i+1,j+1)){
		this.revealSquare(i+1,j+1);
		if ((this.grid[i+1][j+1]).isZero()){
			zeros.push(this.grid[i+1][j+1]);
		}
		squares.push(this.grid[i+1][j+1]);
	}

	if (this.getDangerValue(i+1,j) !== false && !this.isRevealed(i+1,j)){
		this.revealSquare(i+1,j);
		if ((this.grid[i+1][j]).isZero()){
			zeros.push(this.grid[i+1][j]);
		}
		squares.push(this.grid[i+1][j]);
	}

	if (this.getDangerValue(i+1,j-1) !== false && !this.isRevealed(i+1,j-1)){
		this.revealSquare(i+1,j-1);
		if ((this.grid[i+1][j-1]).isZero()){
			zeros.push(this.grid[i+1][j-1]);
		}
		squares.push(this.grid[i+1][j-1]);
	}

	if (this.getDangerValue(i,j-1) !== false && !this.isRevealed(i,j-1)){
		this.revealSquare(i,j-1);
		if ((this.grid[i][j-1]).isZero()){
			zeros.push(this.grid[i][j-1]);
		}
		squares.push(this.grid[i][j-1]);
	}

	if (this.getDangerValue(i-1,j-1) !== false && !this.isRevealed(i-1,j-1)){
		this.revealSquare(i-1,j-1);
		if ((this.grid[i-1][j-1]).isZero()){
			zeros.push(this.grid[i-1][j-1]);
		}
		squares.push(this.grid[i-1][j-1]);
	}

	for (var i = 0; i < zeros.length; i++){
		var zero = zeros[i];
		if (zero.row != null && zero.col != null){
			var array = this.getSurroundingSquares(squares, zero.row, zero.col);
			for (var j = 0; j < array.length; j++){
				var found  = false;
				for (var k = 0; k < squares.length; k++){
					if (array[j] == squares[k]){
						found = true;
					}
				}
				if (!found){
					squares.push(array[i]);
				}
			}
		}
	}

	return squares;
};

MinesweeperBoard.prototype.minesLeft = function(){
	return this.mines;
};

MinesweeperBoard.prototype.isMine = function(i, j){
	if (i >= 0 && i < this.rows && j >= 0 && j < this.cols){
		return this.grid[i][j].isMine();
	}
	return 0;
};

MinesweeperBoard.prototype.isZero = function(i, j){
	if (i >= 0 && i < this.rows && j >= 0 && j < this.cols){
		return this.grid[i][j].isZero();
	}
	return true;
};

//--------------------------------------------------

var MinesweeperGame = function(room, player1, player2, a1, a2, callback){
    this.room = room;
	this.turn = 0;
	this.players = [player1, player2];
    this.alias = [a1, a2];
	this.playerScores = [0,0];

	this.board = new MinesweeperBoard(ROWS, COLS, MINES);
	this.timeLeft = TIME;

	var me = this;
    this.timer = setInterval(function() {
        callback(me.timeLeft, me.turn);
        me.timeLeft--;
	if (me.timeLeft == -1){
	   me.stopInterval();
	}
    }, 1000);
}

MinesweeperGame.prototype.stopInterval = function(){
   clearInterval(this.timer);
};

MinesweeperGame.prototype.playerWon = function(){
	var difference = Math.abs(this.playerScores[0], this.playerScores[1]);
	if (difference > this.board.minesLeft() || this.board.minesLeft() == 0){
        clearInterval(this.timer);
		if (this.playerScores[0] > this.playerScores[1]){
			return players[0];
		}else if (this.playerScores[1] > this.playerScores[0]){
			return players[1];
		}else{
			return true;
		}
	}else{
		return false;
	}
};

MinesweeperGame.prototype.takeTurn = function(i,j){
	if (!this.board.isRevealed(i,j)){
		if (this.board.isMine(i,j)){
			this.playerScores[this.turn]++;
		    this.timeLeft = TIME;
		}else{
			this.swapTurn();
		    this.timeLeft = TIME;
		}
		return this.board.getSquareValues(i,j);
	}
	return false;
};

MinesweeperGame.prototype.getTurnWithAlias = function(){
	return this.alias[this.turn];
};

MinesweeperGame.prototype.getTurn = function(){
	return this.players[this.turn];
};

MinesweeperGame.prototype.swapTurn = function(){
	if (this.turn == 0){
		this.turn = 1;
	}else if(this.turn == 1){
		this.turn = 0;
	}
};

module.exports = MinesweeperGame;

/*
var game = new MinesweeperGame("nick", "andy");
for (var i = 0; i < game.board.grid.length; i++){
	var s = "";
  for (var j = 0; j < game.board.grid[i].length; j++){
  	if (game.board.grid[i][j].isMine()){
    	s += "* ";
    }else{
  		s += game.board.grid[i][j].getValue() + " ";
    }
  }
  console.log(s);
}

console.log(game.takeTurn(0,0));
console.log(game.takeTurn(0,29));
console.log(game.takeTurn(15,29));
console.log(game.takeTurn(15,0));
console.log(game.takeTurn(7,15));

console.log(game);
*/

/*
var square = new Square(0, false);
console.log(square);
square.setValue(10);
console.log(square);
square.setMine();
console.log(square);
square.reveal(square);
console.log(square);
*/

/*
var board = new MinesweeperBoard(10,20,50);
console.log(board);

for (var i = 0; i < board.grid.length; i++){
	var s = "";
  for (var j = 0; j < board.grid[i].length; j++){
  	if (board.grid[i][j].isMine()){
    	s += "* ";
    }else{
  		s += board.grid[i][j].getValue() + " ";
    }
  }
  console.log(s);
}
*/
<|endoftext|>


<commit_before>/*
* jQuery Mobile Framework : sample plugin for making button-like links
* Copyright (c) jQuery Project
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
* Note: Code is in draft form and is subject to change
*/
(function($){
$.fn.buttonMarkup = function(options){
	return $(this).each(function(){
		var el = $(this);
		var o = $.extend({
			theme: (function(){
				//if data-theme attr is present
				if(el.is('[data-theme]')){
					return el.attr('data-theme');
				}
				//if not, find closest theme container
				if(el.parents('body').length){
					var themedParent = el.closest('[class*=ui-bar-]'); //this still catches ui-bar-blah...
					return themedParent.length ? themedParent.attr('class').match(/ui-bar-([a-z])/)[1] : 'c';
				}
				else {
					return 'c';
				}
			})(),
			corners: true,
			shadow: true,
			iconshadow: true,
			iconPos: el.attr('data-iconPos'),
			icon: el.attr('data-icon')
		},options);

		if(o.icon){
			o.icon = 'ui-icon-'+o.icon;
			if(!o.iconPos){ o.iconPos = 'left'; }
		}

		el
			.attr('data-theme', o.theme)
			.addClass('ui-btn ui-btn-up-'+ o.theme + (o.corners?' ui-btn-corner-all':'') + (o.iconPos? ' ui-btn-icon-'+o.iconPos : '')+ (o.shadow? ' ui-shadow' : ''))
			.wrapInner('')
			.prepend(o.iconPos ? '': '')
			.wrapInner('')
			.clickable();
	});

};
})(jQuery);

<commit_msg>added a wrapperEls option to allow the generated wrapper elements to be divs rather than spans. This will be useful in listview, when button-like elements may have block-level hierarchy inside them.<commit_after>/*
* jQuery Mobile Framework : sample plugin for making button-like links
* Copyright (c) jQuery Project
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
* Note: Code is in draft form and is subject to change
*/
(function($){
$.fn.buttonMarkup = function(options){
	return $(this).each(function(){
		var el = $(this);
		var o = $.extend({
			theme: (function(){
				//if data-theme attr is present
				if(el.is('[data-theme]')){
					return el.attr('data-theme');
				}
				//if not, find closest theme container
				if(el.parents('body').length){
					var themedParent = el.closest('[class*=ui-bar-]'); //this still catches ui-bar-blah...
					return themedParent.length ? themedParent.attr('class').match(/ui-bar-([a-z])/)[1] : 'c';
				}
				else {
					return 'c';
				}
			})(),
			corners: true,
			shadow: true,
			iconshadow: true,
			iconPos: el.attr('data-iconPos'),
			icon: el.attr('data-icon'),
			wrapperEls: 'span'
		},options);

		if(o.icon){
			o.icon = 'ui-icon-'+o.icon;
			if(!o.iconPos){ o.iconPos = 'left'; }
		}

		el
			.attr('data-theme', o.theme)
			.addClass('ui-btn ui-btn-up-'+ o.theme + (o.corners?' ui-btn-corner-all':'') + (o.iconPos? ' ui-btn-icon-'+o.iconPos : '')+ (o.shadow? ' ui-shadow' : ''))
			.wrapInner('<'+o.wrapperEls+' class="ui-btn-text"></'+o.wrapperEls+'>')
			.prepend(o.iconPos ? '': '')
			.wrapInner('<'+o.wrapperEls+' class="ui-btn-inner '+ (o.corners?' ui-btn-corner-all':'') +'"></'+o.wrapperEls+'>')
			.clickable();
	});

};
})(jQuery);

<|endoftext|>


<commit_before>/*
Alessandro Minoccheri
V 1.0.0
15-02-2014
*/

(function ($) {
    $.fn.extend({
        customSelect: function (argumentOptions) {
            var defaults = {
				width		: 100,
				height		: 19,
				image       : "select.jpg",
				textAlign   : "left",
				textIndent	: "10"
			}

			var options = $.extend(defaults, argumentOptions);

			return this.each(function () {
                var o = options;
				var obj = $(this);
				var ul = $("ul", obj);
				var li = $("li", ul);
				var span = $("span", obj);
				var active = -1;
				var numberOfLiElement = ul.find('li').length;
				var $document = $(document);

				unbind = function(){
		    		ul.unbind("mouseleave");
		    		$document.unbind("keyup");
		    		$document.unbind("click");
		    	}

				var input= '<input type="text" value="' + span.text() + '" name="' + obj.attr('id') + '" style="display:none"/>';
				obj.append(input);

				obj.css("background", "url(img/" + o.image + ") no-repeat");
				obj.css("width", o.width);
				obj.css("height", o.height);

				span.css("text-align", o.textAlign);
				span.css("margin-left", o.textIndent + "px");

				ul.css('width', o.width);
				li.css('width', o.width);

				obj.click(function(){
					ul.css("opacity", "1");
					ul.css("display", "block");

					ul.bind("mouseleave", function(){
						 time = setTimeout(function() {
							 ul.animate({
								opacity: 0
							  }, 500, function() {
							  	unbind();
								ul.css("display", "none");
							 });
						}, 400);
					});
					$document.bind("keyup", function(e){
					    if (e.keyCode == 40) {
					        if(active < numberOfLiElement - 1){
					       		active++;
					       		ul.find('li').removeClass('active');
					       		ul.find('li:eq(' + active + ')').addClass('active');
					       	}
					    }
					    if (e.keyCode == 38) {
					        if(active > 0){
					       		active--;
					       		ul.find('li').removeClass('active');
					       		ul.find('li:eq(' + active + ')').addClass('active');
					       	}
					    }
					});

					time2 = setTimeout(function() {
						$document.bind("click", function(){
							ul.animate({
								opacity: 0
								}, 500, function() {
									unbind();
									ul.css("display", "none");
							});
						});
					}, 400);
				});

				li.on("click",function(){
					unbind();
					span.text($(this).text());
					$('input[name="' + obj.attr('id') + '"]').attr("value", $(this).text());

					ul.animate({
						opacity: 0
						}, 500, function() {
							ul.css("display", "none");
					});
				});
            });
        }
    });
})(jQuery);<commit_msg>indent well code<commit_after>/*
Alessandro Minoccheri
V 1.0.0
15-02-2014
*/

(function ($) {
    $.fn.extend({
        customSelect: function (argumentOptions) {
            var defaults = {
				width		: 100,
				height		: 19,
				image       : "select.jpg",
				textAlign   : "left",
				textIndent	: "10"
			}

			var options = $.extend(defaults, argumentOptions);

			return this.each(function () {
                var o 					= options;
				var obj 				= $(this);
				var ul 					= $("ul", obj);
				var li 					= $("li", ul);
				var span 				= $("span", obj);
				var active 				= -1;
				var numberOfLiElement 	= ul.find('li').length;
				var $document 			= $(document);

				unbind = function(){
		    		ul.unbind("mouseleave");
		    		$document.unbind("keyup");
		    		$document.unbind("click");
		    	}

				var input= '<input type="text" value="' + span.text() + '" name="' + obj.attr('id') + '" style="display:none"/>';
				obj.append(input);

				obj.css("background", "url(img/" + o.image + ") no-repeat");
				obj.css("width", o.width);
				obj.css("height", o.height);

				span.css("text-align", o.textAlign);
				span.css("margin-left", o.textIndent + "px");

				ul.css('width', o.width);
				li.css('width', o.width);

				obj.click(function(){
					ul.css("opacity", "1");
					ul.css("display", "block");

					ul.bind("mouseleave", function(){
						 time = setTimeout(function() {
							 ul.animate({
								opacity: 0
							  }, 500, function() {
							  	unbind();
								ul.css("display", "none");
							 });
						}, 400);
					});

					$document.bind("keyup", function(e){
					    if (e.keyCode == 40) {
					        if(active < numberOfLiElement - 1){
					       		active++;
					       		ul.find('li').removeClass('active');
					       		ul.find('li:eq(' + active + ')').addClass('active');
					       	}
					    }
					    if (e.keyCode == 38) {
					        if(active > 0){
					       		active--;
					       		ul.find('li').removeClass('active');
					       		ul.find('li:eq(' + active + ')').addClass('active');
					       	}
					    }
					});

					time2 = setTimeout(function() {
						$document.bind("click", function(){
							ul.animate({
								opacity: 0
								}, 500, function() {
									unbind();
									ul.css("display", "none");
							});
						});
					}, 400);
				});

				li.on("click",function(){
					unbind();
					span.text($(this).text());
					$('input[name="' + obj.attr('id') + '"]').attr("value", $(this).text());

					ul.animate({
						opacity: 0
						}, 500, function() {
							ul.css("display", "none");
					});
				});
            });
        }
    });
})(jQuery);<|endoftext|>


<commit_before>/*
 * freepano - WebGL panorama viewer
 *
 * Copyright (c) 2014 FOXEL SA - http://foxel.ch
 * Please read <http://foxel.ch/license> for more information.
 *
 *
 * Author(s):
 *
 *      Kevin Velickovic <k.velickovic@foxel.ch>
 *
 *
 * Contributor(s):
 *
 *      Luc Deschenaux <l.deschenaux@foxel.ch>
 *      Alexandre Kraft
 *
 *
 * This file is part of the FOXEL project <http://foxel.ch>.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Additional Terms:
 *
 *      You are required to preserve legal notices and author attributions in
 *      that material or in the Appropriate Legal Notices displayed by works
 *      containing it.
 *
 *      You are required to attribute the work as explained in the "Usage and
 *      Attribution" section of <http://foxel.ch/license>.
 */

// Plugin constructor
function Map(options)
{
    // merge specified options with default options and extend this instance
    $.extend(true,this,Map.prototype.defaults,options);

    // Initialize plugin
    this.init();
}

// Extend plugin prototype
$.extend(true, Map.prototype, {

    // default values
    defaults: {
        // active
        active: false
    },

    show: function map_show(map) {

        // dom
        var pano = map.panorama;
        var container = $(pano.container);

        // Create map container
        var mapContainer = $('
',{
            id: 'mapContainer'
        });

        // Append map
        container.append(mapContainer);

        var zoom = {
            base: 4,
            min: 3,
            max: 25,
            native: 18,
            bounds: 17
        };

        // Create leaflet map object
        var map = L.map('mapContainer', {
            keyboard: false,
            scrollWheelZoom: true,
            minZoom: zoom.min,
            maxZoom: zoom.max
        }).setView([51.505, -0.09], zoom.base);

        // add an OpenStreetMap tile layer
        L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
            attribution: '© OpenStreetMap contributors',
            minZoom: zoom.min,
            maxZoom: zoom.max,
            maxNativeZoom: zoom.native
        }).addTo(map);

        // Compute icon sizes and anchors
        var iconSize     = [22.5, 36.25];
        var shadowSize   = [40.75, 36.25];
        var iconAnchor   = [iconSize[0] / 2, 36.25];
        var shadowAnchor = [iconSize[0] / 2, 36.25];

        // Create marker icon
        var markerIcon = L.icon({
            iconUrl:     'img/marker_icon.png', // Icon image file path
            shadowUrl:   'img/marker_shadow.png', // Shadow image file path

            iconSize:     iconSize, // size of the icon
            shadowSize:   shadowSize, // size of the shadow

            iconAnchor:   iconAnchor, // point of the icon which will correspond to marker's location
            shadowAnchor: shadowAnchor,  // the same for the shadow

            popupAnchor:  [0, 0] // point from which the popup should open relative to the iconAnchor
        });

        // Create highlighted marker icon
        var markerIcon_Highlighted = L.icon({
            iconUrl:     'img/marker_icon_highlight.png', // Icon image file path
            shadowUrl:   'img/marker_shadow.png', // Shadow image file path

            iconSize:     iconSize, // size of the icon
            shadowSize:   shadowSize, // size of the shadow

            iconAnchor:   iconAnchor, // point of the icon which will correspond to marker's location
            shadowAnchor: shadowAnchor,  // the same for the shadow

            popupAnchor:  [0, 0] // point from which the popup should open relative to the iconAnchor
        });

        var markers = [];

        // Iterate over panoramas and create markers
        $.each(pano.list.images, function( index, value ) {

            // No geo coordinates
            if (value.coords === undefined || value.coords === null)
                return;

            // Icon
            var icon = markerIcon;

            // Use first image if currentImage is not defined
            if (pano.list.currentImage === undefined)
                pano.list.currentImage = Object.keys(pano.list.images)[0];

            // Determine if the marker is highlighted
            if ( pano.list.currentImage == index )
                icon = markerIcon_Highlighted;

            // Create marker
            var marker = L.marker([value.coords.lat, value.coords.lon], {icon: icon})
            .on('click', function(e) {

                // Reset all markers to default icon
                $.each(markers, function( index, value ) {
                    value.setIcon(markerIcon);
                });

                // Set highlighted icon for marker
                marker.setIcon(markerIcon_Highlighted);

                // Check if panorama has changed, then change it
                if(e.target.panorama.index != pano.list.currentImage)
                    pano.list.show(e.target.panorama.index);
            });

            // Extend marker with panorama object
            $.extend( marker, {
                panorama: {
                    index: index,
                    value: value
                }
            });

            // Add marker to array
            markers.push( marker )

            // Add marker to map
            marker.addTo(map)

        });

        // No elligible markers
        if (markers.length == 0) {
            this.hide(this);
            return;
        }

        // Create makers featureGroup
        var markersGroup = new L.featureGroup(markers);

        // Fit map to markers
        map.fitBounds(markersGroup.getBounds());

        // Unzoom from bounds
        if (map.getZoom() > zoom.bounds)
            map.setZoom(zoom.bounds);
        else
            map.setZoom(map.getZoom()-1);

    },

    hide: function map_hide(map) {
        $("#mapContainer").remove();
    },

    init: function map_init() {
        var map = this;

        if (map.active)
            map.show(map);

        // watch touch move properties
        watch(map,['active'], function() {
            if (map.active)
                map.show(map);
            else
                map.hide(map);
        });

        map.callback();
    }

});

// register freepano.map plugin

$.extend(Map.prototype,{
    // save pointer to Panorama.prototype.init in Map.prototype
    panorama_init: Panorama.prototype.init
});

$.extend(Panorama.prototype,{

  // hook Map.prototype.init to Panorama.prototype.init
  init: function panorama_init() {

    var panorama=this;

    // skip Map instantiation if map preferences undefined in panorama
    if (panorama.map!==undefined) {

      // or if map is already instantiated
      if (!(panorama.map instanceof Map)) {

        // intantiate map
        panorama.map=new Map($.extend(true,{

          // pass panorama instance pointer to map instance
          panorama: panorama,

          // to be run when map is instantiated
          callback: function() {

            // chain with old panorama.prototype.init on callback
            Map.prototype.panorama_init.call(panorama);

          }

        },panorama.map));

      }

    } else {

      // chain with old panorama.prototype.init
      Map.prototype.panorama_init.call(panorama);

    }
  }
});

<commit_msg>Minor zoom bounds change<commit_after>/*
 * freepano - WebGL panorama viewer
 *
 * Copyright (c) 2014 FOXEL SA - http://foxel.ch
 * Please read <http://foxel.ch/license> for more information.
 *
 *
 * Author(s):
 *
 *      Kevin Velickovic <k.velickovic@foxel.ch>
 *
 *
 * Contributor(s):
 *
 *      Luc Deschenaux <l.deschenaux@foxel.ch>
 *      Alexandre Kraft
 *
 *
 * This file is part of the FOXEL project <http://foxel.ch>.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Additional Terms:
 *
 *      You are required to preserve legal notices and author attributions in
 *      that material or in the Appropriate Legal Notices displayed by works
 *      containing it.
 *
 *      You are required to attribute the work as explained in the "Usage and
 *      Attribution" section of <http://foxel.ch/license>.
 */

// Plugin constructor
function Map(options)
{
    // merge specified options with default options and extend this instance
    $.extend(true,this,Map.prototype.defaults,options);

    // Initialize plugin
    this.init();
}

// Extend plugin prototype
$.extend(true, Map.prototype, {

    // default values
    defaults: {
        // active
        active: false
    },

    show: function map_show(map) {

        // dom
        var pano = map.panorama;
        var container = $(pano.container);

        // Create map container
        var mapContainer = $('
',{
            id: 'mapContainer'
        });

        // Append map
        container.append(mapContainer);

        var zoom = {
            base: 4,
            min: 3,
            max: 25,
            native: 18,
            bounds: 18
        };

        // Create leaflet map object
        var map = L.map('mapContainer', {
            keyboard: false,
            scrollWheelZoom: true,
            minZoom: zoom.min,
            maxZoom: zoom.max
        }).setView([51.505, -0.09], zoom.base);

        // add an OpenStreetMap tile layer
        L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
            attribution: '© OpenStreetMap contributors',
            minZoom: zoom.min,
            maxZoom: zoom.max,
            maxNativeZoom: zoom.native
        }).addTo(map);

        // Compute icon sizes and anchors
        var iconSize     = [22.5, 36.25];
        var shadowSize   = [40.75, 36.25];
        var iconAnchor   = [iconSize[0] / 2, 36.25];
        var shadowAnchor = [iconSize[0] / 2, 36.25];

        // Create marker icon
        var markerIcon = L.icon({
            iconUrl:     'img/marker_icon.png', // Icon image file path
            shadowUrl:   'img/marker_shadow.png', // Shadow image file path

            iconSize:     iconSize, // size of the icon
            shadowSize:   shadowSize, // size of the shadow

            iconAnchor:   iconAnchor, // point of the icon which will correspond to marker's location
            shadowAnchor: shadowAnchor,  // the same for the shadow

            popupAnchor:  [0, 0] // point from which the popup should open relative to the iconAnchor
        });

        // Create highlighted marker icon
        var markerIcon_Highlighted = L.icon({
            iconUrl:     'img/marker_icon_highlight.png', // Icon image file path
            shadowUrl:   'img/marker_shadow.png', // Shadow image file path

            iconSize:     iconSize, // size of the icon
            shadowSize:   shadowSize, // size of the shadow

            iconAnchor:   iconAnchor, // point of the icon which will correspond to marker's location
            shadowAnchor: shadowAnchor,  // the same for the shadow

            popupAnchor:  [0, 0] // point from which the popup should open relative to the iconAnchor
        });

        var markers = [];

        // Iterate over panoramas and create markers
        $.each(pano.list.images, function( index, value ) {

            // No geo coordinates
            if (value.coords === undefined || value.coords === null)
                return;

            // Icon
            var icon = markerIcon;

            // Use first image if currentImage is not defined
            if (pano.list.currentImage === undefined)
                pano.list.currentImage = Object.keys(pano.list.images)[0];

            // Determine if the marker is highlighted
            if ( pano.list.currentImage == index )
                icon = markerIcon_Highlighted;

            // Create marker
            var marker = L.marker([value.coords.lat, value.coords.lon], {icon: icon})
            .on('click', function(e) {

                // Reset all markers to default icon
                $.each(markers, function( index, value ) {
                    value.setIcon(markerIcon);
                });

                // Set highlighted icon for marker
                marker.setIcon(markerIcon_Highlighted);

                // Check if panorama has changed, then change it
                if(e.target.panorama.index != pano.list.currentImage)
                    pano.list.show(e.target.panorama.index);
            });

            // Extend marker with panorama object
            $.extend( marker, {
                panorama: {
                    index: index,
                    value: value
                }
            });

            // Add marker to array
            markers.push( marker )

            // Add marker to map
            marker.addTo(map)

        });

        // No elligible markers
        if (markers.length == 0) {
            this.hide(this);
            return;
        }

        // Create makers featureGroup
        var markersGroup = new L.featureGroup(markers);

        // Fit map to markers
        map.fitBounds(markersGroup.getBounds());

        // Unzoom from bounds
        if (map.getZoom() > zoom.bounds)
            map.setZoom(zoom.bounds);
        else
            map.setZoom(map.getZoom()-1);

    },

    hide: function map_hide(map) {
        $("#mapContainer").remove();
    },

    init: function map_init() {
        var map = this;

        if (map.active)
            map.show(map);

        // watch touch move properties
        watch(map,['active'], function() {
            if (map.active)
                map.show(map);
            else
                map.hide(map);
        });

        map.callback();
    }

});

// register freepano.map plugin

$.extend(Map.prototype,{
    // save pointer to Panorama.prototype.init in Map.prototype
    panorama_init: Panorama.prototype.init
});

$.extend(Panorama.prototype,{

  // hook Map.prototype.init to Panorama.prototype.init
  init: function panorama_init() {

    var panorama=this;

    // skip Map instantiation if map preferences undefined in panorama
    if (panorama.map!==undefined) {

      // or if map is already instantiated
      if (!(panorama.map instanceof Map)) {

        // intantiate map
        panorama.map=new Map($.extend(true,{

          // pass panorama instance pointer to map instance
          panorama: panorama,

          // to be run when map is instantiated
          callback: function() {

            // chain with old panorama.prototype.init on callback
            Map.prototype.panorama_init.call(panorama);

          }

        },panorama.map));

      }

    } else {

      // chain with old panorama.prototype.init
      Map.prototype.panorama_init.call(panorama);

    }
  }
});

<|endoftext|>


<commit_before>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Simple class additions for links.
//>>label: Link Classes

define( [ "jquery" ], function( $ ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

$( document ).bind( "pagecreate create", function( e ){

	//links within content areas
	$( e.target )
		.find( "a" )
		.not( ".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')" )
		.addClass( "ui-link" );

});

})( jQuery );

//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<commit_msg>links module respects data-enhance=false<commit_after>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Simple class additions for links.
//>>label: Link Classes

define( [ "jquery" ], function( $ ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

$( document ).bind( "pagecreate create", function( e ){

	//links within content areas, tests included with page
	$( e.target )
		.find( "a" )
		.jqmEnhanceable()
		.not( ".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')" )
		.addClass( "ui-link" );

});

})( jQuery );

//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<|endoftext|>


<commit_before>;(function($) {

    $.fn.validateForm = function(typeOptions, attrMsgs, notify) {

        //Extend the defaults with provided options
        typeOptions = $.extend(true, {}, $.fn.validateForm.typeOptions, typeOptions);
        attrMsgs = $.extend(true, {}, $.fn.validateForm.attrMsgs, attrMsgs);
        notify = $.extend(true, {}, $.fn.validateForm.notify, notify);

            // Validate input value according to the pattern
        var validateRegExp = function(inputValue, pattern) {
                if(pattern) {
                    try {
                        var regExpObj = new RegExp(pattern);
                        return regExpObj.test(inputValue);
                    } catch (err) {
                        notify.patternError(pattern, err);
                        return false;
                    };
                } else {
                    return false;
                };
            },

            // Validate pattern
            validatePattern = function(fieldOpts, pattern, message, formState) {
                fieldOpts.isInvalid = !validateRegExp(fieldOpts.value, pattern);
                fieldOpts.message = message;
                processField(fieldOpts, formState);
            },

            /*  Validate required field
            */
            validateRequiredField = function(fieldOpts, formState) {
                //field is invalid if it has no value
                fieldOpts.isInvalid = !fieldOpts.value;

                if (fieldOpts.type === "checkbox") {
                    //checkbox may have value but may be unchecked
                    fieldOpts.isInvalid = !fieldOpts.jField.is(":checked");
                };

                processField(fieldOpts, formState);
            },

            /*  Validate required group of fields
            */
            validateRequiredFieldGroup = function(fieldOpts, formState) {
                //field group is invalid if it has no checked fields
                fieldOpts.isInvalid = fieldOpts.jField.find(":checked").length === 0;

                processField(fieldOpts, formState);

                //TODO: remember uniqueID for fieldOpts, or maybe not
            },

            /*  Process a field, manipulate with formState object and warnings
            */
            processField = function(fieldOpts, formState) {
                // Add id attribute for a field if it does not have it
                if ( (typeof fieldOpts.id === "undefined") || !$.trim(fieldOpts.id) ) {
                    fieldOpts.id = addFieldId(fieldOpts.jField, fieldOpts.uniqueId);
                    fieldOpts.uniqueId++; //increment uniqueId
                };

                if (fieldOpts.isInvalid) {
                    //Track form state, send warning
                    formState[fieldOpts.id] = fieldOpts.message;
                    if (fieldOpts.toNotify) {
                        notify.warning(fieldOpts.id, fieldOpts.message);
                    };
                } else {
                    //Field is valid, remove from formState object if set so
                    if (fieldOpts.toClearState && formState[fieldOpts.id]) {
                        delete formState[fieldOpts.id];
                    };
                };
            },

            // Add id attribute for a field that does not have it yet
            addFieldId = function($field, uniqueId) {
                //TODO: make field prefix defined by user
                var fieldId = "field_" + uniqueId;
                $field.attr("id", fieldId);
                return fieldId;
            };

        return this.each(function() {
            var parentForm = $(this),   //a form to validate
                uniqueId = 1,           // will increment and is used for id generation
                formState = {};         //formSate object contains warning messages for fields

            //TODO: define better rules for novalidation/validation
            /* If a user disabled html5 validation,
            ** then validate everything even if browser supports html5 form validation
            */
            /*if ((parentForm.attr("novalidate") !== undefined) || (!!parentForm.find("[formnovalidate]").length)) {
                //no validation required
                return;
            }; */

            //track every field change
            parentForm.on('change', function(e) {
                var $field = $(e.target),                       //changed field
                    $fParent = $field.parent(),                 //parent of field
                    fieldOpts = {
                        jField: $field,
                        id: $field.attr("id"),
                        uniqueId: uniqueId,
                        type: $field.attr("type"),
                        value: $.trim($field.val()),
                        pattern: $field.attr("pattern"),
                        isRequired: typeof $field.attr("required") !== "undefined",
                        message: attrMsgs.required,     //for now we set this as required
                        isInvalid: false,   //will override that later for each check
                        toNotify: true,     //notify when a field has changed
                        toClearState: true  //clear formState object when a field has changed to be valid
                    };

                //if required attribute is set
                if ( fieldOpts.isRequired ) {
                    validateRequiredField(fieldOpts, formState);
                };
                //console.log("Form State", formState);

                //check multiple checkboxes,
                //if field is a checkbox with required attribute set for parent,
                if ( (fieldOpts.type === "checkbox") && (typeof $fParent.data("required") !== "undefined") ) {
                    var fieldGroupOpts = {
                            jField: $fParent,
                            id: $fParent.attr("id"),
                            uniqueId: uniqueId,
                            //type: $field.attr("type"),
                            //value: $.trim($field.val()),
                            //pattern: $field.attr("pattern"),
                            //isRequired: typeof $field.attr("required") !== "undefined",
                            message: attrMsgs.required,     //for now we set this as required
                            isInvalid: false,   //will override that later for each check
                            toNotify: true,     //do not notify when a field has changed, we do it later
                            toClearState: true  //do not clear formState, nothing is filled on submit
                        };

                    validateRequiredFieldGroup(fieldGroupOpts, formState);

                    //save uniqueId so that next time we set new id it will be unique (incremented)
                    uniqueId = fieldGroupOpts.uniqueId;

                };

                //validate patterns
                if (fieldOpts.value && fieldOpts.type !== "checkbox") {
                    //if attribute pattern is set, use it to validate value
                    //inline pattern takes presidence over patterns defined with plugin options
                    if ( typeof fieldOpts.pattern !== "undefined" ) {
                        validatePattern(fieldOpts, fieldOpts.pattern, attrMsgs.pattern, formState);
                    } else if (typeof typeOptions[fieldOpts.type] !== "undefined") {
                        //if validation rules exist for this field type, validate
                        var fTypeOpts = typeOptions[fieldOpts.type];
                        validatePattern(fieldOpts, fTypeOpts.pattern, fTypeOpts.message, formState);
                    };
                };

                //save uniqueId so that next time we set new id it will be unique (incremented)
                uniqueId = fieldOpts.uniqueId;

                //console.log(formState);

            });

            parentForm.on('submit', function(e) {
                //We still can have empty required fields or empty required radio button/checkbox groups.
                //No need to check for type attr again because if a user has changed those fields, we have all warnings in our object

                //Check all required fields
                $(this).find("[required]").each(function() {
                    var $field = $(this),             //required field
                        fieldOpts = {
                            jField: $field,
                            id: $field.attr("id"),
                            uniqueId: uniqueId,
                            type: $field.attr("type"),
                            value: $.trim($field.val()),
                            //pattern: $field.attr("pattern"),
                            //isRequired: typeof $field.attr("required") !== "undefined",
                            message: attrMsgs.required,     //for now we set this as required
                            isInvalid: false,   //will override that later for each check
                            toNotify: false,    //do not notify when a field has changed, we do it later
                            toClearState: false //do not clear formState, nothing is fixed on submit
                        };

                    validateRequiredField(fieldOpts, formState);

                    //save uniqueId so that next time we set new id it will be unique (incremented)
                    uniqueId = fieldOpts.uniqueId;

                });

                //Check all required groups
                $(this).find("[data-required]").each(function() {
                    var $field = $(this),             //required field group
                        fieldOpts = {
                            jField: $field,
                            id: $field.attr("id"),
                            uniqueId: uniqueId,
                            //type: $field.attr("type"),
                            //value: $.trim($field.val()),
                            //pattern: $field.attr("pattern"),
                            //isRequired: typeof $field.attr("required") !== "undefined",
                            message: attrMsgs.required,     //for now we set this as required
                            isInvalid: false,   //will override that later for each check
                            toNotify: false,    //do not notify when a field has changed, we do it later
                            toClearState: false //do not clear formState, nothing is filled on submit
                        };

                    validateRequiredFieldGroup(fieldOpts, formState);

                    //save uniqueId so that next time we set new id it will be unique (incremented)
                    uniqueId = fieldOpts.uniqueId;

                });

                if ($.isEmptyObject(formState)) {
                    //if formState object is still empty, then the form passed validation
                    return true;
                } else {
                    //formState object has warnings, so show them all, do not allow form submition
                    var fieldId;
                    for (fieldId in formState) {
                        notify.warning(fieldId, formState[fieldId]);
                    };
                    //TODO: some global warning about form submition failed
                    return false;
                };
            });
        });
    };

    //TODO: maybe it is better to combine all options into one object
    //Set the default validation options for input fields with types as below:
    $.fn.validateForm.typeOptions = {
        "number": {
            "pattern": "^[+-]?\\d+(\.\\d+)?$",
            "message": "Please enter correct number."
        },
        "email": {
            "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$",
            "message": "Please enter valid email."
        },
        "url": {
            "pattern": "^(http:\/\/www.|https:\/\/www.|ftp:\/\/www.|www.){1}([0-9A-Za-z]+\.)",
            "message": "Please enter valid URL."
        },
        "tel": {
            "pattern": "^\\(?(\\d{3})\\)?[- ]?(\\d{3})[- ]?(\\d{4})$",
            "message": "Please enter valid phone number."
        }
    };

    //Set the default validation options for input fields with attributes as below:
    $.fn.validateForm.attrMsgs = {
        required: "Please enter all required fields.",
        pattern: "Please use correct format."
    };

    //Notify that something is not valid
    $.fn.validateForm.notify = {
        warning: function(fieldId, message) {
            console.log('Warning for field: ', fieldId, ': ', message);
        },
        patternError: function(pattern, err) {
            console.log('Pattern is wrong: ', pattern, ': ', err);
        }
    };

})(jQuery);<commit_msg>field object refactoring<commit_after>;(function($) {

    $.fn.validateForm = function(typeOptions, attrMsgs, notify) {

        //Extend the defaults with provided options
        typeOptions = $.extend(true, {}, $.fn.validateForm.typeOptions, typeOptions);
        attrMsgs = $.extend(true, {}, $.fn.validateForm.attrMsgs, attrMsgs);
        notify = $.extend(true, {}, $.fn.validateForm.notify, notify);

            // Validate input value according to the pattern
        var validateRegExp = function(inputValue, pattern) {
                if(pattern) {
                    try {
                        var regExpObj = new RegExp(pattern);
                        return regExpObj.test(inputValue);
                    } catch (err) {
                        notify.patternError(pattern, err);
                        return false;
                    };
                } else {
                    return false;
                };
            },

            // Validate pattern
            validatePattern = function(fieldOpts, pattern, message, formState) {
                fieldOpts.isInvalid = !validateRegExp(fieldOpts.value, pattern);
                fieldOpts.message = message;
                processField(fieldOpts, formState);
            },

            /*  Validate required field
            */
            validateRequiredField = function(fieldOpts, formState) {
                fieldOpts.message = attrMsgs.required;

                //field is invalid if it has no value
                fieldOpts.isInvalid = !fieldOpts.value;

                if (fieldOpts.type === "checkbox") {
                    //checkbox may have value but may be unchecked
                    fieldOpts.isInvalid = !fieldOpts.jField.is(":checked");
                };

                processField(fieldOpts, formState);
            },

            /*  Validate required group of fields
            */
            validateRequiredFieldGroup = function(fieldOpts, formState) {
                fieldOpts.message = attrMsgs.required;

                //field group is invalid if it has no checked fields
                fieldOpts.isInvalid = fieldOpts.jField.find(":checked").length === 0;

                processField(fieldOpts, formState);
            },

            /*  Process a field, manipulate with formState object and warnings
            */
            processField = function(fieldOpts, formState) {
                // Add id attribute for a field if it does not have it
                if ( (typeof fieldOpts.id === "undefined") || !$.trim(fieldOpts.id) ) {
                    fieldOpts.id = addFieldId(fieldOpts.jField, fieldOpts.uniqueId);
                    fieldOpts.uniqueId++; //increment uniqueId
                };

                if (fieldOpts.isInvalid) {
                    //Track form state, send warning
                    formState[fieldOpts.id] = fieldOpts.message;
                    if (fieldOpts.toNotify) {
                        notify.warning(fieldOpts.id, fieldOpts.message);
                    };
                } else {
                    //Field is valid, remove from formState object if set so
                    if (fieldOpts.toClearState && formState[fieldOpts.id]) {
                        delete formState[fieldOpts.id];
                    };
                };
            },

            // Add id attribute for a field that does not have it yet
            addFieldId = function($field, uniqueId) {
                //TODO: make field prefix defined by user
                var fieldId = "field_" + uniqueId;
                $field.attr("id", fieldId);
                return fieldId;
            },

            fieldOptions = function($field, uniqueId, toNotify, toClearState) {
                return {
                    jField: $field,
                    id: $field.attr("id"),
                    uniqueId: uniqueId,
                    type: $field.attr("type"),
                    value: $.trim($field.val()),
                    pattern: $field.attr("pattern"),
                    isRequired: typeof $field.attr("required") !== "undefined",
                    toNotify: (typeof toNotify === "undefined") ? true : toNotify,     //notify when a field has changed
                    toClearState: (typeof toClearState === "undefined") ? true : toClearState  //clear formState object when a field has changed to be valid
                };
            };

        return this.each(function() {
            var parentForm = $(this),   //a form to validate
                uniqueId = 1,           // will increment and is used for id generation
                formState = {};         //formSate object contains warning messages for fields

            //TODO: define better rules for novalidation/validation
            /* If a user disabled html5 validation,
            ** then validate everything even if browser supports html5 form validation
            */
            /*if ((parentForm.attr("novalidate") !== undefined) || (!!parentForm.find("[formnovalidate]").length)) {
                //no validation required
                return;
            }; */

            //track every field change
            parentForm.on('change', function(e) {
                var $field = $(e.target),                       //changed field
                    $fParent = $field.parent(),                 //parent of field
                    fieldOpts = fieldOptions($field, uniqueId);

                //if required attribute is set
                if ( fieldOpts.isRequired ) {
                    validateRequiredField(fieldOpts, formState);
                };
                //console.log("Form State", formState);

                //check multiple checkboxes,
                //if field is a checkbox with required attribute set for parent,
                if ( (fieldOpts.type === "checkbox") && (typeof $fParent.data("required") !== "undefined") ) {
                    var fieldGroupOpts = fieldOptions($fParent, uniqueId);

                    validateRequiredFieldGroup(fieldGroupOpts, formState);

                    //save uniqueId so that next time we set new id it will be unique (incremented)
                    uniqueId = fieldGroupOpts.uniqueId;

                };

                //validate patterns
                if (fieldOpts.value && fieldOpts.type !== "checkbox") {
                    //if attribute pattern is set, use it to validate value
                    //inline pattern takes presidence over patterns defined with plugin options
                    if ( typeof fieldOpts.pattern !== "undefined" ) {
                        validatePattern(fieldOpts, fieldOpts.pattern, attrMsgs.pattern, formState);
                    } else if (typeof typeOptions[fieldOpts.type] !== "undefined") {
                        //if validation rules exist for this field type, validate
                        var fTypeOpts = typeOptions[fieldOpts.type];
                        validatePattern(fieldOpts, fTypeOpts.pattern, fTypeOpts.message, formState);
                    };
                };

                //save uniqueId so that next time we set new id it will be unique (incremented)
                //TODO:be careful with overrding this to lower value
                if (uniqueId < fieldOpts.uniqueId) {
                    uniqueId = fieldOpts.uniqueId;
                };

                //console.log(formState);

            });

            parentForm.on('submit', function(e) {
                //We still can have empty required fields or empty required radio button/checkbox groups.
                //No need to check for type attr again because if a user has changed those fields, we have all warnings in our object

                //Check all required fields
                $(this).find("[required]").each(function() {
                    var $field = $(this),             //required field
                        fieldOpts = fieldOptions($field, uniqueId, false, false);

                    validateRequiredField(fieldOpts, formState);

                    //save uniqueId so that next time we set new id it will be unique (incremented)
                    uniqueId = fieldOpts.uniqueId;
                });

                //Check all required groups
                $(this).find("[data-required]").each(function() {
                    var $field = $(this),             //required field group
                        fieldOpts = fieldOptions($field, uniqueId, false, false);

                    validateRequiredFieldGroup(fieldOpts, formState);

                    //save uniqueId so that next time we set new id it will be unique (incremented)
                    uniqueId = fieldOpts.uniqueId;
                });

                if ($.isEmptyObject(formState)) {
                    //if formState object is still empty, then the form passed validation
                    return true;
                } else {
                    //formState object has warnings, so show them all, do not allow form submition
                    var fieldId;
                    for (fieldId in formState) {
                        notify.warning(fieldId, formState[fieldId]);
                    };
                    //TODO: some global warning about form submition failed
                    return false;
                };
            });
        });
    };

    //TODO: maybe it is better to combine all options into one object
    //Set the default validation options for input fields with types as below:
    $.fn.validateForm.typeOptions = {
        "number": {
            "pattern": "^[+-]?\\d+(\.\\d+)?$",
            "message": "Please enter correct number."
        },
        "email": {
            "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$",
            "message": "Please enter valid email."
        },
        "url": {
            "pattern": "^(http:\/\/www.|https:\/\/www.|ftp:\/\/www.|www.){1}([0-9A-Za-z]+\.)",
            "message": "Please enter valid URL."
        },
        "tel": {
            "pattern": "^\\(?(\\d{3})\\)?[- ]?(\\d{3})[- ]?(\\d{4})$",
            "message": "Please enter valid phone number."
        }
    };

    //Set the default validation options for input fields with attributes as below:
    $.fn.validateForm.attrMsgs = {
        required: "Please enter all required fields.",
        pattern: "Please use correct format."
    };

    //Notify that something is not valid
    $.fn.validateForm.notify = {
        warning: function(fieldId, message) {
            console.log('Warning for field: ', fieldId, ': ', message);
        },
        patternError: function(pattern, err) {
            console.log('Pattern is wrong: ', pattern, ': ', err);
        }
    };

})(jQuery);<|endoftext|>


<commit_before>// Copyright 2020, University of Colorado Boulder

/**
 * This prints out (in JSON form) the tests and operations requested for continuous testing for whatever is in master
 * at this point.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

'use strict';

const getActiveRepos = require( './common/getActiveRepos' );
const getRepoList = require( './common/getRepoList' );
const fs = require( 'fs' );

const repos = getActiveRepos();
const phetioRepos = getRepoList( 'testable-phet-io' );
const phetioReposValidated = getRepoList( 'testable-phet-io-validated' );
const runnableRepos = getRepoList( 'testable-runnables' );
const interactiveDescriptionRepos = getRepoList( 'interactive-descriptions' );
const phetioNoState = getRepoList( 'phet-io-state-unsupported' );
const unitTestRepos = getRepoList( 'unit-tests' );

/**
 * {Array.<string>} test
 * {string} type
 * {string} [url]
 * {string} [repo]
 * {string} [queryParameters]
 * {string} [testQueryParameters]
 * {boolean} [es5]
 * {string} [brand]
 * {number} [priority]
 * {Array.<string>} buildDependencies
 */
const tests = [];

tests.push( {
  test: [ 'perennial', 'lint-everything' ],
  type: 'lint-everything',
  priority: 100
} );

// phet brand builds
[
  ...runnableRepos,
  'scenery',
  'kite',
  'dot'
].forEach( repo => {
  tests.push( {
    test: [ repo, 'build' ],
    type: 'build',
    brands: phetioRepos.includes( repo ) ? [ 'phet', 'phet-io' ] : [ 'phet' ],
    repo: repo,
    priority: 1
  } );
} );

// lints
repos.forEach( repo => {
  if ( fs.existsSync( `../${repo}/Gruntfile.js` ) ) {
    tests.push( {
      test: [ repo, 'lint' ],
      type: 'lint',
      repo: repo,
      priority: 8
    } );
  }
} );

runnableRepos.forEach( repo => {
  tests.push( {
    test: [ repo, 'fuzz', 'unbuilt' ],
    type: 'sim-test',
    url: `${repo}/${repo}_en.html`,
    queryParameters: 'brand=phet&ea&fuzz&memoryLimit=1000'
  } );

  tests.push( {
    test: [ repo, 'xss-fuzz' ],
    type: 'sim-test',
    url: `${repo}/${repo}_en.html`,
    queryParameters: 'brand=phet&ea&fuzz&stringTest=xss&memoryLimit=1000',
    testQueryParameters: 'duration=40000',
    priority: 0.3
  } );

  tests.push( {
    test: [ repo, 'fuzz', 'built' ],
    type: 'sim-test',
    url: `${repo}/build/phet/${repo}_en_phet.html`,
    queryParameters: 'fuzz&memoryLimit=1000',
    testQueryParameters: 'duration=80000',

    // We want to elevate the priority so that we get a more even balance (we can't test these until they are built,
    // which doesn't happen always)
    priority: 2,

    brand: 'phet',
    buildDependencies: [ repo ],
    es5: true
  } );

  if ( phetioRepos.includes( repo ) ) {
    tests.push( {
      test: [ repo, 'fuzz', 'built-phet-io' ],
      type: 'sim-test',
      url: `${repo}/build/phet-io/${repo}_all_phet-io.html`,
      queryParameters: 'fuzz&memoryLimit=1000&phetioStandalone',
      testQueryParameters: 'duration=80000',

      brand: 'phet-io',
      buildDependencies: [ repo ],
      es5: true
    } );
  }
} );

phetioRepos.forEach( repo => {
  const validated = phetioReposValidated.includes( repo );
  const validatedParam = validated ? '&phetioValidateTandems' : '';

  tests.push( {
    test: [ repo, 'phet-io-fuzz', 'unbuilt' ],
    type: 'sim-test',
    url: `${repo}/${repo}_en.html`,
    queryParameters: 'brand=phet-io&phetioStandalone&ea' + validatedParam + '&fuzz&memoryLimit=1000'
  } );

  // fuzz test important wrappers
  tests.push( {
    test: [ repo, 'phet-io-studio-fuzz', 'unbuilt' ],
    type: 'wrapper-test',
    url: `studio/?sim=${repo}&phetioDebug&fuzz`,
    testQueryParameters: 'duration=60000'
  } );

  // only test state on phet-io sims that support it
  phetioNoState.indexOf( repo ) === -1 && tests.push( {
    test: [ repo, 'phet-io-state-fuzz', 'unbuilt' ],
    type: 'wrapper-test',
    url: `phet-io-wrappers/state/?sim=${repo}&phetioDebug&fuzz`,
    testQueryParameters: 'duration=60000'
  } );

  tests.push( {
    test: [ repo, 'phet-io-mirror-inputs-fuzz', 'unbuilt' ],
    type: 'wrapper-test',
    url: `phet-io-wrappers/mirror-inputs/?sim=${repo}&phetioDebug&fuzz`,
    testQueryParameters: 'duration=60000'
  } );
} );

// accessible tests
interactiveDescriptionRepos.forEach( repo => {
  tests.push( {
    test: [ repo, 'interactive-description-fuzz', 'unbuilt' ],
    type: 'sim-test',
    url: `${repo}/${repo}_en.html`,
    queryParameters: 'brand=phet&ea&fuzz&supportsDescriptions&memoryLimit=1000',
    testQueryParameters: 'duration=40000'
  } );

  tests.push( {
    test: [ repo, 'interactive-description-fuzzBoard', 'unbuilt' ],
    type: 'sim-test',
    url: `${repo}/${repo}_en.html`,
    queryParameters: 'brand=phet&ea&fuzzBoard&supportsDescriptions&memoryLimit=1000',
    testQueryParameters: 'duration=40000'
  } );

  tests.push( {
    test: [ repo, 'interactive-description-fuzz', 'built' ],
    type: 'sim-test',
    url: `${repo}/build/phet/${repo}_en_phet.html`,
    queryParameters: 'fuzz&supportsDescriptions&memoryLimit=1000',
    testQueryParameters: 'duration=40000',

    brand: 'phet',
    buildDependencies: [ repo ],
    es5: true
  } );

  tests.push( {
    test: [ repo, 'interactive-description-fuzzBoard', 'built' ],
    type: 'sim-test',
    url: `${repo}/build/phet/${repo}_en_phet.html`,
    queryParameters: 'fuzzBoard&supportsDescriptions&memoryLimit=1000',
    testQueryParameters: 'duration=40000',

    brand: 'phet',
    buildDependencies: [ repo ],
    es5: true
  } );
} );

// phet-io wrappers tests for each PhET-iO Sim
phetioRepos.forEach( repo => {
  [ false, true ].forEach( useAssert => {
    tests.push( {
      test: [ repo, 'phet-io-wrappers-tests', useAssert ? 'assert' : 'no-assert' ],
      type: 'qunit-test',
      url: 'phet-io-wrappers/phet-io-wrappers-tests.html?sim=' + repo + ( useAssert ? '&phetioDebug' : '' )
    } );
  } );
} );

// repo-specific Unit tests (unbuilt mode) from `grunt generate-test-harness`
unitTestRepos.forEach( repo => {
  // Skip phet-io-wrappers unit tests here, we run it with multiple repos above
  if ( repo === 'phet-io-wrappers' ) {
    return;
  }
  // All tests should work with no query parameters, with assertions enables and also in phet-io brand
  [ '', '?ea', '?brand=phet-io', '?ea&brand=phet-io' ].forEach( queryString => {
    if ( repo === 'phet-io' && !queryString.includes( 'phet-io' ) ) {
      return;
    }
    tests.push( {
      test: [ repo, 'top-level-unit-tests', 'unbuilt' + queryString ],
      type: 'qunit-test',
      url: repo + '/' + repo + '-tests.html' + queryString
    } );
  } );
} );

// Page-load tests (non-built)
[
  {
    repo: 'dot',
    urls: [
      '', // the root URL
      'tests/',
      'tests/playground.html'
    ]
  },
  {
    repo: 'kite',
    urls: [
      '', // the root URL
      'tests/playground.html',
      'tests/visual-shape-test.html'
    ]
  },
  {
    repo: 'scenery',
    urls: [
      '', // the root URL
      'tests/',
      'tests/playground.html',
      'tests/renderer-comparison.html?renderers=canvas,svg,dom',
      'tests/text-quality-test.html'
    ]
  }
].forEach( ( { repo, urls } ) => {
  urls.forEach( pageloadRelativeURL => {
    tests.push( {
      test: [ repo, 'pageload', '/' + pageloadRelativeURL ],
      type: 'pageload-test',
      url: repo + '/' + pageloadRelativeURL,
      priority: 4 // Fast to test, so test them more
    } );
  } );
} );

// Page-load tests (built)
[
  {
    repo: 'dot',
    urls: [
      'doc/',
      'examples/',
      'examples/convex-hull-2.html'
    ]
  },
  {
    repo: 'kite',
    urls: []
  },
  {
    repo: 'scenery',
    urls: [
      'doc/',
      'doc/a-tour-of-scenery.html',
      'doc/accessibility.html',
      'doc/implementation-notes.html',
      'doc/user-input.html',
      'examples/',
      'examples/cursors.html',
      'examples/hello-world.html',
      'examples/input-multiple-displays.html',
      'examples/input.html',
      'examples/mouse-wheel.html',
      'examples/multi-touch.html',
      'examples/nodes.html',
      'examples/shapes.html',
      'examples/sprites.html',
      // 'examples/webglnode.html', // currently disabled, since it fails without webgl
      'tests/text-bounds-comparison.html'
    ]
  }
].forEach( ( { repo, urls } ) => {
  urls.forEach( pageloadRelativeURL => {
    tests.push( {
      test: [ repo, 'pageload', '/' + pageloadRelativeURL ],
      type: 'pageload-test',
      url: repo + '/' + pageloadRelativeURL,
      priority: 5, // When these are built, it should be really quick to test

      brand: 'phet',
      buildDependencies: [ repo ],
      es5: true
    } );
  } );
} );

//----------------------------------------------------------------------------------------------------------------------
// Sim-specific query parameter tests
//----------------------------------------------------------------------------------------------------------------------

// beers-law-lab
tests.push( {
  test: [ 'beers-law-lab', 'fuzz', 'unbuilt', 'query-parameters' ],
  type: 'sim-test',
  url: 'beers-law-lab/beers-law-lab_en.html',
  queryParameters: 'brand=phet&ea&fuzz&memoryLimit=1000&showSoluteAmount&concentrationMeterUnits=percent&beakerUnits=milliliters'
} );

// gas-properties
tests.push( {
  test: [ 'gas-properties', 'fuzz', 'unbuilt', 'query-parameters' ],
  type: 'sim-test',
  url: 'gas-properties/gas-properties_en.html',
  queryParameters: 'brand=phet&ea&fuzz&memoryLimit=1000&pressureNoise=false'
} );

// natural-selection
tests.push( {
  test: [ 'natural-selection', 'fuzz', 'unbuilt', 'secondsPerGeneration' ],
  type: 'sim-test',
  url: 'natural-selection/natural-selection_en.html',
  queryParameters: 'brand=phet&ea&fuzz&memoryLimit=1000&secondsPerGeneration=1'
} );

// ph-scale
tests.push( {
  test: [ 'ph-scale', 'autofill-fuzz', 'unbuilt', 'query-parameters' ],
  type: 'sim-test',
  url: 'ph-scale/ph-scale_en.html',
  queryParameters: 'brand=phet&ea&fuzz&memoryLimit=1000&autoFill=false'
} );

console.log( JSON.stringify( tests, null, 2 ) );
<commit_msg>add query-parameter test for natural-selection<commit_after>// Copyright 2020, University of Colorado Boulder

/**
 * This prints out (in JSON form) the tests and operations requested for continuous testing for whatever is in master
 * at this point.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

'use strict';

const getActiveRepos = require( './common/getActiveRepos' );
const getRepoList = require( './common/getRepoList' );
const fs = require( 'fs' );

const repos = getActiveRepos();
const phetioRepos = getRepoList( 'testable-phet-io' );
const phetioReposValidated = getRepoList( 'testable-phet-io-validated' );
const runnableRepos = getRepoList( 'testable-runnables' );
const interactiveDescriptionRepos = getRepoList( 'interactive-descriptions' );
const phetioNoState = getRepoList( 'phet-io-state-unsupported' );
const unitTestRepos = getRepoList( 'unit-tests' );

/**
 * {Array.<string>} test
 * {string} type
 * {string} [url]
 * {string} [repo]
 * {string} [queryParameters]
 * {string} [testQueryParameters]
 * {boolean} [es5]
 * {string} [brand]
 * {number} [priority]
 * {Array.<string>} buildDependencies
 */
const tests = [];

tests.push( {
  test: [ 'perennial', 'lint-everything' ],
  type: 'lint-everything',
  priority: 100
} );

// phet brand builds
[
  ...runnableRepos,
  'scenery',
  'kite',
  'dot'
].forEach( repo => {
  tests.push( {
    test: [ repo, 'build' ],
    type: 'build',
    brands: phetioRepos.includes( repo ) ? [ 'phet', 'phet-io' ] : [ 'phet' ],
    repo: repo,
    priority: 1
  } );
} );

// lints
repos.forEach( repo => {
  if ( fs.existsSync( `../${repo}/Gruntfile.js` ) ) {
    tests.push( {
      test: [ repo, 'lint' ],
      type: 'lint',
      repo: repo,
      priority: 8
    } );
  }
} );

runnableRepos.forEach( repo => {
  tests.push( {
    test: [ repo, 'fuzz', 'unbuilt' ],
    type: 'sim-test',
    url: `${repo}/${repo}_en.html`,
    queryParameters: 'brand=phet&ea&fuzz&memoryLimit=1000'
  } );

  tests.push( {
    test: [ repo, 'xss-fuzz' ],
    type: 'sim-test',
    url: `${repo}/${repo}_en.html`,
    queryParameters: 'brand=phet&ea&fuzz&stringTest=xss&memoryLimit=1000',
    testQueryParameters: 'duration=40000',
    priority: 0.3
  } );

  tests.push( {
    test: [ repo, 'fuzz', 'built' ],
    type: 'sim-test',
    url: `${repo}/build/phet/${repo}_en_phet.html`,
    queryParameters: 'fuzz&memoryLimit=1000',
    testQueryParameters: 'duration=80000',

    // We want to elevate the priority so that we get a more even balance (we can't test these until they are built,
    // which doesn't happen always)
    priority: 2,

    brand: 'phet',
    buildDependencies: [ repo ],
    es5: true
  } );

  if ( phetioRepos.includes( repo ) ) {
    tests.push( {
      test: [ repo, 'fuzz', 'built-phet-io' ],
      type: 'sim-test',
      url: `${repo}/build/phet-io/${repo}_all_phet-io.html`,
      queryParameters: 'fuzz&memoryLimit=1000&phetioStandalone',
      testQueryParameters: 'duration=80000',

      brand: 'phet-io',
      buildDependencies: [ repo ],
      es5: true
    } );
  }
} );

phetioRepos.forEach( repo => {
  const validated = phetioReposValidated.includes( repo );
  const validatedParam = validated ? '&phetioValidateTandems' : '';

  tests.push( {
    test: [ repo, 'phet-io-fuzz', 'unbuilt' ],
    type: 'sim-test',
    url: `${repo}/${repo}_en.html`,
    queryParameters: 'brand=phet-io&phetioStandalone&ea' + validatedParam + '&fuzz&memoryLimit=1000'
  } );

  // fuzz test important wrappers
  tests.push( {
    test: [ repo, 'phet-io-studio-fuzz', 'unbuilt' ],
    type: 'wrapper-test',
    url: `studio/?sim=${repo}&phetioDebug&fuzz`,
    testQueryParameters: 'duration=60000'
  } );

  // only test state on phet-io sims that support it
  phetioNoState.indexOf( repo ) === -1 && tests.push( {
    test: [ repo, 'phet-io-state-fuzz', 'unbuilt' ],
    type: 'wrapper-test',
    url: `phet-io-wrappers/state/?sim=${repo}&phetioDebug&fuzz`,
    testQueryParameters: 'duration=60000'
  } );

  tests.push( {
    test: [ repo, 'phet-io-mirror-inputs-fuzz', 'unbuilt' ],
    type: 'wrapper-test',
    url: `phet-io-wrappers/mirror-inputs/?sim=${repo}&phetioDebug&fuzz`,
    testQueryParameters: 'duration=60000'
  } );
} );

// accessible tests
interactiveDescriptionRepos.forEach( repo => {
  tests.push( {
    test: [ repo, 'interactive-description-fuzz', 'unbuilt' ],
    type: 'sim-test',
    url: `${repo}/${repo}_en.html`,
    queryParameters: 'brand=phet&ea&fuzz&supportsDescriptions&memoryLimit=1000',
    testQueryParameters: 'duration=40000'
  } );

  tests.push( {
    test: [ repo, 'interactive-description-fuzzBoard', 'unbuilt' ],
    type: 'sim-test',
    url: `${repo}/${repo}_en.html`,
    queryParameters: 'brand=phet&ea&fuzzBoard&supportsDescriptions&memoryLimit=1000',
    testQueryParameters: 'duration=40000'
  } );

  tests.push( {
    test: [ repo, 'interactive-description-fuzz', 'built' ],
    type: 'sim-test',
    url: `${repo}/build/phet/${repo}_en_phet.html`,
    queryParameters: 'fuzz&supportsDescriptions&memoryLimit=1000',
    testQueryParameters: 'duration=40000',

    brand: 'phet',
    buildDependencies: [ repo ],
    es5: true
  } );

  tests.push( {
    test: [ repo, 'interactive-description-fuzzBoard', 'built' ],
    type: 'sim-test',
    url: `${repo}/build/phet/${repo}_en_phet.html`,
    queryParameters: 'fuzzBoard&supportsDescriptions&memoryLimit=1000',
    testQueryParameters: 'duration=40000',

    brand: 'phet',
    buildDependencies: [ repo ],
    es5: true
  } );
} );

// phet-io wrappers tests for each PhET-iO Sim
phetioRepos.forEach( repo => {
  [ false, true ].forEach( useAssert => {
    tests.push( {
      test: [ repo, 'phet-io-wrappers-tests', useAssert ? 'assert' : 'no-assert' ],
      type: 'qunit-test',
      url: 'phet-io-wrappers/phet-io-wrappers-tests.html?sim=' + repo + ( useAssert ? '&phetioDebug' : '' )
    } );
  } );
} );

// repo-specific Unit tests (unbuilt mode) from `grunt generate-test-harness`
unitTestRepos.forEach( repo => {
  // Skip phet-io-wrappers unit tests here, we run it with multiple repos above
  if ( repo === 'phet-io-wrappers' ) {
    return;
  }
  // All tests should work with no query parameters, with assertions enables and also in phet-io brand
  [ '', '?ea', '?brand=phet-io', '?ea&brand=phet-io' ].forEach( queryString => {
    if ( repo === 'phet-io' && !queryString.includes( 'phet-io' ) ) {
      return;
    }
    tests.push( {
      test: [ repo, 'top-level-unit-tests', 'unbuilt' + queryString ],
      type: 'qunit-test',
      url: repo + '/' + repo + '-tests.html' + queryString
    } );
  } );
} );

// Page-load tests (non-built)
[
  {
    repo: 'dot',
    urls: [
      '', // the root URL
      'tests/',
      'tests/playground.html'
    ]
  },
  {
    repo: 'kite',
    urls: [
      '', // the root URL
      'tests/playground.html',
      'tests/visual-shape-test.html'
    ]
  },
  {
    repo: 'scenery',
    urls: [
      '', // the root URL
      'tests/',
      'tests/playground.html',
      'tests/renderer-comparison.html?renderers=canvas,svg,dom',
      'tests/text-quality-test.html'
    ]
  }
].forEach( ( { repo, urls } ) => {
  urls.forEach( pageloadRelativeURL => {
    tests.push( {
      test: [ repo, 'pageload', '/' + pageloadRelativeURL ],
      type: 'pageload-test',
      url: repo + '/' + pageloadRelativeURL,
      priority: 4 // Fast to test, so test them more
    } );
  } );
} );

// Page-load tests (built)
[
  {
    repo: 'dot',
    urls: [
      'doc/',
      'examples/',
      'examples/convex-hull-2.html'
    ]
  },
  {
    repo: 'kite',
    urls: []
  },
  {
    repo: 'scenery',
    urls: [
      'doc/',
      'doc/a-tour-of-scenery.html',
      'doc/accessibility.html',
      'doc/implementation-notes.html',
      'doc/user-input.html',
      'examples/',
      'examples/cursors.html',
      'examples/hello-world.html',
      'examples/input-multiple-displays.html',
      'examples/input.html',
      'examples/mouse-wheel.html',
      'examples/multi-touch.html',
      'examples/nodes.html',
      'examples/shapes.html',
      'examples/sprites.html',
      // 'examples/webglnode.html', // currently disabled, since it fails without webgl
      'tests/text-bounds-comparison.html'
    ]
  }
].forEach( ( { repo, urls } ) => {
  urls.forEach( pageloadRelativeURL => {
    tests.push( {
      test: [ repo, 'pageload', '/' + pageloadRelativeURL ],
      type: 'pageload-test',
      url: repo + '/' + pageloadRelativeURL,
      priority: 5, // When these are built, it should be really quick to test

      brand: 'phet',
      buildDependencies: [ repo ],
      es5: true
    } );
  } );
} );

//----------------------------------------------------------------------------------------------------------------------
// Sim-specific query parameter tests
//----------------------------------------------------------------------------------------------------------------------

// beers-law-lab
tests.push( {
  test: [ 'beers-law-lab', 'fuzz', 'unbuilt', 'query-parameters' ],
  type: 'sim-test',
  url: 'beers-law-lab/beers-law-lab_en.html',
  queryParameters: 'brand=phet&ea&fuzz&memoryLimit=1000&showSoluteAmount&concentrationMeterUnits=percent&beakerUnits=milliliters'
} );

// gas-properties
tests.push( {
  test: [ 'gas-properties', 'fuzz', 'unbuilt', 'query-parameters' ],
  type: 'sim-test',
  url: 'gas-properties/gas-properties_en.html',
  queryParameters: 'brand=phet&ea&fuzz&memoryLimit=1000&pressureNoise=false'
} );

// natural-selection

tests.push( {
  test: [ 'natural-selection', 'fuzz', 'unbuilt', 'query-parameters' ],
  type: 'sim-test',
  url: 'natural-selection/natural-selection_en.html',
  queryParameters: 'brand=phet&ea&fuzz&memoryLimit=1000&allelesVisible=false&introMutations=F&introPopulation=10Ff&labMutations=FeT&labPopulation=2FFeett,2ffEEtt,2ffeeTT'
} );

// To have one test where the generation clock is running faster, and more things are liable to happen
tests.push( {
  test: [ 'natural-selection', 'fuzz', 'unbuilt', 'secondsPerGeneration' ],
  type: 'sim-test',
  url: 'natural-selection/natural-selection_en.html',
  queryParameters: 'brand=phet&ea&fuzz&memoryLimit=1000&secondsPerGeneration=1'
} );

// ph-scale
tests.push( {
  test: [ 'ph-scale', 'autofill-fuzz', 'unbuilt', 'query-parameters' ],
  type: 'sim-test',
  url: 'ph-scale/ph-scale_en.html',
  queryParameters: 'brand=phet&ea&fuzz&memoryLimit=1000&autoFill=false'
} );

console.log( JSON.stringify( tests, null, 2 ) );
<|endoftext|>