<commit_before>$(document).ready(function() {

   $('.reloadDiv').click(function()
   {
      var link_name = $(this).attr('id');
      $("#mainSection").load(link_name+".php");
    });

   // when the textboxes change value run this function
	$(".addToBasket").change(function()
	{

  	// keep track of number of item
  	var itemCount = itemCounter();

  	// store the values we need for later purposes
  	var nameOfItem = $(this).attr("name");

  	// get variables for updating the stock tags
  	var stockTag = getStock(nameOfItem);
  	var quantity = $(this).val();
  	var stock = $(this).attr('max') - quantity;

  	if(itemCount > 0)
  	{
  		$("#noItems").hide();
  		$("#storeBasket").show();
		$("#btnCheckout").show();

  		// replace the -'s with spaces throughout the name
  		nameOfItem = nameOfItem.replace(/-/g, ' ');

  		var itemID = $(this).attr('id');
		var category = itemID.substr(0, itemID.indexOf("-"));

			var money = itemID.substr(itemID.indexOf("-") + 1);
  		var total =  (parseFloat(money) * parseFloat(quantity)).toFixed(2);
  		var found = false;

  		// if the item we just changed was set to zero
  		if(quantity === "0")
  		{

  			// update the stock pile
 				updateStockTags(stock, stockTag);

  			// cycle through all the item in the shopping cart to find the correct item to remove
  			$('#storeBasket .removable').each(function()
				{
					if($(this).children(":first-child").html() === nameOfItem)
					{
						$(this).remove();
					}
				});
  		}
			else //otherwise we want to add the item to the shopping cart
			{
				// check to see wheather the item is already in the shopping cart
				// if so then increment quantity and total otherwise add whole row
				$('#storeBasket .itemname').each(function()
				{
				 	if(($(this).html()) === nameOfItem)
				 	{
				 		$(this).next().next().html(quantity);
				 		$(this).next().next().next().html("$" + total);
				 		found = true;
				 	}
				});
				if(found != true)
				{
					var htmltoadd = "
" + nameOfItem + "	" + category + "	$" + money +
					 "	" + quantity + "	$" + total + "
";

					$("#storeBasket tbody").prepend(htmltoadd);
				}
			}

  		// update the total
  		var subtotal = calctotal();
 			$("#totalPrice").html("$" + subtotal);

 			// update the stock pile
 			updateStockTags(stock, stockTag);

 		}
		else // no items in cart
  	{
  		// update the stock pile
 			updateStockTags(stock, stockTag);
			$("#btnCheckout").hide();

			$("#noItems").show();

			$('#storeBasket .removable').each(function()
			{
				$(this).remove();
			});

  		$("#storeBasket").hide();
  	}

	});

	$('#purchase').click(function(){

				$.ajax({
					url: '../assets/scripts/getQuantities.php',
	        data: $(this).serialize(),
	        type: 'post',
	        dataType: 'json',
	        success: function(data) {
	            if(data.success)
	            {
	            	parseTable(data['tables']);
	            }
	        },
	        error: function(){
	        	console.log("Error! Could not connect to database.");
	        }
			});
	});

});

// functoin for parsing bedding table that's returned from ajax call to php
function parseTable(allTables)
{

	// get each key=>value pair and print to appropriate tag
	$.each(allTables, function(key, value){

    // prep key for use later
    key = key.replace(/\s/g,'-');

    // new variable for more manipulating
    var manipulatedKey = key;
    manipulatedKey = manipulatedKey.toLowerCase();
    manipulatedKey = manipulatedKey + "-stock";

    if(value == 0)
    {
    	$('#'+manipulatedKey).attr('class', 'outOfStock');
			$('#'+manipulatedKey).html('Out of Stock!');

			// search through all items with class 'addToBasket' and find the input box thathas matching name and hide
			disableQuantityBox(key);
    }
    else
    {
    	setMaxForQuantityBox(key, value);
    	$('#'+manipulatedKey).html(value + ' in stock');
    }

	});

}

// function for updating Stock tags
function updateStockTags(stock, stockTag)
{
	// update the stock pile
 			if(stock == 0)
 			{
				stockTag.attr('class', 'outOfStock');
				stockTag.html('Out of Stock!');
 			}
 			else
 			{
 				stockTag.html(stock + " in stock");
 			}
}

// function for cycling through stock labels and getting the result
function getStock(nameToFind)
{
	// append -stock for easier searching
	nameToFind = nameToFind.toLowerCase() + '-stock';
	nameToFind = nameToFind.replace('/', '-');
	var result;

	$('.updateStockPile').each(function()
	{
	  if($(this).attr('id') == nameToFind)
	  {

	  	result = $(this);
	  	// break out of loop to return the correct element
	  	return false;
	  }
	});

	return result;
};

// function for cycling through quantitiy boxes to hide the appropriate one
function disableQuantityBox(nameToFind)
{
	$('.addToBasket').each(function()
	{
	  if($(this).attr('name') == nameToFind)
	  {
	  	$(this).hide();
	  }
	});

};

// function for cycling through quantitiy boxes to set max value
function setMaxForQuantityBox(nameToFind, value)
{
	$('.addToBasket').each(function()
	{
	  if($(this).attr('name') == nameToFind)
	  {
	  	$(this).attr('max', value);
	  }
	});

};

// function for getting number of item in quantity boxes
function itemCounter()
{
	var count = 0;
	$('.addToBasket').each(function()
	{
	  count = count + parseInt($(this).val(), 10);
	});

	return count;
};

// function for getting the cumulative total of all item in cart
function calctotal()
{
	var total = 0;
	$('.itemincart').each(function()
	{
	  total = total + parseFloat($(this).html().substring(1));
	});

	total = total.toFixed(2);
	return total;
};

function goToStore()
{
	$("#mainSection").load("purchase.php");
};

function CheckoutForm()
{
	//check database
	$('#bigDiv').load("checkout.php");
	$('#btnCheckout').toggle();
}

function hideshipping()
{
	$('#Shipping').toggle();
}

function cancelCheckout()
{
	if (confirm("Are you sure you want to cancel your order?"))
	{
		$("#mainSection").load("purchase.php");

		setTimeout(function(){
		$.ajax({
					url: '../assets/scripts/getQuantities.php',
	        data: $(this).serialize(),
	        type: 'post',
	        dataType: 'json',
	        success: function(data) {
	            if(data.success)
	            {
	            	parseTable(data['tables']);
	            }
	        },
	        error: function(){
	        	console.log("Error! Could not connect to database.");
	        }
			});
		}, 25);

	}
	else
	{

	}
}

function validateCheckout()
{
	var b_fname = $("#bill_fname").val();
	var b_lname = $("#bill_lname").val();
	var b_street = $("#bill_street").val();
	var b_phone = $("#bill_phone").val();
	var b_state = $("#bill_state").val();
	var b_email = $("#bill_email").val();
	var b_zip = $("#bill_zip").val();
	var b_zip_num = isNaN(b_zip);
	var b_phone_num = isNaN(b_phone);

	// Check to make sure nothing is left blank
	if (b_fname == "" || b_lname == "" || b_street == "" || b_phone == "" || b_state == "" || b_email == "" || b_zip == "")
	{
		var message = "Error! All fields in the billing section must be filled out.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}
	else if(b_zip_num == true)
	{
		var message = "Error! Bad zip code in the billing section.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}
	else if(b_phone_num == true)
	{
		var message = "Error! Bad phone number in your billing section.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}

	// See  if billing and shipping are the same information, if not validate
	if (!$("#ship_and_bill").prop('checked')) {
		var s_fname = $("#ship_fname").val();
		var s_lname = $("#ship_lname").val();
		var s_street = $("#ship_street").val();
		var s_phone = $("#ship_phone").val();
		var s_state = $("#ship_state").val();
		var s_email = $("#ship_email").val();
		var s_zip = $("#ship_zip").val();
		var phone_num = isNaN(s_phone);
		var zip_num = isNaN(s_zip);

		if (s_fname == "" || s_lname == "" || s_street == "" || s_phone == "" || s_state == "" || s_email == "" || s_zip == "")
		{
			var message = "Error! All fields in the shipping section must be filled out.";
			$('#alert_placeholder').html('
' + message + '
');
			return;
		}
		else if(phone_num == true)
		{
			var message = "Error! Bad phone number in your shipping information.";
			$('#alert_placeholder').html('
' + message + '
');
			return;
		}
		else if(zip_num == true)
		{
			var message = "Error! Bad zip code in the shipping section.";
			$('#alert_placeholder').html('
' + message + '
');
			return;
		}
	}

	// Check Payment option
	var c_name = $("#card_name").val();
	var c_num = $("#card_num").val();
	var c_sec = $("#sec_num").val();
	var c_exp = $("#exp").val();
	var exp_num = isNaN(c_exp);
	var sec_num = isNaN(c_sec);
	var cnum_num = isNaN(c_num);

	if(c_name == "" || c_num == "" || c_sec == "" || c_exp == "")
	{
		var message = "Error! All payment fields must be filled out.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}
	else if(sec_num == true)
	{
		var message = "Error! Bad credit card security number.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}
	else if(cnum_num == true)
	{
		var message = "Error! Bad credit card number.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}

	//Output thank you and reload store
	var message = "Thank you for placing your order!";
	//$('#alert_placeholder').html('
' + message + '
');
	//var table = "
" + $("#storeBasket").html() + "
";
	var category, item_name, itemCount;

	var myRows = [];
	var headersText = [];
	var $headers = $("th");

	// Loop through grabbing everything
	var $rows = $("#basketBody tr");
	var totalRows = $rows.length;
	for (var rowIndex = 0; rowIndex < totalRows; ++rowIndex) {
		var currentRow = $($rows[rowIndex]);
		var $cells = currentRow.find("td");
		myRows[rowIndex] = {};
		var totalCells = $cells.length;

		for (var cellIndex = 0; cellIndex < totalCells; ++cellIndex) {
			var currentCell = $($cells[cellIndex]);
			// Set the header text
			if(headersText[cellIndex] === undefined) {
				headersText[cellIndex] = $($headers[cellIndex]).text();
			}
			// Update the row object with the header/cell combo
			myRows[rowIndex][headersText[cellIndex]] = currentCell.text();
			if (headersText[cellIndex] == "Item")
			{
				item_name = currentCell.text();
			}
			else if (headersText[cellIndex] == "Category")
			{
				category = currentCell.text();

				if (category.substr(0,4) == "toys")
				{
				category = "toys";
				}
				else if (category.substring(0,9) == "nutrition")
				{
				category = "nutrition";
				}
				else if (category.substr(0,3) == "egg")
				{
				category = "egg-kits";
				}
			}
			else if (headersText[cellIndex] == "Quantity")
			{
				itemCount = currentCell.text();
			}
			else if (currentCell.text() == "Total:")
			{

			}

		}

		$.ajax({
			url: '../assets/scripts/checkoutUpdate.php',
			data: "category="+category+"&item_name="+item_name+"&quantity="+itemCount,
			type: 'POST',
			success: function(data) {
			if(data.success)
			{

			}
			},
			error: function(){
				console.log("Error! Could not connect to database.");
			}
		});
	}

	// Let's put this in the object like you want and convert to JSON (Note: jQuery will also do this for you on the Ajax request)
	var myObj = {
	"myrows": myRows
	};

	alert("Thank you for your order!");
	$("#mainSection").load("home_text.php");
}<commit_msg>yaaaaaaay<commit_after>$(document).ready(function() {

   $('.reloadDiv').click(function()
   {
      var link_name = $(this).attr('id');
      $("#mainSection").load(link_name+".php");
    });

   // when the textboxes change value run this function
	$(".addToBasket").change(function()
	{

  	// keep track of number of item
  	var itemCount = itemCounter();

  	// store the values we need for later purposes
  	var nameOfItem = $(this).attr("name");

  	// get variables for updating the stock tags
  	var stockTag = getStock(nameOfItem);
  	var quantity = $(this).val();
  	var stock = $(this).attr('max') - quantity;

  	if(itemCount > 0)
  	{
  		$("#noItems").hide();
  		$("#storeBasket").show();
		$("#btnCheckout").show();

  		// replace the -'s with spaces throughout the name
  		nameOfItem = nameOfItem.replace(/-/g, ' ');

  		var itemID = $(this).attr('id');
		var category = itemID.substr(0, itemID.indexOf("-"));

			var money = itemID.substr(itemID.indexOf("-") + 1);
  		var total =  (parseFloat(money) * parseFloat(quantity)).toFixed(2);
  		var found = false;

  		// if the item we just changed was set to zero
  		if(quantity === "0")
  		{

  			// update the stock pile
 				updateStockTags(stock, stockTag);

  			// cycle through all the item in the shopping cart to find the correct item to remove
  			$('#storeBasket .removable').each(function()
				{
					if($(this).children(":first-child").html() === nameOfItem)
					{
						$(this).remove();
					}
				});
  		}
			else //otherwise we want to add the item to the shopping cart
			{
				// check to see wheather the item is already in the shopping cart
				// if so then increment quantity and total otherwise add whole row
				$('#storeBasket .itemname').each(function()
				{
				 	if(($(this).html()) === nameOfItem)
				 	{
				 		$(this).next().next().html(quantity);
				 		$(this).next().next().next().html("$" + total);
				 		found = true;
				 	}
				});
				if(found != true)
				{
					var htmltoadd = "
" + nameOfItem + "	" + category + "	$" + money +
					 "	" + quantity + "	$" + total + "
";

					$("#storeBasket tbody").prepend(htmltoadd);
				}
			}

  		// update the total
  		var subtotal = calctotal();
 			$("#totalPrice").html("$" + subtotal);

 			// update the stock pile
 			updateStockTags(stock, stockTag);

 		}
		else // no items in cart
  	{
  		// update the stock pile
 			updateStockTags(stock, stockTag);
			$("#btnCheckout").hide();

			$("#noItems").show();

			$('#storeBasket .removable').each(function()
			{
				$(this).remove();
			});

  		$("#storeBasket").hide();
  	}

	});

	$('#purchase').click(function(){

				$.ajax({
					url: '../assets/scripts/getQuantities.php',
	        data: $(this).serialize(),
	        type: 'post',
	        dataType: 'json',
	        success: function(data) {
	            if(data.success)
	            {
	            	parseTable(data['tables']);
	            }
	        },
	        error: function(){
	        	console.log("Error! Could not connect to database.");
	        }
			});
	});

});

// functoin for parsing bedding table that's returned from ajax call to php
function parseTable(allTables)
{

	// get each key=>value pair and print to appropriate tag
	$.each(allTables, function(key, value){

    // prep key for use later
    key = key.replace(/\s/g,'-');

    // new variable for more manipulating
    var manipulatedKey = key;
    manipulatedKey = manipulatedKey.toLowerCase();
    manipulatedKey = manipulatedKey + "-stock";

    if(value == 0)
    {
    	$('#'+manipulatedKey).attr('class', 'outOfStock');
			$('#'+manipulatedKey).html('Out of Stock!');

			// search through all items with class 'addToBasket' and find the input box thathas matching name and hide
			disableQuantityBox(key);
    }
    else
    {
    	setMaxForQuantityBox(key, value);
    	$('#'+manipulatedKey).html(value + ' in stock');
    }

	});

}

// function for updating Stock tags
function updateStockTags(stock, stockTag)
{
	// update the stock pile
 			if(stock == 0)
 			{
				stockTag.attr('class', 'outOfStock');
				stockTag.html('Out of Stock!');
 			}
 			else
 			{
 				stockTag.html(stock + " in stock");
 			}
}

// function for cycling through stock labels and getting the result
function getStock(nameToFind)
{
	// append -stock for easier searching
	nameToFind = nameToFind.toLowerCase() + '-stock';
	nameToFind = nameToFind.replace('/', '-');
	var result;

	$('.updateStockPile').each(function()
	{
	  if($(this).attr('id') == nameToFind)
	  {

	  	result = $(this);
	  	// break out of loop to return the correct element
	  	return false;
	  }
	});

	return result;
};

// function for cycling through quantitiy boxes to hide the appropriate one
function disableQuantityBox(nameToFind)
{
	$('.addToBasket').each(function()
	{
	  if($(this).attr('name') == nameToFind)
	  {
	  	$(this).hide();
	  }
	});

};

// function for cycling through quantitiy boxes to set max value
function setMaxForQuantityBox(nameToFind, value)
{
	$('.addToBasket').each(function()
	{
	  if($(this).attr('name') == nameToFind)
	  {
	  	$(this).attr('max', value);
	  }
	});

};

// function for getting number of item in quantity boxes
function itemCounter()
{
	var count = 0;
	$('.addToBasket').each(function()
	{
	  count = count + parseInt($(this).val(), 10);
	});

	return count;
};

// function for getting the cumulative total of all item in cart
function calctotal()
{
	var total = 0;
	$('.itemincart').each(function()
	{
	  total = total + parseFloat($(this).html().substring(1));
	});

	total = total.toFixed(2);
	return total;
};

function goToStore()
{
	$("#mainSection").load("purchase.php");
};

function CheckoutForm()
{
	//check database
	$('#bigDiv').load("checkout.php");
	$('#btnCheckout').toggle();
}

function hideshipping()
{
	$('#Shipping').toggle();
}

function cancelCheckout()
{
	if (confirm("Are you sure you want to cancel your order? You will lose all your items."))
	{
		$("#mainSection").load("purchase.php");

		setTimeout(function(){
		$.ajax({
					url: '../assets/scripts/getQuantities.php',
	        data: $(this).serialize(),
	        type: 'post',
	        dataType: 'json',
	        success: function(data) {
	            if(data.success)
	            {
	            	parseTable(data['tables']);
	            }
	        },
	        error: function(){
	        	console.log("Error! Could not connect to database.");
	        }
			});
		}, 25);

	}
	else
	{

	}
}

function validateCheckout()
{
	var b_fname = $("#bill_fname").val();
	var b_lname = $("#bill_lname").val();
	var b_street = $("#bill_street").val();
	var b_phone = $("#bill_phone").val();
	var b_state = $("#bill_state").val();
	var b_email = $("#bill_email").val();
	var b_zip = $("#bill_zip").val();
	var b_zip_num = isNaN(b_zip);
	var b_phone_num = isNaN(b_phone);

	// Check to make sure nothing is left blank
	if (b_fname == "" || b_lname == "" || b_street == "" || b_phone == "" || b_state == "" || b_email == "" || b_zip == "")
	{
		var message = "Error! All fields in the billing section must be filled out.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}
	else if(b_zip_num == true)
	{
		var message = "Error! Bad zip code in the billing section.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}
	else if(b_phone_num == true)
	{
		var message = "Error! Bad phone number in your billing section.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}

	// See  if billing and shipping are the same information, if not validate
	if (!$("#ship_and_bill").prop('checked')) {
		var s_fname = $("#ship_fname").val();
		var s_lname = $("#ship_lname").val();
		var s_street = $("#ship_street").val();
		var s_phone = $("#ship_phone").val();
		var s_state = $("#ship_state").val();
		var s_email = $("#ship_email").val();
		var s_zip = $("#ship_zip").val();
		var phone_num = isNaN(s_phone);
		var zip_num = isNaN(s_zip);

		if (s_fname == "" || s_lname == "" || s_street == "" || s_phone == "" || s_state == "" || s_email == "" || s_zip == "")
		{
			var message = "Error! All fields in the shipping section must be filled out.";
			$('#alert_placeholder').html('
' + message + '
');
			return;
		}
		else if(phone_num == true)
		{
			var message = "Error! Bad phone number in your shipping information.";
			$('#alert_placeholder').html('
' + message + '
');
			return;
		}
		else if(zip_num == true)
		{
			var message = "Error! Bad zip code in the shipping section.";
			$('#alert_placeholder').html('
' + message + '
');
			return;
		}
	}

	// Check Payment option
	var c_name = $("#card_name").val();
	var c_num = $("#card_num").val();
	var c_sec = $("#sec_num").val();
	var c_exp = $("#exp").val();
	var exp_num = isNaN(c_exp);
	var sec_num = isNaN(c_sec);
	var cnum_num = isNaN(c_num);

	if(c_name == "" || c_num == "" || c_sec == "" || c_exp == "")
	{
		var message = "Error! All payment fields must be filled out.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}
	else if(sec_num == true)
	{
		var message = "Error! Bad credit card security number.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}
	else if(cnum_num == true)
	{
		var message = "Error! Bad credit card number.";
		$('#alert_placeholder').html('
' + message + '
');
		return;
	}

	//Output thank you and reload store
	var message = "Thank you for placing your order!";
	//$('#alert_placeholder').html('
' + message + '
');
	//var table = "
" + $("#storeBasket").html() + "
";
	var category, item_name, itemCount;

	var myRows = [];
	var headersText = [];
	var $headers = $("th");

	// Loop through grabbing everything
	var $rows = $("#basketBody tr");
	var totalRows = $rows.length;
	for (var rowIndex = 0; rowIndex < totalRows; ++rowIndex) {
		var currentRow = $($rows[rowIndex]);
		var $cells = currentRow.find("td");
		myRows[rowIndex] = {};
		var totalCells = $cells.length;

		for (var cellIndex = 0; cellIndex < totalCells; ++cellIndex) {
			var currentCell = $($cells[cellIndex]);
			// Set the header text
			if(headersText[cellIndex] === undefined) {
				headersText[cellIndex] = $($headers[cellIndex]).text();
			}
			// Update the row object with the header/cell combo
			myRows[rowIndex][headersText[cellIndex]] = currentCell.text();
			if (headersText[cellIndex] == "Item")
			{
				item_name = currentCell.text();
			}
			else if (headersText[cellIndex] == "Category")
			{
				category = currentCell.text();
			}
			else if (headersText[cellIndex] == "Quantity")
			{
				itemCount = currentCell.text();
			}
			else if (currentCell.text() == "Total:")
			{

			}

		}

		$.ajax({
			url: '../assets/scripts/checkoutUpdate.php',
			data: "category="+category+"&item_name="+item_name+"&quantity="+itemCount,
			type: 'POST',
			success: function(data) {
			if(data.success)
			{

			}
			},
			error: function(){
				console.log("Error! Could not connect to database.");
			}
		});
	}

	// Let's put this in the object like you want and convert to JSON (Note: jQuery will also do this for you on the Ajax request)
	var myObj = {
	"myrows": myRows
	};

	alert("Thank you for your order!");
	$("#mainSection").load("home_text.php");
}<|endoftext|>


<commit_before>// I would like to lazy load json2, but I'm worried we're trying to access
// JSON very early in the code
//= require <json2>
if (!window.sessionStorage) {
  window.sessionStorage = (function () {
    var data = window.top.name ? JSON.parse(window.top.name) : {};

    return {
      clear: function () {
        data = {};
        window.top.name = '';
      },
      getItem: function (key) {
        return data[key] || null;
      },
      removeItem: function (key) {
        delete data[key];
        window.top.name = JSON.stringify(data);
      },
      setItem: function (key, value) {
        data[key] = value;
        window.top.name = JSON.stringify(data);
      }
    };
  })();
}

if (!window.localStorage) {
  // dirty, but will do for our purposes
  window.localStorage = window.sessionStorage;
}<commit_msg>Rewrite of the storage logic to get around Firefox requiring cookies enabled to even access the global object. Fixes issue #5 f:30<commit_after>// I would like to lazy load json2, but I'm worried we're trying to access
// JSON very early in the code
//= require <json2>

var requiresCookies = (function () {
  var ua = navigator.userAgent.toLowerCase(),
      id = null;
  if (/mozilla/.test(ua) && !/compatible/.test(ua)) {
    id = new Date().getTime();
    document.cookie = '__cprobe=' + id + ';path=/';
    if (document.cookie.indexOf(id,0) === -1) {
      return true;
    }
  }
  return false;
})();

// Firefox with Cookies disabled triggers a security error when we probe window.sessionStorage
// currently we're just disabling all the session features if that's the case.
var sessionStorage, localStorage;

if (!requiresCookies && window.sessionStorage) {
  sessionStorage = window.sessionStorage;
}

if (!sessionStorage) {
  sessionStorage = (function () {
    var data = window.top.name ? JSON.parse(window.top.name) : {};

    return {
      clear: function () {
        data = {};
        window.top.name = '';
      },
      getItem: function (key) {
        return data[key] || null;
      },
      removeItem: function (key) {
        delete data[key];
        window.top.name = JSON.stringify(data);
      },
      setItem: function (key, value) {
        data[key] = value;
        window.top.name = JSON.stringify(data);
      }
    };
  })();
}

if ((!requiresCookies && !window.localStorage) || requiresCookies) {
  // dirty, but will do for our purposes
  localStorage = sessionStorage;
} else if (!requiresCookies) {
  localStorage = window.localStorage;
}<|endoftext|>


<commit_before>const $ = window.jQuery = require("jquery");
const moment = require("moment");
const templatesMock = require("./__mocks__/templatesMock");
const ajaxMock = require("./__mocks__/ajaxMock");

jest.useFakeTimers();

test("silence form", () => {
    let body = templatesMock.loadTemplates();
    body.push(
        "
" +
        "
" +
        "
" +
        "

" +
        "
" +
        "
" +
        "
" +
        "" +
        ""
    );
    document.body.innerHTML = body;

    const templates = require("./templates");
    templates.init();

    const config = require("./config");
    config.init({
        CopySelector: "#copy-settings-with-filter",
        SaveSelector: "#save-default-filter",
        ResetSelector: "#reset-settings"
    });

    const silence = require("./silence");
    silence.setupSilenceForm();

    require("bootstrap/js/tooltip.js");
    require("bootstrap/js/modal.js");

    // rendering silence form requires AJAX call to pull data
    // first check failed request
    let ajaxServer = ajaxMock.createServer(500, {
        "status": "error",
        "errorType": "server_error",
        "error": "request failed"
    });
    ajaxServer.start();
    // click on the button, modal should show and render via ajax call
    $("#silenceButton").click();
    jest.runOnlyPendingTimers();
    let silenceModal = $("#silenceModal").html().trim();
    expect(silenceModal).toMatchSnapshot();
    ajaxServer.stop();

    // hide the form
    $("#silenceModal").modal("hide");

    // next try successful request
    ajaxServer = ajaxMock.createServer(200, {
        "groups": [ {
            "receiver": "default",
            "labels": {"alertname": "fakeAlert"},
            "alerts": [ {
                "annotations": {},
                "labels": {
                    "alertname": "fakeAlert",
                    "cluster": "prod",
                    "foo": "bar"
                },
                "startsAt": "2017-07-22T01:07:54.32189391Z",
                "endsAt": "0001-01-01T00:00:00Z",
                "state": "active",
                "alertmanager": [ {
                    "name": "mock",
                    "uri": "http://localhost",
                    "state": "active",
                    "startsAt": "2017-07-22T01:07:54.32189391Z",
                    "endsAt": "0001-01-01T00:00:00Z",
                    "source": "localhost/prometheus",
                    "silences": {}
                } ],
                "receiver": "default",
                "links": {}
            } ],
            "id": "12345",
            "hash": "abcdef",
            "stateCount": {"active": 1, "suppressed": 0, "unprocessed": 0}
        } ]
    });
    ajaxServer.start();
    // click on the button, modal should show and render via ajax call
    $("#silenceButton").click();
    jest.runOnlyPendingTimers();
    // default times are relative to current time, use fixed values
    let startsAt = moment("2050-01-01T01:00:00.000Z").utc();
    let endsAt = moment("2050-01-01T02:00:00.000Z").utc();
    $("#endsAt").data("DateTimePicker").date(endsAt);
    $("#startsAt").data("DateTimePicker").date(startsAt);
    // compare html to a snapshot
    silenceModal = $("#silenceModal").html().trim();
    expect(silenceModal).toMatchSnapshot();
    ajaxServer.stop();

    // submit silence
    ajaxServer = ajaxMock.createServer(200, {
        "status": "success",
        "data": {"silenceId": "abcdef"}
    });
    ajaxServer.start();
    $("#newSilenceForm").submit();
    ajaxServer.stop();
    silenceModal = $("#silenceModal").html().trim();
    expect(silenceModal).toMatchSnapshot();
});

test("successful sendSilencePOST()", () => {
    var body = templatesMock.loadTemplates();
    body.push(
        "" +
        ""
    );
    document.body.innerHTML = body;

    const templates = require("./templates");
    templates.init();

    const ajaxServer = ajaxMock.createServer(200, {
        "status": "success",
        "data": {"silenceId": "abcdef"}
    });
    ajaxServer.start();

    const silence = require("./silence");
    silence.sendSilencePOST("http://localhost", {});

    let resultElem = $(".silence-post-result").html().trim();
    expect(resultElem).toMatchSnapshot();

    ajaxServer.stop();
});

test("failed sendSilencePOST()", () => {
    var body = templatesMock.loadTemplates();
    body.push(
        "" +
        ""
    );
    document.body.innerHTML = body;

    const templates = require("./templates");
    templates.init();

    const ajaxServer = ajaxMock.createServer(500, {
        "status": "success",
        "errorType": "server_error",
        "error": "request failed"
    });
    ajaxServer.start();

    const silence = require("./silence");
    silence.sendSilencePOST("http://localhost", {});

    let resultElem = $(".silence-post-result").html().trim();
    expect(resultElem).toMatchSnapshot();

    ajaxServer.stop();
});
<commit_msg>Fix wrong status in error<commit_after>const $ = window.jQuery = require("jquery");
const moment = require("moment");
const templatesMock = require("./__mocks__/templatesMock");
const ajaxMock = require("./__mocks__/ajaxMock");

jest.useFakeTimers();

test("silence form", () => {
    let body = templatesMock.loadTemplates();
    body.push(
        "
" +
        "
" +
        "
" +
        "

" +
        "
" +
        "
" +
        "
" +
        "" +
        ""
    );
    document.body.innerHTML = body;

    const templates = require("./templates");
    templates.init();

    const config = require("./config");
    config.init({
        CopySelector: "#copy-settings-with-filter",
        SaveSelector: "#save-default-filter",
        ResetSelector: "#reset-settings"
    });

    const silence = require("./silence");
    silence.setupSilenceForm();

    require("bootstrap/js/tooltip.js");
    require("bootstrap/js/modal.js");

    // rendering silence form requires AJAX call to pull data
    // first check failed request
    let ajaxServer = ajaxMock.createServer(500, {
        "status": "error",
        "errorType": "server_error",
        "error": "request failed"
    });
    ajaxServer.start();
    // click on the button, modal should show and render via ajax call
    $("#silenceButton").click();
    jest.runOnlyPendingTimers();
    let silenceModal = $("#silenceModal").html().trim();
    expect(silenceModal).toMatchSnapshot();
    ajaxServer.stop();

    // hide the form
    $("#silenceModal").modal("hide");

    // next try successful request
    ajaxServer = ajaxMock.createServer(200, {
        "groups": [ {
            "receiver": "default",
            "labels": {"alertname": "fakeAlert"},
            "alerts": [ {
                "annotations": {},
                "labels": {
                    "alertname": "fakeAlert",
                    "cluster": "prod",
                    "foo": "bar"
                },
                "startsAt": "2017-07-22T01:07:54.32189391Z",
                "endsAt": "0001-01-01T00:00:00Z",
                "state": "active",
                "alertmanager": [ {
                    "name": "mock",
                    "uri": "http://localhost",
                    "state": "active",
                    "startsAt": "2017-07-22T01:07:54.32189391Z",
                    "endsAt": "0001-01-01T00:00:00Z",
                    "source": "localhost/prometheus",
                    "silences": {}
                } ],
                "receiver": "default",
                "links": {}
            } ],
            "id": "12345",
            "hash": "abcdef",
            "stateCount": {"active": 1, "suppressed": 0, "unprocessed": 0}
        } ]
    });
    ajaxServer.start();
    // click on the button, modal should show and render via ajax call
    $("#silenceButton").click();
    jest.runOnlyPendingTimers();
    // default times are relative to current time, use fixed values
    let startsAt = moment("2050-01-01T01:00:00.000Z").utc();
    let endsAt = moment("2050-01-01T02:00:00.000Z").utc();
    $("#endsAt").data("DateTimePicker").date(endsAt);
    $("#startsAt").data("DateTimePicker").date(startsAt);
    // compare html to a snapshot
    silenceModal = $("#silenceModal").html().trim();
    expect(silenceModal).toMatchSnapshot();
    ajaxServer.stop();

    // submit silence
    ajaxServer = ajaxMock.createServer(200, {
        "status": "success",
        "data": {"silenceId": "abcdef"}
    });
    ajaxServer.start();
    $("#newSilenceForm").submit();
    ajaxServer.stop();
    silenceModal = $("#silenceModal").html().trim();
    expect(silenceModal).toMatchSnapshot();
});

test("successful sendSilencePOST()", () => {
    var body = templatesMock.loadTemplates();
    body.push(
        "" +
        ""
    );
    document.body.innerHTML = body;

    const templates = require("./templates");
    templates.init();

    const ajaxServer = ajaxMock.createServer(200, {
        "status": "success",
        "data": {"silenceId": "abcdef"}
    });
    ajaxServer.start();

    const silence = require("./silence");
    silence.sendSilencePOST("http://localhost", {});

    let resultElem = $(".silence-post-result").html().trim();
    expect(resultElem).toMatchSnapshot();

    ajaxServer.stop();
});

test("failed sendSilencePOST()", () => {
    var body = templatesMock.loadTemplates();
    body.push(
        "" +
        ""
    );
    document.body.innerHTML = body;

    const templates = require("./templates");
    templates.init();

    const ajaxServer = ajaxMock.createServer(500, {
        "status": "error",
        "errorType": "server_error",
        "error": "request failed"
    });
    ajaxServer.start();

    const silence = require("./silence");
    silence.sendSilencePOST("http://localhost", {});

    let resultElem = $(".silence-post-result").html().trim();
    expect(resultElem).toMatchSnapshot();

    ajaxServer.stop();
});
<|endoftext|>


<commit_before>/*jshint nonew:true, jquery:true, curly:true, noarg:true, forin:true, noempty:true, eqeqeq:true, strict:true, undef:true, bitwise:true, newcap:true, immed:true, onevar:true, browser:true, es3:true, gcl:true */
/**
 * $('#main_container')                             // Container of the load more, search, and sort
 *      .cloneFiltering({
 *          container: '#container',                // (REQUIRED) Container of the template to be inserted
 *          template: '#template',                  // (REQUIRED) Template to be cloned
 *          url: 'localhost/ajax/get_something',    // (OPTIONAL) URL of the data needed for the cloning and filtering
 *          csrfToken: {                            // (OPTIONAL) For security reason
 *              value: 'x5925626lsd62',
 *              name: 'my_token'
 *          },
 *          data: oData,                            // (OPTIONAL) If from ajax success this is required
 *                                                                  if url is set do not use this
 *          limit: 10,                              // (OPTIONAL) Will be using in load more
 *          loadMore: 'button.load_more',           // (OPTIONAL) Selector of the load more button MUST BE A BUTTON
 *          search: {                               // (OPTIONAL) Selector of the search input MUST BE AN INPUT
 *              input: {
 *                  selector: 'input.search',
 *                  eventType: 'keypress'
 *              },
 *              btn: 'div.search_btn'
 *          },
 *          sort: {                                 // (OPTIONAL) Selector of the sort
 *              selector: 'ul.sort',                // if sort has been set this selector is required
 *              activeClass: 'active sorting'       // if not set the default active class would be the 'active sorting'
 *          }
 *      });
 *
 * @todo: No documentation yet.
 */
(function ($) {
    "use strict";

    /**
     * Error message container
     * @const
     */
    var ERROR_MSG = {
        ui          : 'DOM not found or do not exists!',
        objectFormat: 'Format of the object is invalid. Format should be:' +
                    '{ ' +
                        'data:' +
                            '{ 0:' +
                                '{ any: "value" }' +
                                '{ any: "value" }' +
                                '...' +
                            '},' +
                             '...' +
                        'total_rows: 10' +
                    '}'
    };

    /**
     * DOM main container of the cloning or filtering
     *
     * @param {Object} uiMainContainer jQuery object referring to the main container. This will attach event to this DOM
     */
    var uiMainContainer;

    /**
     * Configuration of the plugin
     *
     */
    var config = {};

    /**
     * Default value of the plugin
     *
     */
    var defaults = {
        container: '#container',
        template : '#template',
        limit    : 10
    };

    /**
     * Storage of the active class
     *
     * @type {string}
     */
    var sActiveClass = '';

    var init = function (options) {
        config = $.extend(defaults, options);

        // find for the container and the template
        var uiContainer = uiMainContainer.find(config.container),
            uiTemplate = uiMainContainer.find(config.template);

        // uiContainer && uiTemplate should be present so validate this
        if (!helper.isValidUI(uiContainer) && !helper.isValidUI(uiTemplate)) {
            return config.container + ' and ' + config.template + ' ' + ERROR_MSG.ui; // stop if the two is not found
        }

        // if config.data is found or set process cloning immediately
        if (helper.lengthOf(config.data)) {
            process._cloning(config.data);
        }

        // if url is defined then one of the following should also be defined:
        // config.sort, config.loadMore or config.search
        else if (config.url !== undefined) {

            // check first if config.search is an object and has at least one element in it
            if (helper.lengthOf(config.search)) {

                // check if config.search has 'input' property
                if (config.search.hasOwnProperty('input')) {

                    // check if config has 'loadMore'
                    if (config.hasOwnProperty('loadMore')) {
                        process._attachEvent(config.loadMore, 'click', function () {
                            process.loadMore();
                        });
                    }

                    // TODO: no structure validation yet in search, sort and etc.
                    // check if config.search.input has 'selector'
                    if (config.search.input.hasOwnProperty('selector')) {
                        var sEventType = (config.search.input.eventType !== undefined) ?
                                         config.search.input.eventType : 'keypress';

                        process._attachEvent(config.search.input.selector, sEventType, function (e) {
                            if (sEventType === 'keypress' && e.keyCode === 13) {
                                process.search();
                            }
                        });
                    }

                    // check if config.search has 'btn'
                    if (config.search.hasOwnProperty('btn')) {
                        process._attachEvent(config.search.btn, 'click', function () {
                            process.search();
                        });
                    }

                    // check if config.sort has 'selector'
                    if(config.sort.hasOwnProperty('selector')) {

                        process._attachEvent(uiMainContainer.find(config.sort.selector).children(), 'click', function (e) {
                            process.sort(e);
                        });
                    }
                }
            }
        }
    };

    var process = {
        /**
         * For search functionality
         *
         */
        search: function () {
            var uiSearch = uiMainContainer.find(config.search.input.selector),
                oOrder = process._getOrdering(),
                oData = {
                    'keyword' : uiMainContainer.find(config.search.input.selector).val(),
                    'order_by': oOrder.orderBy,
                    'order'   : oOrder.order
                };

            // remove the children of the container
            // also make sure the template is not remove
            process._removeChildrenOfContainer();

            // Process ajax
            process._ajax(config.url, oData, function () {
                uiSearch.attr('data-searched', uiSearch.val());
            });
        },

        /**
         * For load more functionality
         *
         */
        loadMore: function () {
            var oOrder = process._getOrdering(),
                oData = {
                    'keyword' : process._getPrevSearched(),
                    'offset'  : process._getOffset(),
                    'order_by': oOrder.orderBy,
                    'order'   : oOrder.order
                };

            // put previous searched value in the search input
            process._setSearchValue();

            process._ajax(config.url, oData, function (oRetData) {
                process._loadMoreShowHide(oRetData.total_rows, helper.lengthOf(oRetData.data) + oData.offset);
            });
        },

        sort: function (e) {
            var uiTarget = uiMainContainer.find(e.target), oData, oOrder;

            sActiveClass = (config.sort.activeClass !== undefined) ?
                           config.sort.activeClass : 'active sorting';

            if (uiTarget.hasClass(sActiveClass)) {
                if (uiTarget.attr('data-sort') === 'asc') {
                    uiTarget.attr('data-sort', 'desc');
                } else {
                    uiTarget.attr('data-sort', 'asc');
                }
            } else {
                uiTarget
                    .addClass(sActiveClass)
                    .attr('data-sort', 'asc')
                    .siblings("li")
                    .removeClass(sActiveClass).removeAttr('data-sort');
            }

            // put previous searched value in the search input
            process._setSearchValue();

            // get order
            oOrder = process._getOrdering();

            // assign value in oData
            oData = {
                'keyword' : process._getPrevSearched(),
                'order_by': oOrder.orderBy,
                'order'   : oOrder.order
            };

            // remove the children of the container
            // also make sure the template is not remove
            process._removeChildrenOfContainer();

            process._ajax(config.url, oData, function (oRetData) {
                process._loadMoreShowHide(oRetData.total_rows, helper.lengthOf(oRetData.data) + oData.offset);
            });
        },

        /**
         * Show or Hide the Load more button
         *
         * @param {Number} numTotalRows
         * @param {Number} numUIRows
         * @private
         */
        _loadMoreShowHide: function (numTotalRows, numUIRows) {
            var uiLoadMore = uiMainContainer.find(config.loadMore);

            uiLoadMore.text('Load More'); // TODO: This should be check if it is a button or other tag because what if load more is an input then text will not be suitable for it.

            if (numTotalRows === numUIRows || numTotalRows <= config.limit || numUIRows > numTotalRows) {
                uiLoadMore.hide();
            } else {
                // show again the load more button if it is hidden
                uiLoadMore.show();
            }
        },

        /**
         * Remove the children of the container and also
         *  make sure the template is not remove if in case template is inside the container
         *
         * @private
         */
        _removeChildrenOfContainer: function () {
            uiMainContainer.find(config.container).children().not(config.template).remove();
        },

        /**
         * Check if oData has a valid format
         *
         * @param {Object} oData
         * @returns {boolean} If true oData has a valid format
         * @private
         */
        _isValidFormat: function (oData) {
            return (oData.hasOwnProperty('data') && oData.hasOwnProperty('total_rows'));
        },

        /**
         * Set field value, text, or class
         *
         * @param {Object} uiClonedTemplate
         * @param {Object} oInsertData
         * @private
         */
        _setFieldValue: function (uiClonedTemplate, oInsertData) {
            for (var key in oInsertData) {
                if (oInsertData.hasOwnProperty(key)) {
                    // set class of specific DOM
                    uiClonedTemplate.find('[data-class-field="' + key + '"]').addClass(oInsertData[key]);

                    // set text of view field
                    uiClonedTemplate.find('[data-view-field="' + key + '"]').text(oInsertData[key]);

                    // set value of input field
                    uiClonedTemplate.find('[data-input-field="' + key + '"]').val(oInsertData[key]);
                }
            }
        },

        /**
         * Clone the template and append it to container
         *
         * @param {Object} oData  Note: This should have a valid format. Pls refer to ERROR_MSG.objectFormat
         * @returns {String|Null} If this function returns string, cloning failed
         * @private
         */
        _cloning: function (oData) {
            if (!this._isValidFormat(oData)) {
                return ERROR_MSG.objectFormat; // stop if format of the object is invalid
            }

            var uiClonedTemplate;

            for (var key in oData.data) {
                if (oData.data.hasOwnProperty(key)) {
                    // clone the template
                    uiClonedTemplate = uiMainContainer.find(config.template).clone();

                    // set field value, text or class
                    this._setFieldValue(uiClonedTemplate, oData.data[key]);

                    // remove the custom attribute of the cloned template and append it to the container
                    uiClonedTemplate
                        .removeAttr('data-template')// TODO: This should be parsed because this is dynamic
                        .appendTo(uiMainContainer.find(config.container));
                }
            }
        },

        /**
         * Ajax Processing function
         *
         * @param {String} sUrl
         * @param {Object} oData
         * @param [fnAdditionalCallback]
         * @private
         */
        _ajax: function (sUrl, oData, fnAdditionalCallback) {
            // check if csrf_token is set
            if(helper.lengthOf(config.csrfToken)) {
                oData[config.csrfToken.name] = config.csrfToken.value;
            }

            $.ajax(sUrl, {
                type   : 'POST',
                data   : oData,
                success: function (sRetData) {
                    if (typeof sRetData === 'string' && sRetData.length) {
                        var oRetData = $.parseJSON(sRetData);

                        if (!process._isValidFormat(oRetData)) {
                            return ERROR_MSG.objectFormat; // stop if format of the object is invalid
                        }

                        if (helper.lengthOf(oRetData.data)) {
                            process._cloning(oRetData);

                            if (typeof fnAdditionalCallback === 'function') {
                                fnAdditionalCallback(oRetData);
                            }
                        }
                    }
                }
            });
        },

        /**
         * Attach an event to a specific selector
         *
         * @param {String} sSelector
         * @param {String} sEventType
         * @param fnCallback
         * @private
         */
        _attachEvent: function (sSelector, sEventType, fnCallback) {
            uiMainContainer.find(sSelector).on(sEventType, function (e) {
                e.stopPropagation();
                if (typeof fnCallback === 'function') {
                    fnCallback(e);
                }
            });
        },

        /**
         * Returns the value of Previous searched keyword
         * and additionally set the value of the input from the prev keyword
         *
         * @return String
         * @private
         */
        _getPrevSearched: function () {
            var uiInputSearch = uiMainContainer.find(config.search.input.selector),
                sSearchedKeyword = uiInputSearch.attr('data-searched'),
                sReturnVal = '';

            if (sSearchedKeyword !== undefined) {
                sReturnVal = sSearchedKeyword.trim();
            }

            // set the value of search
            uiInputSearch.val(sReturnVal);

            return sReturnVal;
        },

        /**
         * Get the ordering details
         *
         * @returns {{orderBy: *, order: *}}
         * @private
         */
        _getOrdering: function () {
            // cache DOM sort for better performance
            var sProcessedClass = helper.classBuilder(sActiveClass),
                uiSort = uiMainContainer.find(sProcessedClass);

            if (!helper.isValidUI(uiSort)) {
                // stop if sort DOM not found and return data
                return {orderBy: null, order: null};
            }

            return {
                orderBy: uiSort.attr('data-sort-field'),
                order   : uiSort.attr('data-sort')
            };
        },

        /**
         * Gets the length of shown children of the container
         *
         * @returns {Number} The number of shown children of the container
         * @private
         * @todo Should put a selector in the children of the container for specificity and for more flexibity of the program
         */
        _getOffset: function () {
            return uiMainContainer.find(config.container).children().not(config.template).length;
        },

        /**
         * Set the value of search input from the previous searched item.
         *
         * @private
         */
        _setSearchValue: function () {
            var uiSearch = uiMainContainer.find(config.search.input),
                sDataSearch = uiSearch.attr('data-searched');

            if(sDataSearch !== undefined && sDataSearch.length) {
                // set the value of search input to it's data-searched (previous searched)
                uiSearch.val(sDataSearch);
            }
        }
    };

    var helper = {
        /**
         * Helper to know the length of a specific object
         *
         * @param {Object} oObject
         *
         * @returns {number} Length of an oObject
         */
        lengthOf: function (oObject) {
            if (oObject === undefined && typeof oObject !== 'object') {
                return 0; // stop here if oObject is not defined
            }

            var size = 0, key;

            for (key in oObject) {
                if (oObject.hasOwnProperty(key)) {
                    size++;
                }
            }

            return size;
        },

        /**
         * Validates ui if exist or not
         *
         * @param {Object} ui jQuery Object
         *
         * @returns {Boolean} True if found
         */
        isValidUI: function (ui) {
            return (ui !== undefined && ui.length > 0);
        },

        /**
         * Build the class so that it will have a dot(.) sign in the beginning of each
         *
         * @param {String} sClasses Space Concatenated classes. Example 'active sorting'
         *
         * @returns {String} Returns a concatenated class. Example '.active.sorting'
         */
        classBuilder: function (sClasses) {
            var sReturnClass = '',
                arrClasses = [];

            if(sClasses !== undefined && sClasses.trim().length > 0) {
                arrClasses = sClasses.trim().split(' ');
                sReturnClass = '.' + arrClasses.join('.');
            }

            return sReturnClass;
        }
    };

    /**
     * Extends jQuery
     *
     * @param {Object} options Options of the plugin
     *
     * @returns {jQuery}
     * @constructor
     */
    $.fn.cloneFiltering = function (options) {

        if (!this[0]) {
            return this; // stop here if main container not found
        }

        uiMainContainer = this; // put the main container to the private variable for whole scope availability

        init(options); // initialization

        return this; // for chaining of the jQuery
    };
}(jQuery));<commit_msg>added a custom ajax functionality<commit_after>/*jshint nonew:true, jquery:true, curly:true, noarg:true, forin:true, noempty:true, eqeqeq:true, strict:true, undef:true, bitwise:true, newcap:true, immed:true, onevar:true, browser:true, es3:true, gcl:true */
/**
 * $('#main_container')                             // Container of the load more, search, and sort
 *      .cloneFiltering({
 *          container: '#container',                // (REQUIRED) Container of the template to be inserted
 *          template: '#template',                  // (REQUIRED) Template to be cloned
 *          url: 'localhost/ajax/get_something',    // (OPTIONAL) URL of the data needed for the cloning and filtering
 *          csrfToken: {                            // (OPTIONAL) For security reason
 *              value: 'x5925626lsd62',
 *              name: 'my_token'
 *          },
 *          data: oData,                            // (OPTIONAL) If from ajax success this is required
 *                                                                  if url is set do not use this
 *          limit: 10,                              // (OPTIONAL) Will be using in load more
 *          loadMore: 'button.load_more',           // (OPTIONAL) Selector of the load more button MUST BE A BUTTON
 *          search: {                               // (OPTIONAL) Selector of the search input MUST BE AN INPUT
 *              input: {
 *                  selector: 'input.search',
 *                  eventType: 'keypress'
 *              },
 *              btn: 'div.search_btn'
 *          },
 *          sort: {                                 // (OPTIONAL) Selector of the sort
 *              selector: 'ul.sort',                // if sort has been set this selector is required
 *              activeClass: 'active sorting'       // if not set the default active class would be the 'active sorting'
 *          }
 *      });
 *
 * @todo: No documentation yet.
 */
(function ($) {
    "use strict";

    /**
     * Error message container
     * @const
     */
    var ERROR_MSG = {
        ui          : 'DOM not found or do not exists!',
        objectFormat: 'Format of the object is invalid. Format should be:' +
                    '{ ' +
                        'data:' +
                            '{ 0:' +
                                '{ any: "value" }' +
                                '{ any: "value" }' +
                                '...' +
                            '},' +
                             '...' +
                        'total_rows: 10' +
                    '}'
    };

    /**
     * DOM main container of the cloning or filtering
     *
     * @param {Object} uiMainContainer jQuery object referring to the main container. This will attach event to this DOM
     */
    var uiMainContainer;

    /**
     * Configuration of the plugin
     *
     */
    var config = {};

    /**
     * Default value of the plugin
     *
     */
    var defaults = {
        container: '#container',
        template : '#template',
        limit    : 10
    };

    /**
     * Storage of the active class
     *
     * @type {string}
     */
    var sActiveClass = '';

    var init = function (options) {
        config = $.extend(defaults, options);

        // find for the container and the template
        var uiContainer = uiMainContainer.find(config.container),
            uiTemplate = uiMainContainer.find(config.template);

        // uiContainer && uiTemplate should be present so validate this
        if (!helper.isValidUI(uiContainer) && !helper.isValidUI(uiTemplate)) {
            return config.container + ' and ' + config.template + ' ' + ERROR_MSG.ui; // stop if the two is not found
        }

        // if config.data is found or set process cloning immediately
        if (helper.lengthOf(config.data)) {
            process._cloning(config.data);
        }

        // if url is defined then one of the following should also be defined:
        // config.sort, config.loadMore or config.search
        else if (config.url !== undefined) {

            // check first if config.search is an object and has at least one element in it
            if (helper.lengthOf(config.search)) {

                // check if config.search has 'input' property
                if (config.search.hasOwnProperty('input')) {

                    // check if config has 'loadMore'
                    if (config.hasOwnProperty('loadMore')) {
                        process._attachEvent(config.loadMore, 'click', function () {
                            process.loadMore();
                        });
                    }

                    // TODO: no structure validation yet in search, sort and etc.
                    // check if config.search.input has 'selector'
                    if (config.search.input.hasOwnProperty('selector')) {
                        var sEventType = (config.search.input.eventType !== undefined) ?
                                         config.search.input.eventType : 'keypress';

                        process._attachEvent(config.search.input.selector, sEventType, function (e) {
                            if (sEventType === 'keypress' && e.keyCode === 13) {
                                process.search();
                            }
                        });
                    }

                    // check if config.search has 'btn'
                    if (config.search.hasOwnProperty('btn')) {
                        process._attachEvent(config.search.btn, 'click', function () {
                            process.search();
                        });
                    }

                    // check if config.sort has 'selector'
                    if(config.sort.hasOwnProperty('selector')) {
                        process._attachEvent(uiMainContainer.find(config.sort.selector).children(), 'click', function (e) {
                            process.sort(e);
                        });
                    }
                }
            }
        }
    };

    var process = {
        /**
         * For search functionality
         *
         */
        search: function () {
            var uiSearch = uiMainContainer.find(config.search.input.selector),
                oOrder = process._getOrdering(),
                oData = {
                    'keyword' : uiMainContainer.find(config.search.input.selector).val(),
                    'order_by': oOrder.orderBy,
                    'order'   : oOrder.order
                };

            // Process ajax
            process._ajax(config.url, oData, function () {
                uiSearch.attr('data-searched', uiSearch.val());
            }, function () {
                // remove the children of the container
                // also make sure the template is not remove
                process._removeChildrenOfContainer();
            });
        },

        /**
         * For load more functionality
         *
         */
        loadMore: function () {
            var oOrder = process._getOrdering(),
                oData = {
                    'keyword' : process._getPrevSearched(),
                    'offset'  : process._getOffset(),
                    'order_by': oOrder.orderBy,
                    'order'   : oOrder.order
                };

            // put previous searched value in the search input
            process._setSearchValue();

            process._ajax(config.url, oData, function (oRetData) {
                process._loadMoreShowHide(oRetData.total_rows, helper.lengthOf(oRetData.data) + oData.offset);
            });
        },

        sort: function (e) {
            var uiTarget = uiMainContainer.find(e.target), oData, oOrder;

            sActiveClass = (config.sort.activeClass !== undefined) ?
                           config.sort.activeClass : 'active sorting';

            if (uiTarget.hasClass(sActiveClass)) {
                if (uiTarget.attr('data-sort') === 'asc') {
                    uiTarget.attr('data-sort', 'desc');
                } else {
                    uiTarget.attr('data-sort', 'asc');
                }
            } else {
                uiTarget
                    .addClass(sActiveClass)
                    .attr('data-sort', 'asc')
                    .siblings("li")
                    .removeClass(sActiveClass).removeAttr('data-sort');
            }

            // put previous searched value in the search input
            process._setSearchValue();

            // get order
            oOrder = process._getOrdering();

            // assign value in oData
            oData = {
                'keyword' : process._getPrevSearched(),
                'order_by': oOrder.orderBy,
                'order'   : oOrder.order
            };

            process._ajax(config.url, oData, function (oRetData) {
                process._loadMoreShowHide(oRetData.total_rows, helper.lengthOf(oRetData.data) + oData.offset);
            }, function () {
                // remove the children of the container
                // also make sure the template is not remove
                process._removeChildrenOfContainer();
            });
        },

        /**
         * Show or Hide the Load more button
         *
         * @param {Number} numTotalRows
         * @param {Number} numUIRows
         * @private
         */
        _loadMoreShowHide: function (numTotalRows, numUIRows) {
            var uiLoadMore = uiMainContainer.find(config.loadMore);

            uiLoadMore.text('Load More'); // TODO: This should be check if it is a button or other tag because what if load more is an input then text will not be suitable for it.

            if (numTotalRows === numUIRows || numTotalRows <= config.limit || numUIRows > numTotalRows) {
                uiLoadMore.hide();
            } else {
                // show again the load more button if it is hidden
                uiLoadMore.show();
            }
        },

        /**
         * Remove the children of the container and also
         *  make sure the template is not remove if in case template is inside the container
         *
         * @private
         */
        _removeChildrenOfContainer: function () {
            uiMainContainer.find(config.container).children().not(config.template).remove();
        },

        /**
         * Check if oData has a valid format
         *
         * @param {Object} oData
         * @returns {boolean} If true oData has a valid format
         * @private
         */
        _isValidFormat: function (oData) {
            return (oData.hasOwnProperty('data') && oData.hasOwnProperty('total_rows'));
        },

        /**
         * Set field value, text, or class
         *
         * @param {Object} uiClonedTemplate
         * @param {Object} oInsertData
         * @private
         */
        _setFieldValue: function (uiClonedTemplate, oInsertData) {
            for (var key in oInsertData) {
                if (oInsertData.hasOwnProperty(key)) {
                    // set class of specific DOM
                    uiClonedTemplate.find('[data-class-field="' + key + '"]').addClass(oInsertData[key]);

                    // set text of view field
                    uiClonedTemplate.find('[data-view-field="' + key + '"]').text(oInsertData[key]);

                    // set value of input field
                    uiClonedTemplate.find('[data-input-field="' + key + '"]').val(oInsertData[key]);
                }
            }
        },

        /**
         * Clone the template and append it to container
         *
         * @param {Object} oData  Note: This should have a valid format. Pls refer to ERROR_MSG.objectFormat
         * @returns {String|Null} If this function returns string, cloning failed
         * @private
         */
        _cloning: function (oData) {
            if (!this._isValidFormat(oData)) {
                return ERROR_MSG.objectFormat; // stop if format of the object is invalid
            }

            var uiClonedTemplate;

            for (var key in oData.data) {
                if (oData.data.hasOwnProperty(key)) {
                    // clone the template
                    uiClonedTemplate = uiMainContainer.find(config.template).clone();

                    // set field value, text or class
                    this._setFieldValue(uiClonedTemplate, oData.data[key]);

                    // remove the custom attribute of the cloned template and append it to the container
                    uiClonedTemplate
                        .removeAttr('data-template')// TODO: This should be parsed because this is dynamic
                        .appendTo(uiMainContainer.find(config.container));
                }
            }
        },

        /**
         * Ajax Processing function
         *
         * @param {String} sUrl
         * @param {Object} oData
         * @param [fnAdditionalCallback]
         * @param [fnBeforeCloning]      Function that will be called before cloning
         * @private
         */
        _ajax: function (sUrl, oData, fnAdditionalCallback, fnBeforeCloning) {
            var oSettings = {};

            // check if csrf_token is set
            if(helper.lengthOf(config.csrfToken)) {
                oData[config.csrfToken.name] = config.csrfToken.value;
            }

            oSettings = {
                url    : sUrl,
                type   : 'POST',
                data   : oData,
                success: function (sRetData) {
                    if (typeof sRetData === 'string' && sRetData.length) {
                        var oRetData = $.parseJSON(sRetData);

                        if (!process._isValidFormat(oRetData)) {
                            return ERROR_MSG.objectFormat; // stop if format of the object is invalid
                        }

                        if (helper.lengthOf(oRetData.data)) {
                            if (typeof fnBeforeCloning === 'function') {
                                fnBeforeCloning(oRetData);
                            }

                            process._cloning(oRetData);

                            if (typeof fnAdditionalCallback === 'function') {
                                fnAdditionalCallback(oRetData);
                            }
                        }
                    }
                }
            };

            if(config.hasOwnProperty('customAjax') && typeof config.customAjax === "function") {
                config.customAjax(oSettings);
            } else {
                $.ajax(oSettings);
            }
        },

        /**
         * Attach an event to a specific selector
         *
         * @param {String} sSelector
         * @param {String} sEventType
         * @param fnCallback
         * @private
         */
        _attachEvent: function (sSelector, sEventType, fnCallback) {
            uiMainContainer.find(sSelector).on(sEventType, function (e) {
                e.stopPropagation();
                e.preventDefault();
                if (typeof fnCallback === 'function') {
                    fnCallback(e);
                }
            });
        },

        /**
         * Returns the value of Previous searched keyword
         * and additionally set the value of the input from the prev keyword
         *
         * @return String
         * @private
         */
        _getPrevSearched: function () {
            var uiInputSearch = uiMainContainer.find(config.search.input.selector),
                sSearchedKeyword = uiInputSearch.attr('data-searched'),
                sReturnVal = '';

            if (sSearchedKeyword !== undefined) {
                sReturnVal = sSearchedKeyword.trim();
            }

            // set the value of search
            uiInputSearch.val(sReturnVal);

            return sReturnVal;
        },

        /**
         * Get the ordering details
         *
         * @returns {{orderBy: *, order: *}}
         * @private
         */
        _getOrdering: function () {
            // cache DOM sort for better performance
            var sProcessedClass = helper.classBuilder(sActiveClass),
                uiSort = uiMainContainer.find(sProcessedClass);

            if (!helper.isValidUI(uiSort)) {
                // stop if sort DOM not found and return data
                return {orderBy: null, order: null};
            }

            return {
                orderBy: uiSort.attr('data-sort-field'),
                order  : uiSort.attr('data-sort')
            };
        },

        /**
         * Gets the length of shown children of the container
         *
         * @returns {Number} The number of shown children of the container
         * @private
         * @todo Should put a selector in the children of the container for specificity and for more flexibity of the program
         */
        _getOffset: function () {
            return uiMainContainer.find(config.container).children().not(config.template).length;
        },

        /**
         * Set the value of search input from the previous searched item.
         *
         * @private
         */
        _setSearchValue: function () {
            var uiSearch = uiMainContainer.find(config.search.input),
                sDataSearch = uiSearch.attr('data-searched');

            if(sDataSearch !== undefined && sDataSearch.length) {
                // set the value of search input to it's data-searched (previous searched)
                uiSearch.val(sDataSearch);
            }
        }
    };

    var helper = {
        /**
         * Helper to know the length of a specific object
         *
         * @param {Object} oObject
         *
         * @returns {number} Length of an oObject
         */
        lengthOf: function (oObject) {
            if (oObject === undefined && typeof oObject !== 'object') {
                return 0; // stop here if oObject is not defined
            }

            var size = 0, key;

            for (key in oObject) {
                if (oObject.hasOwnProperty(key)) {
                    size++;
                }
            }

            return size;
        },

        /**
         * Validates ui if exist or not
         *
         * @param {Object} ui jQuery Object
         *
         * @returns {Boolean} True if found
         */
        isValidUI: function (ui) {
            return (ui !== undefined && ui.length > 0);
        },

        /**
         * Build the class so that it will have a dot(.) sign in the beginning of each
         *
         * @param {String} sClasses Space Concatenated classes. Example 'active sorting'
         *
         * @returns {String} Returns a concatenated class. Example '.active.sorting'
         */
        classBuilder: function (sClasses) {
            var sReturnClass = '',
                arrClasses = [];

            if(sClasses !== undefined && sClasses.trim().length > 0) {
                arrClasses = sClasses.trim().split(' ');
                sReturnClass = '.' + arrClasses.join('.');
            }

            return sReturnClass;
        }
    };

    /**
     * Extends jQuery
     *
     * @param {Object} options Options of the plugin
     *
     * @returns {jQuery}
     * @constructor
     */
    $.fn.cloneFiltering = function (options) {

        if (!this[0]) {
            return this; // stop here if main container not found
        }

        uiMainContainer = this; // put the main container to the private variable for whole scope availability

        init(options); // initialization

        return this; // for chaining of the jQuery
    };
}(jQuery));<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { withApollo, gql } from 'react-apollo'
import _ from 'lodash'

import withData from '../lib/withData'
import { GC_USER_ID, GC_USERNAME } from '../constants'
import { FIRSTLOAD_CHATROOMS_QUERY } from './ChatList'

class NewChat extends Component {

  constructor(props) {
    super(props);
    this.state = {
      title: '',
    }
  }

  onCreateChat = async (e) => {
    e.preventDefault()

    const currentUserId = this.props.currentUserId

    if(!currentUserId) {
      alert('You must log in first.')
      return
    }

    try {
      const userCount = (await this.props.client.query({ query: USER_COUNT_QUERY })).data._allUsersMeta.count
      const randomSkip = _.random(0, userCount - 2)
      if(userCount - 2 < 0) {
        alert("Oops, can't find any users.")
        return
      }

      // alert('random skip is' + randomSkip)

      // To prevent duplicate, get two random users, at least one will never be a duplicate.
      const twoRandomUsers  = (await this.props.client.query({
        query: GET_USERS_QUERY,
        variables: {
          first: 2,
          skip: randomSkip,
        }
      })).data.allUsers

      const twoRandomUserIds = twoRandomUsers.map(u => u.id)

      // alert('fetched users are' + twoRandomUsers.map(u => u.username).join(' '))

      let anotherUserId;

      // if both not same as current user id, can random
      if (twoRandomUserIds[0] !== currentUserId && twoRandomUserIds[1] !== currentUserId) {
        anotherUserId = _.sample(twoRandomUserIds)
      } else {
        // some is duplicate, choose one that's not
        anotherUserId = twoRandomUserIds[0] !== currentUserId ? twoRandomUserIds[0] : twoRandomUserIds[1]
      }

      const { data: { createChatroom: { id } } } = await this.props.client.mutate({
        mutation: CREATE_CHAT_MUTATION,
        variables: {
          title: this.state.title,
          userIds: [currentUserId, anotherUserId],
        },
        optimisticResponse: {
          __typename: 'Mutation',
          createChatroom: {
            __typename: 'Chatroom',
            id: '',
            createdAt: (new Date()).toISOString(),
            title: this.state.title,
            users: [
              {
                __typename: 'User',
                id: '',
                username: localStorage.getItem(GC_USERNAME),
              }
            ],
          }
        },
        update: (store, { data: { createChatroom }}) => {

          // Update the store (so that the graphql components across the app will get updated)

          console.log('should update!')
          // 1. read from store
          const data = store.readQuery({
            query: FIRSTLOAD_CHATROOMS_QUERY,
          })

          // Must update messagesMeta manually, since the newly added object doesn't has one
          createChatroom._messagesMeta = {
            count: 0,
            __typename: '_QueryMeta',
          }

          // 2. append at first position
          data.allChatrooms.splice(0,0,createChatroom)
          data._allChatroomsMeta.count += 1

          // 3. write back
          store.writeQuery({
            query: FIRSTLOAD_CHATROOMS_QUERY,
            data
          })
        },
      })

      this.setState({
        title: '',
      })
      this.props.onCreateNewChatroom(id)
    } catch(err) {
      alert("Oops: " + err.graphQLErrors[0].message);
    }
  }

  render() {
    const confirmDisabled = !this.state.title
    return (

        <form onSubmit={this.onCreateChat}>
          <input
              value={this.state.title}
              onChange={e => this.setState({ title: e.target.value})}
              placeholder="insert a topic here"
              type="text"
              className="add-chat-input"
          />

          <button
              className="primary-button"
              type="submit"
              disabled={confirmDisabled}
          >
              Create chat
          </button>

        </form>

)
    }
}

const CREATE_CHAT_MUTATION = gql`
  mutation CreateChatroomMutation($title: String!, $userIds: [ID!]!) {
    createChatroom(title: $title, usersIds: $userIds) {
      id,
      createdAt,
      title,
      users {
        id,
        username,
      }
    }
  }
`

const USER_COUNT_QUERY = gql`
  query UserCountQuery {
    _allUsersMeta {
      count
    }
  }
`

const GET_USERS_QUERY =  gql`
  query GetUsersQuery($first: Int!, $skip: Int!) {
    allUsers(first: $first, skip: $skip) {
      id,
      username,
    }
  }
`

NewChat.propTypes = {
  currentUserId: React.PropTypes.string.isRequired,
}

export default withApollo(NewChat)
<commit_msg>Remove unused log<commit_after>import React, { Component } from 'react'
import { withApollo, gql } from 'react-apollo'
import _ from 'lodash'

import withData from '../lib/withData'
import { GC_USER_ID, GC_USERNAME } from '../constants'
import { FIRSTLOAD_CHATROOMS_QUERY } from './ChatList'

class NewChat extends Component {

  constructor(props) {
    super(props);
    this.state = {
      title: '',
    }
  }

  onCreateChat = async (e) => {
    e.preventDefault()

    const currentUserId = this.props.currentUserId

    if(!currentUserId) {
      alert('You must log in first.')
      return
    }

    try {
      const userCount = (await this.props.client.query({ query: USER_COUNT_QUERY })).data._allUsersMeta.count
      const randomSkip = _.random(0, userCount - 2)
      if(userCount - 2 < 0) {
        alert("Oops, can't find any users.")
        return
      }

      // alert('random skip is' + randomSkip)

      // To prevent duplicate, get two random users, at least one will never be a duplicate.
      const twoRandomUsers  = (await this.props.client.query({
        query: GET_USERS_QUERY,
        variables: {
          first: 2,
          skip: randomSkip,
        }
      })).data.allUsers

      const twoRandomUserIds = twoRandomUsers.map(u => u.id)

      // alert('fetched users are' + twoRandomUsers.map(u => u.username).join(' '))

      let anotherUserId;

      // if both not same as current user id, can random
      if (twoRandomUserIds[0] !== currentUserId && twoRandomUserIds[1] !== currentUserId) {
        anotherUserId = _.sample(twoRandomUserIds)
      } else {
        // some is duplicate, choose one that's not
        anotherUserId = twoRandomUserIds[0] !== currentUserId ? twoRandomUserIds[0] : twoRandomUserIds[1]
      }

      const { data: { createChatroom: { id } } } = await this.props.client.mutate({
        mutation: CREATE_CHAT_MUTATION,
        variables: {
          title: this.state.title,
          userIds: [currentUserId, anotherUserId],
        },
        optimisticResponse: {
          __typename: 'Mutation',
          createChatroom: {
            __typename: 'Chatroom',
            id: '',
            createdAt: (new Date()).toISOString(),
            title: this.state.title,
            users: [
              {
                __typename: 'User',
                id: '',
                username: localStorage.getItem(GC_USERNAME),
              }
            ],
          }
        },
        update: (store, { data: { createChatroom }}) => {

          // Update the store (so that the graphql components across the app will get updated)

          // 1. read from store
          const data = store.readQuery({
            query: FIRSTLOAD_CHATROOMS_QUERY,
          })

          // Must update messagesMeta manually, since the newly added object doesn't has one
          createChatroom._messagesMeta = {
            count: 0,
            __typename: '_QueryMeta',
          }

          // 2. append at first position
          data.allChatrooms.splice(0,0,createChatroom)
          data._allChatroomsMeta.count += 1

          // 3. write back
          store.writeQuery({
            query: FIRSTLOAD_CHATROOMS_QUERY,
            data
          })
        },
      })

      this.setState({
        title: '',
      })
      this.props.onCreateNewChatroom(id)
    } catch(err) {
      alert("Oops: " + err.graphQLErrors[0].message);
    }
  }

  render() {
    const confirmDisabled = !this.state.title
    return (

        <form onSubmit={this.onCreateChat}>
          <input
              value={this.state.title}
              onChange={e => this.setState({ title: e.target.value})}
              placeholder="insert a topic here"
              type="text"
              className="add-chat-input"
          />

          <button
              className="primary-button"
              type="submit"
              disabled={confirmDisabled}
          >
              Create chat
          </button>

        </form>

)
    }
}

const CREATE_CHAT_MUTATION = gql`
  mutation CreateChatroomMutation($title: String!, $userIds: [ID!]!) {
    createChatroom(title: $title, usersIds: $userIds) {
      id,
      createdAt,
      title,
      users {
        id,
        username,
      }
    }
  }
`

const USER_COUNT_QUERY = gql`
  query UserCountQuery {
    _allUsersMeta {
      count
    }
  }
`

const GET_USERS_QUERY =  gql`
  query GetUsersQuery($first: Int!, $skip: Int!) {
    allUsers(first: $first, skip: $skip) {
      id,
      username,
    }
  }
`

NewChat.propTypes = {
  currentUserId: React.PropTypes.string.isRequired,
}

export default withApollo(NewChat)
<|endoftext|>


<commit_before>function clearContainer (container) {
    for (var i=0,ilen=container.childNodes.length;i<ilen;i+=1) {
        container.removeChild(container.childNodes[0]);
    }
}

function showMistakes () {
    var commenterID = getParameterByName('commenter');
    var classID = getParameterByName('classid');
    var quizNumber = getParameterByName('quizno');
    var quizMistakes = apiRequest(
        '/?commenter='
            + commenterID
            +'&page=quiz&cmd=quizmistakes'
            + '&classid='
            + classID
            + '&quizno='
            + quizNumber);
    if (false === quizMistakes) return;
    // Empty the container
    var container = document.getElementById('quiz-mistakes');
    clearContainer(container);
    // Get name of commenter from return
    var commenter = quizMistakes.commenter;
    // For each mistake ...
    console.log("Number of mistakes: "+quizMistakes.mistakes.length);
    console.log("JSON: "+JSON.stringify(quizMistakes,null,2));
    for (var i=0,ilen=quizMistakes.mistakes.length;i<ilen;i+=1) {
        var mistake = quizMistakes.mistakes[i];
        var mistakeDiv = document.createElement('div');
        mistakeDiv.setAttribute('id', 'mistake-' + mistake.questionNumber + '-' + mistake.wrongChoice);
        var rubricText = markdown(mistake.rubric);
        var correctText = markdown(mistake.correct);
        var wrongText = markdown(mistake.wrong);
        var buttonMode = {edit:'none',comment:'inline',save:'none'};
        if (mistake.hasCommenterComment) {
            buttonMode = {edit:'inline',comment:'none',save:'none'};
        }
        mistakeDiv.innerHTML = '

' + rubricText + '
'
            + '
' + correctText + '
'
            + '
' + wrongText + '

'
            + '
'
            + '<input type="button" class="button" '
            + 'id="comment-button-' + mistake.questionNumber + '-' + mistake.wrongChoice + '" '
            + 'style="display:'
            + buttonMode.comment
            + '" value="Comment" '
            + 'onclick="newComment(this,\'comment-' + commenter + '-' + mistake.questionNumber + '-' + mistake.wrongChoice + '\')"'
            +'/>'
            + '<input type="button" class="button" '
            + 'id="edit-button-' + mistake.questionNumber + '-' + mistake.wrongChoice + '" '
            + 'style="display:'
            + buttonMode.edit
            + '" value="Edit" '
            + 'onclick="openComment(\'comment-' + commenter + '-' + mistake.questionNumber + '-' + mistake.wrongChoice + '\')"'
            +'/>'
            + '<input type="button" class="button" '
            + 'id="save-button-' + mistake.questionNumber + '-' + mistake.wrongChoice + '" '
            + 'style="display:'
            + buttonMode.save
            + '" value="Save" '
            + 'onclick="saveComment(\'comment-' + commenter + '-' + mistake.questionNumber + '-' + mistake.wrongChoice + '\')"'
            +'/>'
            + '
';
        var questionNumber = mistake.questionNumber;
        var wrongChoice = mistake.wrongChoice;
        for (var j=0,jlen=mistake.comments.length;j<jlen;j+=1) {
            var commenter = mistake.comments[j].commenter;
            var comment = mistake.comments[j].comment;
            var commentContainer = buildComment(questionNumber,wrongChoice,commenter,comment);
            mistakeDiv.appendChild(commentContainer);
        }
        container.appendChild(mistakeDiv);

    }
}

function buildComment (questionNumber,wrongChoice,commenter,comment) {
    var commentContainer = document.createElement('div');
    commentContainer.setAttribute('class', 'comment-container');
    commentContainer.setAttribute('id','comment-' + commenter + '-' + questionNumber + '-' + wrongChoice);
    commenterDiv = document.createElement('div');
    commenterDiv.setAttribute('class', 'commenter-name');
    commenterDiv.innerHTML = commenter;
    commentDiv = document.createElement('div');
    commentDiv.innerHTML = markdown(comment);
    commentContainer.appendChild(commenterDiv);
    commentContainer.appendChild(commentDiv);
    return commentContainer;
}

function openComment (id) {
    var commenterID = getParameterByName('commenter');
    var classID = getParameterByName('classid');
    var quizNumber = getParameterByName('quizno');
    var m = id.split('-');
    var questionNumber = m[2];
    var wrongChoice = m[3];
    var commentText = apiRequest(
        '/?commenter='
            + commenterID
            +'&page=quiz&cmd=getonecomment'
            + '&classid='
            + classID
            + '&quizno='
            + quizNumber
        , {
            questionno:questionNumber,
            wrongchoice: wrongChoice
        }
    );
    if (false === commentText) return;

    var node = document.getElementById(id);
    buildOpenComment(node,questionNumber,wrongChoice,commentText);
}

function buildOpenComment(node,questionNumber,wrongChoice,commentText) {
    node.innerHTML = '<textarea style="width:100%" cols="60" rows="3" class="selection-text" placeholder="Saving a comment without content will remove it">'
        + commentText
        + '</textarea>'
    setButtonMode('save',questionNumber,wrongChoice);
}

function saveComment (id) {
    var adminID = getParameterByName('admin');
    var classID = getParameterByName('classid');
    var quizNumber = getParameterByName('quizno');
    var commenter = getParameterByName('commenter');
    var m = id.split('-');
    var questionNumber = m[2];
    var wrongChoice = m[3];
    var commenter = m[1];
    var node = document.getElementById(id);
    var comment = node.firstChild.value;
    if (comment) {
        var commentBlock = buildComment(questionNumber,wrongChoice,commenter,comment);
        node.parentNode.insertBefore(commentBlock,node);
        node.parentNode.removeChild(node);
        setButtonMode('edit',questionNumber,wrongChoice);
    } else {
        // Delete from server, remove the node, restore to comment mode
        node.parentNode.removeChild(node);
        setButtonMode('comment',questionNumber,wrongChoice)
    }
    writeComment(questionNumber,wrongChoice,comment);
}

function newComment(button,id) {
    var parent = button.parentNode;
    var uncle = parent.nextSibling;
    var adminID = getParameterByName('admin');
    var classID = getParameterByName('classid');
    var quizNumber = getParameterByName('quizno');
    var commenter = getParameterByName('commenter');
    var m = id.split('-');
    var questionNumber = m[2];
    var wrongChoice = m[3];
    var commenter = m[1];
    var commentContainer = document.createElement('div');
    commentContainer.setAttribute('id',id);
    commentContainer.setAttribute('class','comment-container');
    parent.parentNode.insertBefore(commentContainer,uncle);
    buildOpenComment(commentContainer,questionNumber,wrongChoice,'');
}

function setButtonMode (mode,questionNumber,wrongChoice) {
    var commentButton = document.getElementById('comment-button-' + questionNumber + '-' + wrongChoice);
    var editButton = document.getElementById('edit-button-' + questionNumber + '-' + wrongChoice);
    var saveButton = document.getElementById('save-button-' + questionNumber + '-' + wrongChoice);
    if (mode === 'edit') {
        commentButton.setAttribute('style', 'display:none');
        editButton.setAttribute('style', 'display:inline');
        saveButton.setAttribute('style', 'display:none');
    } else if (mode === 'save') {
        commentButton.setAttribute('style', 'display:none');
        editButton.setAttribute('style', 'display:none');
        saveButton.setAttribute('style', 'display:inline');
    } else {
        commentButton.setAttribute('style', 'display:inline');
        editButton.setAttribute('style', 'display:none');
        saveButton.setAttribute('style', 'display:none');
    }
};

function writeComment (questionNumber,wrongChoice,comment) {
    var commenterID = getParameterByName('commenter');
    var classID = getParameterByName('classid');
    var quizNumber = getParameterByName('quizno');
    if (comment) {
        comment = comment.replace(/^\s+/,'').replace(/\s+$/,'');
    }
    var ignoreStr = apiRequest(
        '/?commenter='
            + commenterID
            +'&page=quiz&cmd=writeonecomment'
            + '&classid='
            + classID
            + '&quizno='
            + quizNumber
        , {
            questionno:questionNumber,
            wrongchoice: wrongChoice,
            comment: comment
        }
    );
}
<commit_msg>Drop some client-side chatter<commit_after>function clearContainer (container) {
    for (var i=0,ilen=container.childNodes.length;i<ilen;i+=1) {
        container.removeChild(container.childNodes[0]);
    }
}

function showMistakes () {
    var commenterID = getParameterByName('commenter');
    var classID = getParameterByName('classid');
    var quizNumber = getParameterByName('quizno');
    var quizMistakes = apiRequest(
        '/?commenter='
            + commenterID
            +'&page=quiz&cmd=quizmistakes'
            + '&classid='
            + classID
            + '&quizno='
            + quizNumber);
    if (false === quizMistakes) return;
    // Empty the container
    var container = document.getElementById('quiz-mistakes');
    clearContainer(container);
    // Get name of commenter from return
    var commenter = quizMistakes.commenter;
    // For each mistake ...
    for (var i=0,ilen=quizMistakes.mistakes.length;i<ilen;i+=1) {
        var mistake = quizMistakes.mistakes[i];
        var mistakeDiv = document.createElement('div');
        mistakeDiv.setAttribute('id', 'mistake-' + mistake.questionNumber + '-' + mistake.wrongChoice);
        var rubricText = markdown(mistake.rubric);
        var correctText = markdown(mistake.correct);
        var wrongText = markdown(mistake.wrong);
        var buttonMode = {edit:'none',comment:'inline',save:'none'};
        if (mistake.hasCommenterComment) {
            buttonMode = {edit:'inline',comment:'none',save:'none'};
        }
        mistakeDiv.innerHTML = '

' + rubricText + '
'
            + '
' + correctText + '
'
            + '
' + wrongText + '

'
            + '
'
            + '<input type="button" class="button" '
            + 'id="comment-button-' + mistake.questionNumber + '-' + mistake.wrongChoice + '" '
            + 'style="display:'
            + buttonMode.comment
            + '" value="Comment" '
            + 'onclick="newComment(this,\'comment-' + commenter + '-' + mistake.questionNumber + '-' + mistake.wrongChoice + '\')"'
            +'/>'
            + '<input type="button" class="button" '
            + 'id="edit-button-' + mistake.questionNumber + '-' + mistake.wrongChoice + '" '
            + 'style="display:'
            + buttonMode.edit
            + '" value="Edit" '
            + 'onclick="openComment(\'comment-' + commenter + '-' + mistake.questionNumber + '-' + mistake.wrongChoice + '\')"'
            +'/>'
            + '<input type="button" class="button" '
            + 'id="save-button-' + mistake.questionNumber + '-' + mistake.wrongChoice + '" '
            + 'style="display:'
            + buttonMode.save
            + '" value="Save" '
            + 'onclick="saveComment(\'comment-' + commenter + '-' + mistake.questionNumber + '-' + mistake.wrongChoice + '\')"'
            +'/>'
            + '
';
        var questionNumber = mistake.questionNumber;
        var wrongChoice = mistake.wrongChoice;
        for (var j=0,jlen=mistake.comments.length;j<jlen;j+=1) {
            var commenter = mistake.comments[j].commenter;
            var comment = mistake.comments[j].comment;
            var commentContainer = buildComment(questionNumber,wrongChoice,commenter,comment);
            mistakeDiv.appendChild(commentContainer);
        }
        container.appendChild(mistakeDiv);

    }
}

function buildComment (questionNumber,wrongChoice,commenter,comment) {
    var commentContainer = document.createElement('div');
    commentContainer.setAttribute('class', 'comment-container');
    commentContainer.setAttribute('id','comment-' + commenter + '-' + questionNumber + '-' + wrongChoice);
    commenterDiv = document.createElement('div');
    commenterDiv.setAttribute('class', 'commenter-name');
    commenterDiv.innerHTML = commenter;
    commentDiv = document.createElement('div');
    commentDiv.innerHTML = markdown(comment);
    commentContainer.appendChild(commenterDiv);
    commentContainer.appendChild(commentDiv);
    return commentContainer;
}

function openComment (id) {
    var commenterID = getParameterByName('commenter');
    var classID = getParameterByName('classid');
    var quizNumber = getParameterByName('quizno');
    var m = id.split('-');
    var questionNumber = m[2];
    var wrongChoice = m[3];
    var commentText = apiRequest(
        '/?commenter='
            + commenterID
            +'&page=quiz&cmd=getonecomment'
            + '&classid='
            + classID
            + '&quizno='
            + quizNumber
        , {
            questionno:questionNumber,
            wrongchoice: wrongChoice
        }
    );
    if (false === commentText) return;

    var node = document.getElementById(id);
    buildOpenComment(node,questionNumber,wrongChoice,commentText);
}

function buildOpenComment(node,questionNumber,wrongChoice,commentText) {
    node.innerHTML = '<textarea style="width:100%" cols="60" rows="3" class="selection-text" placeholder="Saving a comment without content will remove it">'
        + commentText
        + '</textarea>'
    setButtonMode('save',questionNumber,wrongChoice);
}

function saveComment (id) {
    var adminID = getParameterByName('admin');
    var classID = getParameterByName('classid');
    var quizNumber = getParameterByName('quizno');
    var commenter = getParameterByName('commenter');
    var m = id.split('-');
    var questionNumber = m[2];
    var wrongChoice = m[3];
    var commenter = m[1];
    var node = document.getElementById(id);
    var comment = node.firstChild.value;
    if (comment) {
        var commentBlock = buildComment(questionNumber,wrongChoice,commenter,comment);
        node.parentNode.insertBefore(commentBlock,node);
        node.parentNode.removeChild(node);
        setButtonMode('edit',questionNumber,wrongChoice);
    } else {
        // Delete from server, remove the node, restore to comment mode
        node.parentNode.removeChild(node);
        setButtonMode('comment',questionNumber,wrongChoice)
    }
    writeComment(questionNumber,wrongChoice,comment);
}

function newComment(button,id) {
    var parent = button.parentNode;
    var uncle = parent.nextSibling;
    var adminID = getParameterByName('admin');
    var classID = getParameterByName('classid');
    var quizNumber = getParameterByName('quizno');
    var commenter = getParameterByName('commenter');
    var m = id.split('-');
    var questionNumber = m[2];
    var wrongChoice = m[3];
    var commenter = m[1];
    var commentContainer = document.createElement('div');
    commentContainer.setAttribute('id',id);
    commentContainer.setAttribute('class','comment-container');
    parent.parentNode.insertBefore(commentContainer,uncle);
    buildOpenComment(commentContainer,questionNumber,wrongChoice,'');
}

function setButtonMode (mode,questionNumber,wrongChoice) {
    var commentButton = document.getElementById('comment-button-' + questionNumber + '-' + wrongChoice);
    var editButton = document.getElementById('edit-button-' + questionNumber + '-' + wrongChoice);
    var saveButton = document.getElementById('save-button-' + questionNumber + '-' + wrongChoice);
    if (mode === 'edit') {
        commentButton.setAttribute('style', 'display:none');
        editButton.setAttribute('style', 'display:inline');
        saveButton.setAttribute('style', 'display:none');
    } else if (mode === 'save') {
        commentButton.setAttribute('style', 'display:none');
        editButton.setAttribute('style', 'display:none');
        saveButton.setAttribute('style', 'display:inline');
    } else {
        commentButton.setAttribute('style', 'display:inline');
        editButton.setAttribute('style', 'display:none');
        saveButton.setAttribute('style', 'display:none');
    }
};

function writeComment (questionNumber,wrongChoice,comment) {
    var commenterID = getParameterByName('commenter');
    var classID = getParameterByName('classid');
    var quizNumber = getParameterByName('quizno');
    if (comment) {
        comment = comment.replace(/^\s+/,'').replace(/\s+$/,'');
    }
    var ignoreStr = apiRequest(
        '/?commenter='
            + commenterID
            +'&page=quiz&cmd=writeonecomment'
            + '&classid='
            + classID
            + '&quizno='
            + quizNumber
        , {
            questionno:questionNumber,
            wrongchoice: wrongChoice,
            comment: comment
        }
    );
}
<|endoftext|>