<commit_before>$(function() {
	$(".minifilepreview").click(function() {
		$(this).siblings(".minifilepreview").removeClass('enabled');
		$(this).addClass('enabled');
		$("#filetorollinganalyze").val($(this).prop('id'));
	});

	$("#radioratio").click(function() {
		var timeToToggle = 150;
		$(".rollingsearchwordoptdiv").fadeIn(timeToToggle);
		$(".rollingsearchwordoptdiv").css('display', 'inline');
	});
	$("#radioaverage").click(function() {
		var timeToToggle = 150;
		$(".rollingsearchwordoptdiv").fadeOut(timeToToggle);
	});

	$("#radioinputletter").click(function() {
		var oldVal = $(".rollinginput").val();
		$(".rollinginput").val(oldVal.slice(0,1));
	});

	$("#radioinputword").click(function() {
		if ($("#windowletter").prop('checked')) {
			$("#windowword").click();
		}
	});

	$("#radiowindowletter").click(function() {
		if ($("#inputword").prop('checked')) {
			$("#rwasubmiterrormessage3").show().fadeOut(2500, "easeInOutCubic");
			return false;
		}
	});

	$(".rollinginput").keyup(function(evt) {
		var theEvent = evt || window.event;
		var key = theEvent.keyCode || theEvent.which;
		if (key != 8) { // 8 is backspace
			if ($(this).val().length > 1 && $("#inputletter").prop('checked')) {
				$(this).val($(this).val().slice(0,1));
			}
		}
	});
	$("#rollingwindowsize").keypress(function(evt) {
		var theEvent = evt || window.event;
		var key = theEvent.keyCode || theEvent.which;
		if (key != 8) { // 8 is backspace
			key = String.fromCharCode( key );
			var regex = /[0-9]|\./;
			if( !regex.test(key) ) {
				theEvent.returnValue = false;
				if(theEvent.preventDefault) theEvent.preventDefault();
			}
		}
	});

	$("form").submit(function() {
		var empty = $("input").filter(function() {
			return this.value == '' && (this.type == 'text' || this.type == 'number');
		});
		numEmpty = empty.length;
		if ($(".minifilepreview.enabled").length < 1) {
			$("#rwasubmiterrormessage2").show().fadeOut(2500, "easeInOutCubic");
			return false;
		}
		else if (numEmpty > 0 && !(numEmpty == 1 && empty[0].id == 'rollingsearchwordopt' && $("#rollingaverage").prop('checked')) ) {
			$("#rwasubmiterrormessage1").show().fadeOut(2500, "easeInOutCubic");
			return false;
		}
	});

	function makeRWAGraph() {
		if ($("#rwagraphdiv").text() == 'True') {

			//
			function brushed() {
  				x.domain(brush.empty() ? x2.domain() : brush.extent());
  				focus.select(".line").attr("d", line);
  				focus.select(".x.axis").call(xAxis);
  				focus.selectAll(".dot")
  					.attr("cx", function(d) {return x(d[0]);})
      				.attr("cy", function(d) {return y(d[1]);});
  				};

  			// redraw on zoom
			function redraw() {
				focus.select(".x.axis").call(xAxis);
				focus.select(".y.axis").call(yAxis);
				focus.select(".line")
					.attr("class", "line")
					.attr("d", line);
				focus.selectAll(".dot")
      				.attr("class", "dot")
      				.attr("r", 1.5)
      				.attr("cx", function(d) {return x(d[0]);})
      			 	.attr("cy", function(d) {return y(d[1]);});
      				}

			$("#rwagraphdiv").removeClass('hidden');
			$("#rwagraphdiv").text('');

			// size of the graph variables
			var margin = {top: 20, right: 20, bottom: 180, left: 70},
				margin2 = {top: 520, right: 20, bottom: 20, left: 70},
				width = 940 - margin.left - margin.right,
				height = 640 - margin.top - margin.bottom,
				height2 = 640 - margin2.top - margin2.bottom;

			// scales your x-axis
			var x = d3.scale.linear()
				.range([0, width])
				.domain(d3.extent(dataArray, function(d) { return d[0] }));

			var x2 = d3.scale.linear()
				.range([0, width])
				.domain(d3.extent(dataArray, function(d) { return d[0] }));

			// essentially doing the same thing
			var yExtent = d3.extent(dataArray, function(d) { return d[1] });
			yExtent[0] = yExtent[0] * 0.9;
			yExtent[1] = yExtent[1] * 1.1;

			var y2Extent = d3.extent(dataArray, function(d) { return d[1] });
			y2Extent[0] = y2Extent[0] * 0.9;
			y2Extent[1] = y2Extent[1] * 1.1;

			var y = d3.scale.linear()
				.range([height, 0])
				.domain(yExtent);

			var y2 = d3.scale.linear()
				.range([100, 0])
				.domain(y2Extent);

			//brush
			var brush = d3.svg.brush()
    			.x(x2)
    			.on("brush", brushed);

    		//zoom
    		var zoom = d3.behavior.zoom()
				.x(x)
				.scaleExtent([1, Number.POSITIVE_INFINITY])
				.on("zoom", redraw);

			/////////////////////////////////////////////////////////////

			// creates an svg
			svg = d3.select('#rwagraphdiv')
				.append("svg:svg")
					.attr('width', width + margin.left + margin.right)
					.attr('height', height + margin.top + margin.bottom + 30)

			var focus = svg.append("g")
					.attr("class", "focus")
					.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
					.call(zoom);

			// adds a rectangle to our svg
			focus.append("svg:rect")
				.attr("width", width)
				.attr("height", height)
				.attr("class", "plot");

			// creates a variable x axis
			var xAxis = d3.svg.axis()
				.scale(x)
				.orient("bottom")
				.ticks(5);

			// adds our xAxis to our svg g (group of elements)
			focus.append("svg:g")
				.attr("class", "x axis")
				.attr("transform", "translate(0, " + height + ")")
				.call(xAxis);

			// does the same thing with y axis
			var yAxis = d3.svg.axis()
				.scale(y)
				.orient("left")
				.ticks(10);

			focus.append("g")
				.attr("class", "y axis")
				.call(yAxis);

			// y axis label
			focus.append("text")
    			.attr("class", "y label")
    			.attr("class", "label")
    			.attr("text-anchor", "end")
    			.attr("x", -180)
    			.attr("y", -70)
    			.attr("dy", ".75em")
    			.attr("transform", "rotate(-90)")
    			.text(yAxisLabel);

      		// creates a variable clip which holds the clipPath
      		var clip = focus.append("svg:clipPath")
				.attr("id", "clip")
				.append("svg:rect")
				.attr("x", 0)
				.attr("y", 0)
				.attr("width", width)
				.attr("height", height);

			// we create a variable called ChartBody
			var chartBody = focus.append("g")
				.attr("class", "chartBody")
				.attr("clip-path", "url(#clip)")
				.on("mousemove", function() {
					var infobox = d3.select(".infobox");
					var coord = [0, 0];
					coord = d3.mouse(this)
						infobox.style("left", coord[0] + 15 + "px");
						infobox.style("top", coord[1] + "px");
					});

			// creates scatterplot overlay for line graph and adds browser automatic tooltip for begining of each window
			var dots = focus.append("g").attr("class", "dotgroup").selectAll(".dot")
      			.data(dataArray)
    		    .enter()
    		    .append("circle")
      			.attr("class", "dot")
      			.attr("r", 1.5)
      			.attr("cx", function(d) {return x(d[0]);})
      			.attr("cy", function(d) {return y(d[1]);})
      			.on("mouseover", function(d) {
					d3.select(this)
						.style("fill", "#0068af")
						.attr("r", 3);
					d3.select(".infobox")
						.style("display", "block");
					d3.select("p")
						.text(function() {
							return "(" + d[0] + ", " + d[1] + ")";
						});
					})
      			.on("mousemove", function() {
					var infobox = d3.select(".infobox");
					var coord = [0, 0];
					coord = d3.mouse(this);
						infobox.style("left", coord[0] + 15 + "px");
						infobox.style("top", coord[1] + 300 + "px");
					})
      			.on("mouseout", function() {
					d3.select(this)
						.style("fill", "gray")
						.attr("r", 1.5);
					d3.select(".infobox")
						.style("display", "none");
						})
      			.attr("clip-path", "url(#clip)");

			// specifies the path data
			var line = d3.svg.line()
				.x(function(d) { return x(d[0]); })
				.y(function(d) { return y(d[1]); });

      		// adds a path to our ChartBody
      		chartBody.append("svg:path")
				.datum(dataArray)
				.attr("class", "line")
				.attr("d", line);

			////////////////////////////////////////////////////////////

			var context = svg.append("g")
					.attr("class", "context")
					.attr("width", width)
					.attr("height", 100)
					.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");

			context.append("rect")
				.attr("width", width)
				.attr("height", 100)
				.attr("class", "plot");

			// creates a variable x axis
			var xAxis2 = d3.svg.axis()
				.scale(x2)
				.orient("bottom")
				.ticks(5);

			// adds our xAxis to our svg g (group of elements)
			context.append("svg:g")
				.attr("class", "x2 axis")
				.attr("transform", "translate(0, " + height2 + ")")
				.call(xAxis2);

			// x-axis label
			context.append("text")
    			.attr("class", "x label")
    			.attr("class", "label")
    			.attr("text-anchor", "middle")
    			.attr("x", 420)
    			.attr("y", height2+margin2.bottom+15)
    			.text(xAxisLabel);

			// brush ability
			context.append("g")
      			.attr("class", "x brush")
      			.call(brush)
    			.selectAll("rect")
      			.attr("y", -6)
      			.attr("height", height2 + 7);

			// creates a variable clip which holds the clipPath
      		var clipTwo = context.append("svg:clipPath")
				.attr("id", "clip2")
				.append("svg:rect")
				.attr("x", 0)
				.attr("y", 0)
				.attr("width", width)
				.attr("height", 100);

			// we create a variable called ChartBody
			var chartBody2 = context.append("g")
				.attr("class", "chartBody2")
				.attr("clip-path", "url(#clip2)");

			// specifies the path data
			var line2 = d3.svg.line()
				.x(function(d) { return x2(d[0]); })
				.y(function(d) { return y2(d[1]); });

      		// adds a path to our ChartBody
      		chartBody2.append("svg:path")
				.datum(dataArray)
				.attr("class", "line")
				.attr("d", line2);

			//////////////////////////////////////////////////////////

		}
	}

	makeRWAGraph();
});<commit_msg>fixed rwa for firefox<commit_after>$(function() {
	$(".minifilepreview").click(function() {
		$(this).siblings(".minifilepreview").removeClass('enabled');
		$(this).addClass('enabled');
		$("#filetorollinganalyze").val($(this).prop('id'));
	});

	$("#radioratio").click(function() {
		var timeToToggle = 150;
		$(".rollingsearchwordoptdiv").fadeIn(timeToToggle);
		$(".rollingsearchwordoptdiv").css('display', 'inline');
	});
	$("#radioaverage").click(function() {
		var timeToToggle = 150;
		$(".rollingsearchwordoptdiv").fadeOut(timeToToggle);
	});

	$("#radioinputletter").click(function() {
		var oldVal = $(".rollinginput").val();
		$(".rollinginput").val(oldVal.slice(0,1));
	});

	$("#radioinputword").click(function() {
		if ($("#windowletter").prop('checked')) {
			$("#windowword").click();
		}
	});

	$("#radiowindowletter").click(function() {
		if ($("#inputword").prop('checked')) {
			$("#rwasubmiterrormessage3").show().fadeOut(2500, "easeInOutCubic");
			return false;
		}
	});

	$(".rollinginput").keyup(function(evt) {
		var theEvent = evt || window.event;
		var key = theEvent.keyCode || theEvent.which;
		if (key != 8) { // 8 is backspace
			if ($(this).val().length > 1 && $("#inputletter").prop('checked')) {
				$(this).val($(this).val().slice(0,1));
			}
		}
	});
	$("#rollingwindowsize").keypress(function(evt) {
		var theEvent = evt || window.event;
		var key = theEvent.keyCode || theEvent.which;
		if (key != 8) { // 8 is backspace
			key = String.fromCharCode( key );
			var regex = /[0-9]|\./;
			if( !regex.test(key) ) {
				theEvent.returnValue = false;
				if(theEvent.preventDefault) theEvent.preventDefault();
			}
		}
	});

	$("form").submit(function() {
		var empty = $("input").filter(function() {
			return this.value == '' && (this.type == 'text' || this.type == 'number');
		});
		numEmpty = empty.length;
		if ($(".minifilepreview.enabled").length < 1) {
			$("#rwasubmiterrormessage2").show().fadeOut(2500, "easeInOutCubic");
			return false;
		}
		else if (numEmpty > 0 && !(numEmpty == 1 && empty[0].id == 'rollingsearchwordopt' && $("#rollingaverage").prop('checked')) ) {
			$("#rwasubmiterrormessage1").show().fadeOut(2500, "easeInOutCubic");
			return false;
		}
	});

	function makeRWAGraph() {
		if ($("#rwagraphdiv").text() == 'True') {
			$("#rwagraphdiv").removeClass('hidden');
			$("#rwagraphdiv").text('');

			// size of the graph variables
			var margin = {top: 20, right: 20, bottom: 180, left: 70},
				margin2 = {top: 520, right: 20, bottom: 20, left: 70},
				width = 940 - margin.left - margin.right,
				height = 640 - margin.top - margin.bottom,
				height2 = 640 - margin2.top - margin2.bottom;

			// scales your x-axis
			var x = d3.scale.linear()
				.range([0, width])
				.domain(d3.extent(dataArray, function(d) { return d[0] }));

			var x2 = d3.scale.linear()
				.range([0, width])
				.domain(d3.extent(dataArray, function(d) { return d[0] }));

			// essentially doing the same thing
			var yExtent = d3.extent(dataArray, function(d) { return d[1] });
			yExtent[0] = yExtent[0] * 0.9;
			yExtent[1] = yExtent[1] * 1.1;

			var y2Extent = d3.extent(dataArray, function(d) { return d[1] });
			y2Extent[0] = y2Extent[0] * 0.9;
			y2Extent[1] = y2Extent[1] * 1.1;

			var y = d3.scale.linear()
				.range([height, 0])
				.domain(yExtent);

			var y2 = d3.scale.linear()
				.range([100, 0])
				.domain(y2Extent);

			// brushed on brush
			function brushed() {
  				x.domain(brush.empty() ? x2.domain() : brush.extent());
  				focus.select(".line").attr("d", line);
  				focus.select(".x.axis").call(xAxis);
  				focus.selectAll(".dot")
  					.attr("cx", function(d) {return x(d[0]);})
      				.attr("cy", function(d) {return y(d[1]);});
  				};

			//brush
			var brush = d3.svg.brush()
    			.x(x2)
    			.on("brush", brushed);

    		// redraw on zoom
			function redraw() {
				focus.select(".x.axis").call(xAxis);
				focus.select(".y.axis").call(yAxis);
				focus.select(".line")
					.attr("class", "line")
					.attr("d", line);
				focus.selectAll(".dot")
      				.attr("class", "dot")
      				.attr("r", 1.5)
      				.attr("cx", function(d) {return x(d[0]);})
      			 	.attr("cy", function(d) {return y(d[1]);});
      			};

    		//zoom
    		var zoom = d3.behavior.zoom()
				.x(x)
				.scaleExtent([1, Number.POSITIVE_INFINITY])
				.on("zoom", redraw);

			/////////////////////////////////////////////////////////////

			// creates an svg
			svg = d3.select('#rwagraphdiv')
				.append("svg:svg")
					.attr('width', width + margin.left + margin.right)
					.attr('height', height + margin.top + margin.bottom + 30)

			var focus = svg.append("g")
					.attr("class", "focus")
					.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
					.call(zoom);

			// adds a rectangle to our svg
			focus.append("svg:rect")
				.attr("width", width)
				.attr("height", height)
				.attr("class", "plot");

			// creates a variable x axis
			var xAxis = d3.svg.axis()
				.scale(x)
				.orient("bottom")
				.ticks(5);

			// adds our xAxis to our svg g (group of elements)
			focus.append("svg:g")
				.attr("class", "x axis")
				.attr("transform", "translate(0, " + height + ")")
				.call(xAxis);

			// does the same thing with y axis
			var yAxis = d3.svg.axis()
				.scale(y)
				.orient("left")
				.ticks(10);

			focus.append("g")
				.attr("class", "y axis")
				.call(yAxis);

			// y axis label
			focus.append("text")
    			.attr("class", "y label")
    			.attr("class", "label")
    			.attr("text-anchor", "end")
    			.attr("x", -180)
    			.attr("y", -70)
    			.attr("dy", ".75em")
    			.attr("transform", "rotate(-90)")
    			.text(yAxisLabel);

      		// creates a variable clip which holds the clipPath
      		var clip = focus.append("svg:clipPath")
				.attr("id", "clip")
				.append("svg:rect")
				.attr("x", 0)
				.attr("y", 0)
				.attr("width", width)
				.attr("height", height);

			// we create a variable called ChartBody
			var chartBody = focus.append("g")
				.attr("class", "chartBody")
				.attr("clip-path", "url(#clip)")
				.on("mousemove", function() {
					var infobox = d3.select(".infobox");
					var coord = [0, 0];
					coord = d3.mouse(this)
						infobox.style("left", coord[0] + 15 + "px");
						infobox.style("top", coord[1] + "px");
					});

			// creates scatterplot overlay for line graph and adds browser automatic tooltip for begining of each window
			var dots = focus.append("g").attr("class", "dotgroup").selectAll(".dot")
      			.data(dataArray)
    		    .enter()
    		    .append("circle")
      			.attr("class", "dot")
      			.attr("r", 1.5)
      			.attr("cx", function(d) {return x(d[0]);})
      			.attr("cy", function(d) {return y(d[1]);})
      			.on("mouseover", function(d) {
					d3.select(this)
						.style("fill", "#0068af")
						.attr("r", 3);
					d3.select(".infobox")
						.style("display", "block");
					d3.select("p")
						.text(function() {
							return "(" + d[0] + ", " + d[1] + ")";
						});
					})
      			.on("mousemove", function() {
					var infobox = d3.select(".infobox");
					var coord = [0, 0];
					coord = d3.mouse(this);
						infobox.style("left", coord[0] + 15 + "px");
						infobox.style("top", coord[1] + 300 + "px");
					})
      			.on("mouseout", function() {
					d3.select(this)
						.style("fill", "gray")
						.attr("r", 1.5);
					d3.select(".infobox")
						.style("display", "none");
						})
      			.attr("clip-path", "url(#clip)");

			// specifies the path data
			var line = d3.svg.line()
				.x(function(d) { return x(d[0]); })
				.y(function(d) { return y(d[1]); });

      		// adds a path to our ChartBody
      		chartBody.append("svg:path")
				.datum(dataArray)
				.attr("class", "line")
				.attr("d", line);

			////////////////////////////////////////////////////////////

			var context = svg.append("g")
					.attr("class", "context")
					.attr("width", width)
					.attr("height", 100)
					.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");

			context.append("rect")
				.attr("width", width)
				.attr("height", 100)
				.attr("class", "plot");

			// creates a variable x axis
			var xAxis2 = d3.svg.axis()
				.scale(x2)
				.orient("bottom")
				.ticks(5);

			// adds our xAxis to our svg g (group of elements)
			context.append("svg:g")
				.attr("class", "x2 axis")
				.attr("transform", "translate(0, " + height2 + ")")
				.call(xAxis2);

			// x-axis label
			context.append("text")
    			.attr("class", "x label")
    			.attr("class", "label")
    			.attr("text-anchor", "middle")
    			.attr("x", 420)
    			.attr("y", height2+margin2.bottom+15)
    			.text(xAxisLabel);

			// brush ability
			context.append("g")
      			.attr("class", "x brush")
      			.call(brush)
    			.selectAll("rect")
      			.attr("y", -6)
      			.attr("height", height2 + 7);

			// creates a variable clip which holds the clipPath
      		var clipTwo = context.append("svg:clipPath")
				.attr("id", "clip2")
				.append("svg:rect")
				.attr("x", 0)
				.attr("y", 0)
				.attr("width", width)
				.attr("height", 100);

			// we create a variable called ChartBody
			var chartBody2 = context.append("g")
				.attr("class", "chartBody2")
				.attr("clip-path", "url(#clip2)");

			// specifies the path data
			var line2 = d3.svg.line()
				.x(function(d) { return x2(d[0]); })
				.y(function(d) { return y2(d[1]); });

      		// adds a path to our ChartBody
      		chartBody2.append("svg:path")
				.datum(dataArray)
				.attr("class", "line")
				.attr("d", line2);

			//////////////////////////////////////////////////////////

		}
	}

	makeRWAGraph();
});<|endoftext|>


<commit_before>'use strict'

import Circuit from './circuit'

import {createBody, parseBody} from './networkMessages'

// Utility for testing
window.WebSocket = class WebSocket {
  constructor (url) {
    this.url = url

    this.sendMessages = []
  }

  send (buffer) {
    this.sendMessages.push(buffer)
    expect(buffer).toBeTruthy()
  }

  onopen () {}

  onmessage (buffer) {}

  getSendMessages (index = this.sendMessages.length - 1) {
    return this.sendMessages[index]
  }
}

let sequenceNumberForTests = 0
function createTestMessage (reliable, resend, hasAcks) {
  const body = createBody('TestMessage', {
    TestBlock1: [
      {
        Test1: 0
      }
    ],
    NeighborBlock: [
      {
        Test0: 0,
        Test1: 1,
        Test2: 2
      },
      {
        Test0: 3,
        Test1: 4,
        Test2: 5
      },
      {
        Test0: 6,
        Test1: 7,
        Test2: 8
      },
      {
        Test0: 9,
        Test1: 10,
        Test2: 11
      }
    ]
  })
  const flags = (reliable ? 64 : 0) + (resend ? 32 : 0) + (hasAcks ? 16 : 0)
  const headBuffer = Buffer.from([127, 0, 0, 1, 33, 0, flags, 0, 0, 0, 0, 0])
  headBuffer.writeUInt32BE(sequenceNumberForTests, 7)
  sequenceNumberForTests += 1
  const messageBuffer = Buffer.concat([headBuffer, body.buffer])

  return messageBuffer
}

const circuit = new Circuit('127.0.0.1', 8080, 123456)

test('it should create an instance', () => {
  expect(circuit instanceof Circuit).toBe(true)
  expect(circuit.circuitCode).toBe(123456)
  expect(circuit.ip).toBe('127.0.0.1')
  expect(circuit.ipArray).toEqual([127, 0, 0, 1])
  expect(circuit.port).toBe(8080)
  expect(circuit.websocket.url).toBe('ws://localhost:8080/andromeda-bridge')

  expect(circuit.websocketIsOpen).toBe(false)
})

test('circuit should save messages until the WebSocket is open and send if open', () => {
  circuit.send('PacketAck', {
    Packets: [
      {
        ID: 0
      }
    ]
  })

  expect(circuit.websocket.sendMessages.length).toBe(0)
  expect(circuit.cachedMessages.length).toBe(1)

  circuit.websocket.onopen()

  expect(circuit.websocketIsOpen).toBe(true)
  expect(circuit.cachedMessages.length).toBe(0)
  expect(circuit.websocket.sendMessages.length).toBe(1)

  circuit.send('PacketAck', {
    Packets: [
      {
        ID: 0
      }
    ]
  })

  expect(circuit.cachedMessages.length).toBe(0)
  expect(circuit.websocket.sendMessages.length).toBe(2)
})

test('parse a received package', () => {
  const messageBuffer = createTestMessage(false, false, false)

  const received = []
  circuit.on('packetReceived', message => {
    expect(message.body).toBeTruthy()
    received.push(message)
  })

  circuit.websocket.onmessage({data: messageBuffer})

  expect(received.length).toBe(1)
  expect(circuit.senderSequenceNumber).toBe(0)
  circuit.removeAllListeners()
})

test('save sender sequence number of reliable packages as ack', () => {
  const message1 = createTestMessage(true, false, false)
  const message2 = createTestMessage(true, false, false)

  const websocket = circuit.websocket
  websocket.onmessage({data: message1})
  websocket.onmessage({data: message2})

  expect(circuit.simAcks.length).toBe(2)
  expect(circuit.senderSequenceNumber).toBe(sequenceNumberForTests - 1)
})

test('send ack at end of package', () => {
  circuit.send('CompletePingCheck', {
    PingID: [
      {
        PingID: 0
      }
    ]
  })

  const sendMessages = circuit.websocket.sendMessages
  const last = sendMessages[sendMessages.length - 1]

  // Check if there are the correct number of acks
  expect(circuit.simAcks.length).toBe(2)
  expect(last.byteLength).toBe(23)
  expect((last.readUInt8(6) | 0x10) > 0).toBe(true)

  // Check if the acks are correct
  const offset = last.length - 1
  expect(last.readUInt8(offset)).toBe(2)
  expect([last.readUInt32LE(offset - 4), last.readUInt32LE(offset - 8)].sort()).toEqual([
    sequenceNumberForTests - 2,
    sequenceNumberForTests - 1
  ])
})

test('circuit should send after 200ms a PacketAck', done => {
  setTimeout(() => {
    // could be CompletePingCheck
    const ackMessage1 = circuit.websocket.sendMessages[circuit.websocket.sendMessages.length - 2]
    const ackMessage2 = circuit.websocket.sendMessages[circuit.websocket.sendMessages.length - 1]
    const parsedAckMessageA = parseBody(ackMessage1.slice(12))
    const parsedAckMessageB = parseBody(ackMessage2.slice(12))
    const parsedAckMessage = parsedAckMessageA.name === 'PacketAck'
      ? parsedAckMessageA
      : parsedAckMessageB

    expect(parsedAckMessage).toBeTruthy()
    expect(parsedAckMessage.Packets.data[0].ID.value).toBe(0)

    done()
  }, 250)
})

test('circuit should resend a package after 500ms', done => {
  setTimeout(() => {
    const last = circuit.websocket.getSendMessages()

    expect(last.readUInt8(6) | 32).toBeTruthy()

    done()
  }, 900)
})
<commit_msg>test(circuit): test viewer acks<commit_after>'use strict'

import Circuit from './circuit'

import {createBody, parseBody} from './networkMessages'

// Utility for testing
window.WebSocket = class WebSocket {
  constructor (url) {
    this.url = url

    this.sendMessages = []
  }

  send (buffer) {
    this.sendMessages.push(buffer)
    expect(buffer).toBeTruthy()
  }

  onopen () {}

  onmessage (buffer) {}

  getSendMessages (index = this.sendMessages.length - 1) {
    return this.sendMessages[index]
  }
}

let sequenceNumberForTests = 0
function createTestMessage (reliable, resend, hasAcks) {
  const body = createBody('TestMessage', {
    TestBlock1: [
      {
        Test1: 0
      }
    ],
    NeighborBlock: [
      {
        Test0: 0,
        Test1: 1,
        Test2: 2
      },
      {
        Test0: 3,
        Test1: 4,
        Test2: 5
      },
      {
        Test0: 6,
        Test1: 7,
        Test2: 8
      },
      {
        Test0: 9,
        Test1: 10,
        Test2: 11
      }
    ]
  })
  const headBuffer = createTestHeader(reliable, resend, hasAcks)
  const messageBuffer = Buffer.concat([headBuffer, body.buffer])

  return messageBuffer
}

function createTestHeader (reliable, resend, hasAcks) {
  const flags = (reliable ? 64 : 0) + (resend ? 32 : 0) + (hasAcks ? 16 : 0)
  const headBuffer = Buffer.from([127, 0, 0, 1, 33, 0, flags, 0, 0, 0, 0, 0])
  headBuffer.writeUInt32BE(sequenceNumberForTests, 7)
  sequenceNumberForTests += 1
  return headBuffer
}

const circuit = new Circuit('127.0.0.1', 8080, 123456)

test('it should create an instance', () => {
  expect(circuit instanceof Circuit).toBe(true)
  expect(circuit.circuitCode).toBe(123456)
  expect(circuit.ip).toBe('127.0.0.1')
  expect(circuit.ipArray).toEqual([127, 0, 0, 1])
  expect(circuit.port).toBe(8080)
  expect(circuit.websocket.url).toBe('ws://localhost:8080/andromeda-bridge')

  expect(circuit.websocketIsOpen).toBe(false)
})

test('circuit should save messages until the WebSocket is open and send if open', () => {
  circuit.send('PacketAck', {
    Packets: [
      {
        ID: 0
      }
    ]
  })

  expect(circuit.websocket.sendMessages.length).toBe(0)
  expect(circuit.cachedMessages.length).toBe(1)

  circuit.websocket.onopen()

  expect(circuit.websocketIsOpen).toBe(true)
  expect(circuit.cachedMessages.length).toBe(0)
  expect(circuit.websocket.sendMessages.length).toBe(1)

  circuit.send('PacketAck', {
    Packets: [
      {
        ID: 0
      }
    ]
  })

  expect(circuit.cachedMessages.length).toBe(0)
  expect(circuit.websocket.sendMessages.length).toBe(2)
})

test('parse a received package', () => {
  const messageBuffer = createTestMessage(false, false, false)

  const received = []
  circuit.on('packetReceived', message => {
    expect(message.body).toBeTruthy()
    received.push(message)
  })

  circuit.websocket.onmessage({data: messageBuffer})

  expect(received.length).toBe(1)
  expect(circuit.senderSequenceNumber).toBe(0)
  circuit.removeAllListeners()
})

test('save sender sequence number of reliable packages as ack', () => {
  const message1 = createTestMessage(true, false, false)
  const message2 = createTestMessage(true, false, false)

  const websocket = circuit.websocket
  websocket.onmessage({data: message1})
  websocket.onmessage({data: message2})

  expect(circuit.simAcks.length).toBe(2)
  expect(circuit.senderSequenceNumber).toBe(sequenceNumberForTests - 1)
})

test('send ack at end of package', () => {
  circuit.send('CompletePingCheck', {
    PingID: [
      {
        PingID: 0
      }
    ]
  })

  const sendMessages = circuit.websocket.sendMessages
  const last = sendMessages[sendMessages.length - 1]

  // Check if there are the correct number of acks
  expect(circuit.simAcks.length).toBe(2)
  expect(last.byteLength).toBe(23)
  expect((last.readUInt8(6) | 0x10) > 0).toBe(true)

  // Check if the acks are correct
  const offset = last.length - 1
  expect(last.readUInt8(offset)).toBe(2)
  expect([last.readUInt32LE(offset - 4), last.readUInt32LE(offset - 8)].sort()).toEqual([
    sequenceNumberForTests - 2,
    sequenceNumberForTests - 1
  ])
})

test('circuit should send after 200ms a PacketAck', done => {
  setTimeout(() => {
    // could be CompletePingCheck
    const ackMessage1 = circuit.websocket.sendMessages[circuit.websocket.sendMessages.length - 2]
    const ackMessage2 = circuit.websocket.sendMessages[circuit.websocket.sendMessages.length - 1]
    const parsedAckMessageA = parseBody(ackMessage1.slice(12))
    const parsedAckMessageB = parseBody(ackMessage2.slice(12))
    const parsedAckMessage = parsedAckMessageA.name === 'PacketAck'
      ? parsedAckMessageA
      : parsedAckMessageB

    expect(parsedAckMessage).toBeTruthy()
    expect(parsedAckMessage.Packets.data[0].ID.value).toBe(0)

    done()
  }, 250)
})

test('circuit should resend a package after 500ms', done => {
  setTimeout(() => {
    const last = circuit.websocket.getSendMessages()

    expect(last.readUInt8(6) | 32).toBeTruthy()

    done()
  }, 400)
})

describe('circuit should remove acks that the server has send back', () => {
  test('with the PacketAck', () => {
    const acksBody = createBody('PacketAck', {
      Packets: circuit.viewerAcks.map(ack => ({ID: ack.sequenceNumber}))
    })
    const header = createTestHeader(false, false, false)
    const message = Buffer.concat([header, acksBody.buffer])
    circuit.websocket.onmessage({data: message})

    expect(circuit.viewerAcks.length).toBe(0)
  })

  test('with acks at the end of a packet', () => {
    // create an ack
    circuit.send('OpenCircuit', {
      CircuitInfo: [
        {
          IP: '0.0.0.0',
          Port: 13
        }
      ]
    }, true)
    expect(circuit.viewerAcks.length).toBe(1)

    // test at the end of packet
    const messagePart1 = createTestMessage(false, false, true)

    // create Ack at end
    const acks = Buffer.from([0, 0, 0, 0, 1])
    acks.writeUInt32LE(circuit.viewerAcks[0].sequenceNumber, 0)
    const message2 = Buffer.concat([messagePart1, acks])
    circuit.websocket.onmessage({data: message2})

    expect(circuit.viewerAcks.length).toBe(0)
  })
})
<|endoftext|>


<commit_before>'use strict';

function classDirective(name, selector) {
  name = 'ngClass' + name;
  return function() {
    return {
      restrict: 'AC',
      link: function(scope, element, attr) {
        var oldVal = undefined;

        scope.$watch(attr[name], ngClassWatchAction, true);

        attr.$observe('class', function(value) {
          ngClassWatchAction(scope.$eval(attr[name]));
        });

        if (name !== 'ngClass') {
          scope.$watch('$index', function($index, old$index) {
            var mod = $index & 1;
            if (mod !== old$index & 1) {
              if (mod === selector) {
                addClass(scope.$eval(attr[name]));
              } else {
                removeClass(scope.$eval(attr[name]));
              }
            }
          });
        }

        function ngClassWatchAction(newVal) {
          if (selector === true || scope.$index % 2 === selector) {
            if (oldVal && !equals(newVal,oldVal)) {
              removeClass(oldVal);
            }
            addClass(newVal);
          }
          oldVal = copy(newVal);
        }

        function removeClass(classVal) {
          attr.$removeClass(flattenClasses(classVal));
        }

        function addClass(classVal) {
          attr.$addClass(flattenClasses(classVal));
        }

        function flattenClasses(classVal) {
          if(isArray(classVal)) {
            return classVal.join(' ');
          } else if (isObject(classVal)) {
            var classes = [], i = 0;
            forEach(classVal, function(v, k) {
              if (v) {
                classes.push(k);
              }
            });
            return classes.join(' ');
          }

          return classVal;
        };
      }
    };
  };
}

/**
 * @ngdoc directive
 * @name ng.directive:ngClass
 * @restrict AC
 *
 * @description
 * The `ngClass` allows you to set CSS classes on HTML an element, dynamically, by databinding
 * an expression that represents all classes to be added.
 *
 * The directive won't add duplicate classes if a particular class was already set.
 *
 * When the expression changes, the previously added classes are removed and only then the
 * new classes are added.
 *
 * @animations
 * add - happens just before the class is applied to the element
 * remove - happens just before the class is removed from the element
 *
 * @element ANY
 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
 *   of the evaluation can be a string representing space delimited class
 *   names, an array, or a map of class names to boolean values. In the case of a map, the
 *   names of the properties whose values are truthy will be added as css classes to the
 *   element.
 *
 * @example Example that demostrates basic bindings via ngClass directive.
   <example>
     <file name="index.html">

Map Syntax Example

       <input type="checkbox" ng-model="bold"> bold
       <input type="checkbox" ng-model="strike"> strike
       <input type="checkbox" ng-model="red"> red

Using String Syntax

       <input type="text" ng-model="style" placeholder="Type: bold strike red">

Using Array Syntax

       <input ng-model="style1" placeholder="Type: bold">

       <input ng-model="style2" placeholder="Type: strike">

       <input ng-model="style3" placeholder="Type: red">

     </file>
     <file name="style.css">
       .strike {
         text-decoration: line-through;
       }
       .bold {
           font-weight: bold;
       }
       .red {
           color: red;
       }
     </file>
     <file name="scenario.js">
       it('should let you toggle the class', function() {

         expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/bold/);
         expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/red/);

         input('bold').check();
         expect(element('.doc-example-live p:first').prop('className')).toMatch(/bold/);

         input('red').check();
         expect(element('.doc-example-live p:first').prop('className')).toMatch(/red/);
       });

       it('should let you toggle string example', function() {
         expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('');
         input('style').enter('red');
         expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('red');
       });

       it('array example should have 3 classes', function() {
         expect(element('.doc-example-live p:last').prop('className')).toBe('');
         input('style1').enter('bold');
         input('style2').enter('strike');
         input('style3').enter('red');
         expect(element('.doc-example-live p:last').prop('className')).toBe('bold strike red');
       });
     </file>
   </example>

   ## Animations

   The example below demonstrates how to perform animations using ngClass.

   <example animations="true">
     <file name="index.html">
      <input type="button" value="set" ng-click="myVar='my-class'">
      <input type="button" value="clear" ng-click="myVar=''">

      Sample Text
     </file>
     <file name="style.css">
       .my-class-add, .my-class-remove {
         -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
         -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
         -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
       }

       .my-class,
       .my-class-add.my-class-add-active {
         color: red;
         font-size:3em;
       }

       .my-class-remove.my-class-remove-active {
         font-size:1.0em;
         color:black;
       }
     </file>
     <file name="scenario.js">
       it('should check ng-class', function() {
         expect(element('.doc-example-live span').prop('className')).not().
           toMatch(/my-class/);

         using('.doc-example-live').element(':button:first').click();

         expect(element('.doc-example-live span').prop('className')).
           toMatch(/my-class/);

         using('.doc-example-live').element(':button:last').click();

         expect(element('.doc-example-live span').prop('className')).not().
           toMatch(/my-class/);
       });
     </file>
   </example>

   ## ngClass and pre-existing CSS3 Transitions/Animations
   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
   Therefore, if any CSS3 Transition/Animation styles (outside of ngAnimate) are set on the element, then, if a ngClass animation
   is triggered, the ngClass animation will be skipped so that ngAnimate can allow for the pre-existing transition or animation to
   take over. This restriction allows for ngClass to still work with standard CSS3 Transitions/Animations that are defined
   outside of ngAnimate.
 */
var ngClassDirective = classDirective('', true);

/**
 * @ngdoc directive
 * @name ng.directive:ngClassOdd
 * @restrict AC
 *
 * @description
 * The `ngClassOdd` and `ngClassEven` directives work exactly as
 * {@link ng.directive:ngClass ngClass}, except it works in
 * conjunction with `ngRepeat` and takes affect only on odd (even) rows.
 *
 * This directive can be applied only within a scope of an
 * {@link ng.directive:ngRepeat ngRepeat}.
 *
 * @element ANY
 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
 *   of the evaluation can be a string representing space delimited class names or an array.
 *
 * @example
   <example>
     <file name="index.html">

             {{name}}

     </file>
     <file name="style.css">
       .odd {
         color: red;
       }
       .even {
         color: blue;
       }
     </file>
     <file name="scenario.js">
       it('should check ng-class-odd and ng-class-even', function() {
         expect(element('.doc-example-live li:first span').prop('className')).
           toMatch(/odd/);
         expect(element('.doc-example-live li:last span').prop('className')).
           toMatch(/even/);
       });
     </file>
   </example>
 */
var ngClassOddDirective = classDirective('Odd', 0);

/**
 * @ngdoc directive
 * @name ng.directive:ngClassEven
 * @restrict AC
 *
 * @description
 * The `ngClassOdd` and `ngClassEven` directives work exactly as
 * {@link ng.directive:ngClass ngClass}, except it works in
 * conjunction with `ngRepeat` and takes affect only on odd (even) rows.
 *
 * This directive can be applied only within a scope of an
 * {@link ng.directive:ngRepeat ngRepeat}.
 *
 * @element ANY
 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
 *   result of the evaluation can be a string representing space delimited class names or an array.
 *
 * @example
   <example>
     <file name="index.html">

             {{name}}

     </file>
     <file name="style.css">
       .odd {
         color: red;
       }
       .even {
         color: blue;
       }
     </file>
     <file name="scenario.js">
       it('should check ng-class-odd and ng-class-even', function() {
         expect(element('.doc-example-live li:first span').prop('className')).
           toMatch(/odd/);
         expect(element('.doc-example-live li:last span').prop('className')).
           toMatch(/even/);
       });
     </file>
   </example>
 */
var ngClassEvenDirective = classDirective('Even', 1);
<commit_msg>docs(ngClass): fix grammar<commit_after>'use strict';

function classDirective(name, selector) {
  name = 'ngClass' + name;
  return function() {
    return {
      restrict: 'AC',
      link: function(scope, element, attr) {
        var oldVal = undefined;

        scope.$watch(attr[name], ngClassWatchAction, true);

        attr.$observe('class', function(value) {
          ngClassWatchAction(scope.$eval(attr[name]));
        });

        if (name !== 'ngClass') {
          scope.$watch('$index', function($index, old$index) {
            var mod = $index & 1;
            if (mod !== old$index & 1) {
              if (mod === selector) {
                addClass(scope.$eval(attr[name]));
              } else {
                removeClass(scope.$eval(attr[name]));
              }
            }
          });
        }

        function ngClassWatchAction(newVal) {
          if (selector === true || scope.$index % 2 === selector) {
            if (oldVal && !equals(newVal,oldVal)) {
              removeClass(oldVal);
            }
            addClass(newVal);
          }
          oldVal = copy(newVal);
        }

        function removeClass(classVal) {
          attr.$removeClass(flattenClasses(classVal));
        }

        function addClass(classVal) {
          attr.$addClass(flattenClasses(classVal));
        }

        function flattenClasses(classVal) {
          if(isArray(classVal)) {
            return classVal.join(' ');
          } else if (isObject(classVal)) {
            var classes = [], i = 0;
            forEach(classVal, function(v, k) {
              if (v) {
                classes.push(k);
              }
            });
            return classes.join(' ');
          }

          return classVal;
        };
      }
    };
  };
}

/**
 * @ngdoc directive
 * @name ng.directive:ngClass
 * @restrict AC
 *
 * @description
 * The `ngClass` allows you to set CSS classes on HTML an element, dynamically, by databinding
 * an expression that represents all classes to be added.
 *
 * The directive won't add duplicate classes if a particular class was already set.
 *
 * When the expression changes, the previously added classes are removed and only then the
 * new classes are added.
 *
 * @animations
 * add - happens just before the class is applied to the element
 * remove - happens just before the class is removed from the element
 *
 * @element ANY
 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
 *   of the evaluation can be a string representing space delimited class
 *   names, an array, or a map of class names to boolean values. In the case of a map, the
 *   names of the properties whose values are truthy will be added as css classes to the
 *   element.
 *
 * @example Example that demostrates basic bindings via ngClass directive.
   <example>
     <file name="index.html">

Map Syntax Example

       <input type="checkbox" ng-model="bold"> bold
       <input type="checkbox" ng-model="strike"> strike
       <input type="checkbox" ng-model="red"> red

Using String Syntax

       <input type="text" ng-model="style" placeholder="Type: bold strike red">

Using Array Syntax

       <input ng-model="style1" placeholder="Type: bold">

       <input ng-model="style2" placeholder="Type: strike">

       <input ng-model="style3" placeholder="Type: red">

     </file>
     <file name="style.css">
       .strike {
         text-decoration: line-through;
       }
       .bold {
           font-weight: bold;
       }
       .red {
           color: red;
       }
     </file>
     <file name="scenario.js">
       it('should let you toggle the class', function() {

         expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/bold/);
         expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/red/);

         input('bold').check();
         expect(element('.doc-example-live p:first').prop('className')).toMatch(/bold/);

         input('red').check();
         expect(element('.doc-example-live p:first').prop('className')).toMatch(/red/);
       });

       it('should let you toggle string example', function() {
         expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('');
         input('style').enter('red');
         expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('red');
       });

       it('array example should have 3 classes', function() {
         expect(element('.doc-example-live p:last').prop('className')).toBe('');
         input('style1').enter('bold');
         input('style2').enter('strike');
         input('style3').enter('red');
         expect(element('.doc-example-live p:last').prop('className')).toBe('bold strike red');
       });
     </file>
   </example>

   ## Animations

   The example below demonstrates how to perform animations using ngClass.

   <example animations="true">
     <file name="index.html">
      <input type="button" value="set" ng-click="myVar='my-class'">
      <input type="button" value="clear" ng-click="myVar=''">

      Sample Text
     </file>
     <file name="style.css">
       .my-class-add, .my-class-remove {
         -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
         -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
         -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
       }

       .my-class,
       .my-class-add.my-class-add-active {
         color: red;
         font-size:3em;
       }

       .my-class-remove.my-class-remove-active {
         font-size:1.0em;
         color:black;
       }
     </file>
     <file name="scenario.js">
       it('should check ng-class', function() {
         expect(element('.doc-example-live span').prop('className')).not().
           toMatch(/my-class/);

         using('.doc-example-live').element(':button:first').click();

         expect(element('.doc-example-live span').prop('className')).
           toMatch(/my-class/);

         using('.doc-example-live').element(':button:last').click();

         expect(element('.doc-example-live span').prop('className')).not().
           toMatch(/my-class/);
       });
     </file>
   </example>

   ## ngClass and pre-existing CSS3 Transitions/Animations
   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
   Therefore, if any CSS3 Transition/Animation styles (outside of ngAnimate) are set on the element, then, if a ngClass animation
   is triggered, the ngClass animation will be skipped so that ngAnimate can allow for the pre-existing transition or animation to
   take over. This restriction allows for ngClass to still work with standard CSS3 Transitions/Animations that are defined
   outside of ngAnimate.
 */
var ngClassDirective = classDirective('', true);

/**
 * @ngdoc directive
 * @name ng.directive:ngClassOdd
 * @restrict AC
 *
 * @description
 * The `ngClassOdd` and `ngClassEven` directives work exactly as
 * {@link ng.directive:ngClass ngClass}, except they work in
 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
 *
 * This directive can be applied only within the scope of an
 * {@link ng.directive:ngRepeat ngRepeat}.
 *
 * @element ANY
 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
 *   of the evaluation can be a string representing space delimited class names or an array.
 *
 * @example
   <example>
     <file name="index.html">

             {{name}}

     </file>
     <file name="style.css">
       .odd {
         color: red;
       }
       .even {
         color: blue;
       }
     </file>
     <file name="scenario.js">
       it('should check ng-class-odd and ng-class-even', function() {
         expect(element('.doc-example-live li:first span').prop('className')).
           toMatch(/odd/);
         expect(element('.doc-example-live li:last span').prop('className')).
           toMatch(/even/);
       });
     </file>
   </example>
 */
var ngClassOddDirective = classDirective('Odd', 0);

/**
 * @ngdoc directive
 * @name ng.directive:ngClassEven
 * @restrict AC
 *
 * @description
 * The `ngClassOdd` and `ngClassEven` directives work exactly as
 * {@link ng.directive:ngClass ngClass}, except they work in
 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
 *
 * This directive can be applied only within the scope of an
 * {@link ng.directive:ngRepeat ngRepeat}.
 *
 * @element ANY
 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
 *   result of the evaluation can be a string representing space delimited class names or an array.
 *
 * @example
   <example>
     <file name="index.html">

             {{name}}

     </file>
     <file name="style.css">
       .odd {
         color: red;
       }
       .even {
         color: blue;
       }
     </file>
     <file name="scenario.js">
       it('should check ng-class-odd and ng-class-even', function() {
         expect(element('.doc-example-live li:first span').prop('className')).
           toMatch(/odd/);
         expect(element('.doc-example-live li:last span').prop('className')).
           toMatch(/even/);
       });
     </file>
   </example>
 */
var ngClassEvenDirective = classDirective('Even', 1);
<|endoftext|>


<commit_before>module.exports = {
  options: {
    cssVars: {
      '--status-bar-height': '25px',
      '--window-top': '0px',
      '--window-bottom': '0px',
      '--window-left': '0px',
      '--window-right': '0px'
    },
    extnames: {
      style: '.ttss',
      template: '.ttml'
    },
    project: 'project.tt.json'
  },
  copyWebpackOptions (platformOptions, vueOptions) {
    const copyOptions = ['ttcomponents']
    global.uniModules.forEach(module => {
      copyOptions.push('uni_modules/' + module + '/ttcomponents')
    })
    return copyOptions
  }
}
<commit_msg>feat: 字节跳动支持分包加载<commit_after>module.exports = {
  options: {
    cssVars: {
      '--status-bar-height': '25px',
      '--window-top': '0px',
      '--window-bottom': '0px',
      '--window-left': '0px',
      '--window-right': '0px'
    },
    extnames: {
      style: '.ttss',
      template: '.ttml'
    },
    subPackages: true,
    project: 'project.tt.json'
  },
  copyWebpackOptions (platformOptions, vueOptions) {
    const copyOptions = ['ttcomponents']
    global.uniModules.forEach(module => {
      copyOptions.push('uni_modules/' + module + '/ttcomponents')
    })
    return copyOptions
  }
}
<|endoftext|>


<commit_before>(function(win) {
	"use strict";
	var urlData = readURL();

	getFile(urlData, urlData.pathToMeta +'.gh-embed.json', function(d) {
		if(d.error) {
			return error('Cannot load .gh-embed.json: ' + d.error);
		}

		try {
			d = JSON.parse(d);
		} catch(e) {
			return error('Cannot parse .gh-embed.json: ' + e);
		}

		embed(d);

	});

	$('.on-github').href='//github.com/'+urlData.owner+'/'+urlData.repo+'/tree/'+urlData.ref+'/'+urlData.pathToMeta;

	function error(err) {
		$('main').classList.add('error');
		$('.error-text').textContent = err;
	}

    function embed(o) {
		var el = $('main'),
			nav = $('nav'),
			frames = $('.frames'),
			loadedCount = 0,
			hasActive;

		o.embed.forEach(function(item) {
			var frame,
				link;

			item.owner = item.owner || urlData.owner;
			item.repo = item.repo || urlData.repo;
			item.ref = item.ref || urlData.ref;
			item.type = item.type || item.path.match(/.*\.(\S+)$/);

			if(item.type == 'htmlpage') {
				frame = frames.appendChild(create('iframe', {
					src: item.url,
					onload: loaded,
					onerror: loaded,
					className: 'frame'
				}));

				link = nav.appendChild(create('a', {
					innerHTML: item.label || 'HTML Page'
				}));

			} else {
				link = nav.appendChild(create('a', {
					innerHTML: item.label || item.path
				}));

				frame = frames.appendChild(create('pre', {
					className: 'frame'
				}));

				getFile(item, item.path, function(data) {
					frame.classList.add('prettyprint', 'lang-' + item.type);
					if(o.linenums !== false) {
						frame.classList.add('linenums');
					}

					frame.textContent = data.error ? 'Error' : data;

					loaded();
				});
			}

			link.classList.add('l');

			if(item.active && !hasActive) {
				link.classList.add('active');
				frame.classList.add('active');
				hasActive = true;
			}

			link.addEventListener('click', function(evt) {
				var activeLink = $('a.active', nav),
					activeFrame = $('.frame.active', frames)

				if(activeLink) {
					activeLink.classList.remove('active');
				}

				if(activeFrame) {
					activeFrame.classList.remove('active');
				}

				link.classList.add('active');
				frame.classList.add('active');
			});
		});

		if(!hasActive) {
			$('a.l', nav).classList.add('active');
			$('.frame', frames).classList.add('active');
		}

		function loaded() {
			if(++loadedCount == o.embed.length) {
				el.classList.remove('loading');
				prettyPrint();
			}
		}
	}

	function readURL() {
		var loc = document.location,
			path = loc.pathname.match(/\/v(\d+)\/([^\/]+)\/([^\/]+)\/([^\/]+)?/);

		return {
			owner: path[2],
			repo: path[3],
			pathToMeta: path[4] ? (path[4] + '/') : '',
			ref: (loc.search.match(/\S*(\?|&)ref=(\S+)\S*/) || [])[2] || 'master'
		};
	}

	function getFile(data, path, callback) {
		var xhr = new XMLHttpRequest();
		xhr.open('GET', 'https://api.github.com/repos/'+data.owner+'/'+data.repo+'/contents/'+path+'?ref=' + data.ref, true);

		xhr.onload = function() {
			var resp = JSON.parse(xhr.responseText);
			if(resp.content) {
			  console.log(resp.content);
				callback(atob(resp.content.replace(/\s/g, '')));
			} else {
				xhr.onerror();
			}
		}

		// fallback in case if API limmit is exceeded
		xhr.onerror = function() {
			var xhr2 = new XMLHttpRequest();
			xhr2.open('GET', 'https://raw.githubusercontent.com/'+data.owner+'/'+data.repo+'/'+data.ref+'/'+path, true);
			xhr2.onload = function() {
				if(xhr2.status !== 200) {
					callback({ error: xhr2.statusText })
				} else {
					callback(xhr2.responseText);
				}
			}

			xhr2.onerror = function() {
				callback({ error: xhr2.statusText });
			}

			xhr2.send();
		}

		xhr.send();
	}

	function create(tagName, props, el, i, hasOwn) {
		el = document.createElement(tagName);
		hasOwn = 'hasOwnProperty';
		if (props) {
			for (i in props) {
				el[i] = props[i];
			}
		}
		return el;
	};

	function $(s, ctx) {
		return (ctx || document).querySelector(s);
	}
})(window);
<commit_msg>minor<commit_after>(function(win) {
	"use strict";
	var urlData = readURL();

	getFile(urlData, urlData.pathToMeta +'.gh-embed.json', function(d) {
		if(d.error) {
			return error('Cannot load .gh-embed.json: ' + d.error);
		}

		try {
			d = JSON.parse(d);
		} catch(e) {
			return error('Cannot parse .gh-embed.json: ' + e);
		}

		embed(d);

	});

	$('.on-github').href='//github.com/'+urlData.owner+'/'+urlData.repo+'/tree/'+urlData.ref+'/'+urlData.pathToMeta;

	function error(err) {
		$('main').classList.add('error');
		$('.error-text').textContent = err;
	}

    function embed(o) {
		var el = $('main'),
			nav = $('nav'),
			frames = $('.frames'),
			loadedCount = 0,
			hasActive;

		o.embed.forEach(function(item) {
			var frame,
				link;

			item.owner = item.owner || urlData.owner;
			item.repo = item.repo || urlData.repo;
			item.ref = item.ref || urlData.ref;
			item.type = item.type || item.path.match(/.*\.(\S+)$/);

			if(item.type == 'htmlpage') {
				frame = frames.appendChild(create('iframe', {
					src: item.url,
					onload: loaded,
					onerror: loaded,
					className: 'frame'
				}));

				link = nav.appendChild(create('a', {
					innerHTML: item.label || 'HTML Page'
				}));

			} else {
				link = nav.appendChild(create('a', {
					innerHTML: item.label || item.path
				}));

				frame = frames.appendChild(create('pre', {
					className: 'frame'
				}));

				getFile(item, item.path, function(data) {
					frame.classList.add('prettyprint', 'lang-' + item.type);
					if(o.linenums !== false) {
						frame.classList.add('linenums');
					}

					frame.textContent = data.error ? 'Error' : data;

					loaded();
				});
			}

			link.classList.add('l');

			if(item.active && !hasActive) {
				link.classList.add('active');
				frame.classList.add('active');
				hasActive = true;
			}

			link.addEventListener('click', function(evt) {
				var activeLink = $('a.active', nav),
					activeFrame = $('.frame.active', frames)

				if(activeLink) {
					activeLink.classList.remove('active');
				}

				if(activeFrame) {
					activeFrame.classList.remove('active');
				}

				link.classList.add('active');
				frame.classList.add('active');
			});
		});

		if(!hasActive) {
			$('a.l', nav).classList.add('active');
			$('.frame', frames).classList.add('active');
		}

		function loaded() {
			if(++loadedCount == o.embed.length) {
				el.classList.remove('loading');
				prettyPrint();
			}
		}
	}

	function readURL() {
		var loc = document.location,
			path = loc.pathname.match(/\/v(\d+)\/([^\/]+)\/([^\/]+)\/([^\/]+)?/);

		return {
			owner: path[2],
			repo: path[3],
			pathToMeta: path[4] ? (path[4] + '/') : '',
			ref: (loc.search.match(/\S*(\?|&)ref=(\S+)\S*/) || [])[2] || 'master'
		};
	}

	function getFile(data, path, callback) {
		var xhr = new XMLHttpRequest();
		xhr.open('GET', 'https://api.github.com/repos/'+data.owner+'/'+data.repo+'/contents/'+path+'?ref=' + data.ref, true);

		xhr.onload = function() {
			var resp = JSON.parse(xhr.responseText);
			if(resp.content) {
				callback(atob(resp.content.replace(/\s/g, '')));
			} else {
				xhr.onerror();
			}
		}

		// fallback in case if API limmit is exceeded
		xhr.onerror = function() {
			var xhr2 = new XMLHttpRequest();
			xhr2.open('GET', 'https://raw.githubusercontent.com/'+data.owner+'/'+data.repo+'/'+data.ref+'/'+path, true);
			xhr2.onload = function() {
				if(xhr2.status !== 200) {
					callback({ error: xhr2.statusText })
				} else {
					callback(xhr2.responseText);
				}
			}

			xhr2.onerror = function() {
				callback({ error: xhr2.statusText });
			}

			xhr2.send();
		}

		xhr.send();
	}

	function create(tagName, props, el, i, hasOwn) {
		el = document.createElement(tagName);
		hasOwn = 'hasOwnProperty';
		if (props) {
			for (i in props) {
				el[i] = props[i];
			}
		}
		return el;
	};

	function $(s, ctx) {
		return (ctx || document).querySelector(s);
	}
})(window);
<|endoftext|>


<commit_before>describe("Keyboard Support Tests", function() {
  var testSlider,
      handle1,
      handle2,
      initialMinVal = 0,
      initialMaxVal = 10,
      initialStepVal = 1,
      initialSliderVal = 5;

  describe("Clicking on slider handle automatically gives it focus", function() {

    beforeEach(function() {
      testSlider = $("#testSlider1").slider({
        id: 'testSlider'
      });
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
    });

    it("clicking on handle1 gives focus to handle1", function() {
      var focusWasTriggered = false;
      handle1.focus(function() {
        focusWasTriggered = true;
        expect(focusWasTriggered).toBeTruthy();
      });
      handle1.mousedown();
    });
  });

  describe("When slider handle has TAB focus", function() {

    it("should display it's tooltip if 'tooltip' option is set to 'show'", function() {
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        tooltip: 'show'
      });
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");

      // Check for no tooltip before focus
      var tooltipIsShown = $("#testSlider").find("div.tooltip").hasClass("in");
      expect(tooltipIsShown).toBeFalsy();

      handle1.focus();

      // Tooltip should be present after focus
      tooltipIsShown = $("#testSlider").find("div.tooltip").hasClass("in");
      expect(tooltipIsShown).toBeTruthy();
    });

    it("should not display it's tooltip if 'tooltip' option is set to 'hide'", function() {
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        tooltip: 'hide'
      });
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");

      // Check for hidden tooltip before focus
      var tooltipIsHidden = $("#testSlider1").siblings("div.tooltip").hasClass("hide");
      expect(tooltipIsHidden).toBeTruthy();

      handle1.focus();

      // Tooltip should remain hidden after focus
      tooltipIsHidden = $("#testSlider1").siblings("div.tooltip").hasClass("hide");
      expect(tooltipIsHidden).toBeTruthy();
    });

    it("should not affect the tooltip display if 'tooltip' option is set to 'always'", function() {
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        tooltip: 'always'
      });
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");

      // Check for shown tooltip before focus
      var tooltipIsShown = $("#testSlider1").siblings("div.tooltip").hasClass("in");
      expect(tooltipIsShown).toBeTruthy();

      handle1.focus();

      // Tooltip should remain present after focus
      tooltipIsShown = $("#testSlider1").siblings("div.tooltip").hasClass("in");
      expect(tooltipIsShown).toBeTruthy();
    });
  });

  describe("For horizontal sliders where its handle has focus", function() {

    beforeEach(function() {
      // Initialize the slider
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        orientation: 'horizontal',
        min: initialMinVal,
        max: initialMaxVal,
        step: initialStepVal,
        value: initialSliderVal
      });
      // Focus on handle1
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
      handle1.focus();
    });

    it("moves to the left by the 'step' value when the LEFT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var leftArrowKeyDownEvent = $.Event('keydown');
      leftArrowKeyDownEvent.which = 37;
      handle1.trigger(leftArrowKeyDownEvent);
    });

    it("moves to the right by the 'step' value when the RIGHT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var rightArrowKeyDownEvent = $.Event('keydown');
      rightArrowKeyDownEvent.which = 39;
      handle1.trigger(rightArrowKeyDownEvent);
    });

    it("moves to the left by the 'step' value when the DOWN arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var downArrowKeyDownEvent = $.Event('keydown');
      downArrowKeyDownEvent.which = 40;
      handle1.trigger(downArrowKeyDownEvent);
    });

    it("moves to the right by the 'step' value when the UP arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var upArrowKeyDownEvent = $.Event('keydown');
      upArrowKeyDownEvent.which = 38;
      handle1.trigger(upArrowKeyDownEvent);
    });
  });

  describe("For vertical sliders where its handle has focus", function() {

    beforeEach(function() {
      // Initialize the slider
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        orientation: 'vertical',
        min: initialMinVal,
        max: initialMaxVal,
        step: initialStepVal,
        value: initialSliderVal
      });
      // Focus on handle1
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
      handle1.focus();
    });

    it("moves down by the 'step' value when the LEFT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var leftArrowKeyDownEvent = $.Event('keydown');
      leftArrowKeyDownEvent.which = 37;
      handle1.trigger(leftArrowKeyDownEvent);
    });

    it("moves up by the 'step' value when the RIGHT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var rightArrowKeyDownEvent = $.Event('keydown');
      rightArrowKeyDownEvent.which = 39;
      handle1.trigger(rightArrowKeyDownEvent);
    });

    it("moves down by the 'step' value when the DOWN arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var downArrowKeyDownEvent = $.Event('keydown');
      downArrowKeyDownEvent.which = 40;
      handle1.trigger(downArrowKeyDownEvent);
    });

    it("moves up by the 'step' value when the UP arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var upArrowKeyDownEvent = $.Event('keydown');
      upArrowKeyDownEvent.which = 38;
      handle1.trigger(upArrowKeyDownEvent);
    });
  });

  describe("For a reversed slider (regardless of 'orientation')", function() {

    beforeEach(function() {
      // Initialize the slider
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        reversed: true,
        min: initialMinVal,
        max: initialMaxVal,
        step: initialStepVal,
        value: initialSliderVal
      });
      // Focus on handle1
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
      handle1.focus();
    });

    it("moves to the left by the 'step' value when the LEFT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var leftArrowKeyDownEvent = $.Event('keydown');
      leftArrowKeyDownEvent.which = 37;
      handle1.trigger(leftArrowKeyDownEvent);
    });

    it("moves to the right by the 'step' value when the RIGHT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var rightArrowKeyDownEvent = $.Event('keydown');
      rightArrowKeyDownEvent.which = 39;
      handle1.trigger(rightArrowKeyDownEvent);
    });

    it("moves to the left by the 'step' value when the DOWN arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var downArrowKeyDownEvent = $.Event('keydown');
      downArrowKeyDownEvent.which = 40;
      handle1.trigger(downArrowKeyDownEvent);
    });

    it("moves to the right by the 'step' value when the UP arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var upArrowKeyDownEvent = $.Event('keydown');
      upArrowKeyDownEvent.which = 38;
      handle1.trigger(upArrowKeyDownEvent);
    });
  });

  describe("For a range slider (regardless of 'orientation')", function() {

    beforeEach(function() {
      // Initialize the slider
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        min: initialMinVal,
        max: initialMaxVal,
        step: initialStepVal,
        value: [initialSliderVal, initialSliderVal]
      });
    });

    describe("when handle1 tries to overtake handle2 from the left", function() {
      beforeEach(function() {
        handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
        handle1.focus();
      });

      it("handle2 moves to the right by the step value", function() {
        handle1.on("keydown", function() {
          var sliderValue = testSlider.slider('getValue');
          var expectedSliderValue = initialSliderVal + initialStepVal;

          expect(sliderValue[1]).toBe(expectedSliderValue);
        });

        var rightArrowKeyDownEvent = $.Event('keydown');
        rightArrowKeyDownEvent.which = 39;
        handle1.trigger(rightArrowKeyDownEvent);
      });

      it("handle1's value remains unchanged", function() {
        var sliderValue = testSlider.slider('getValue');

        handle1.on("keydown", function() {
          expect(sliderValue[0]).toBe(initialSliderVal);
        });

        var rightArrowKeyDownEvent = $.Event('keydown');
        rightArrowKeyDownEvent.which = 39;
        handle1.trigger(rightArrowKeyDownEvent);
      });
    });

    describe("when handle2 tries to overtake handle1 from the right", function() {
      beforeEach(function() {
        handle2 = $("#testSlider").find(".slider-track > .slider-handle:eq( 1 )");
        handle2.focus();
      });

      it("handle1 moves to the left by the step value", function() {
        handle2.on("keydown", function() {
          var sliderValue = testSlider.slider('getValue');
          var expectedSliderValue = initialSliderVal - initialStepVal;

          expect(sliderValue[0]).toBe(expectedSliderValue);
        });

        var leftArrowKeyDownEvent = $.Event('keydown');
        leftArrowKeyDownEvent.which = 37;
        handle2.trigger(leftArrowKeyDownEvent);
      });

      it("handle2's value remains unchanged", function() {
        var sliderValue = testSlider.slider('getValue');

        handle2.on("keydown", function() {
          expect(sliderValue[1]).toBe(initialSliderVal);
        });

        var leftArrowKeyDownEvent = $.Event('keydown');
        leftArrowKeyDownEvent.which = 37;
        handle2.trigger(leftArrowKeyDownEvent);
      });
    });
  });

  describe("For the natural arrow keys", function() {
    var testCases = [{
      reversed: false,
      keyEvent: 37, // left
      expectedSliderValue: initialSliderVal - initialStepVal,
      orientation: 'horizontal'
    }, {
      reversed: true,
      keyEvent: 37, // left
      expectedSliderValue: initialSliderVal + initialStepVal,
      orientation: 'horizontal'
    }, {
      reversed: false,
      keyEvent: 39, // right
      expectedSliderValue: initialSliderVal + initialStepVal,
      orientation: 'horizontal'
    }, {
      reversed: true,
      keyEvent: 39, // right
      expectedSliderValue: initialSliderVal - initialStepVal,
      orientation: 'horizontal'
    }, {
      reversed: false,
      keyEvent: 38, // up
      expectedSliderValue: initialSliderVal - initialStepVal,
      orientation: 'vertical'
    }, {
      reversed: true,
      keyEvent: 38, // up
      expectedSliderValue: initialSliderVal + initialStepVal,
      orientation: 'vertical'
    }, {
      reversed: false,
      keyEvent: 40, // down
      expectedSliderValue: initialSliderVal + initialStepVal,
      orientation: 'vertical'
    }, {
      reversed: true,
      keyEvent: 40, // down
      expectedSliderValue: initialSliderVal - initialStepVal,
      orientation: 'vertical'
    }];
    testCases.forEach(function(testCase) {
      describe("when handle1 tries to overtake handle2 from the left", function() {
        beforeEach(function() {
          // Initialize the slider
          testSlider = $("#testSlider1").slider({
            id: 'testSlider',
            min: initialMinVal,
            max: initialMaxVal,
            step: initialStepVal,
            value: initialSliderVal,
            naturalarrowkeys: true,
            reversed: testCase.reversed,
            orientation: testCase.orientation
          });
          handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
          handle1.focus();
        });

        it("moves to the left by the 'step' value when the LEFT arrow key is pressed", function() {
          handle1.on("keydown", function() {
            var sliderValue = testSlider.slider('getValue');
            var expectedSliderValue = testCase.expectedSliderValue;

            expect(sliderValue).toBe(expectedSliderValue);
          });

          var leftArrowKeyDownEvent = $.Event('keydown');
          leftArrowKeyDownEvent.which = testCase.keyEvent;
          handle1.trigger(leftArrowKeyDownEvent);
        });
      });
    });
  });
  afterEach(function() {
    if(testSlider) { testSlider.slider('destroy'); }
  });
});<commit_msg>Updated unit tests names and descriptions<commit_after>describe("Keyboard Support Tests", function() {
  var testSlider,
      handle1,
      handle2,
      initialMinVal = 0,
      initialMaxVal = 10,
      initialStepVal = 1,
      initialSliderVal = 5;

  describe("Clicking on slider handle automatically gives it focus", function() {

    beforeEach(function() {
      testSlider = $("#testSlider1").slider({
        id: 'testSlider'
      });
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
    });

    it("clicking on handle1 gives focus to handle1", function() {
      var focusWasTriggered = false;
      handle1.focus(function() {
        focusWasTriggered = true;
        expect(focusWasTriggered).toBeTruthy();
      });
      handle1.mousedown();
    });
  });

  describe("When slider handle has TAB focus", function() {

    it("should display it's tooltip if 'tooltip' option is set to 'show'", function() {
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        tooltip: 'show'
      });
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");

      // Check for no tooltip before focus
      var tooltipIsShown = $("#testSlider").find("div.tooltip").hasClass("in");
      expect(tooltipIsShown).toBeFalsy();

      handle1.focus();

      // Tooltip should be present after focus
      tooltipIsShown = $("#testSlider").find("div.tooltip").hasClass("in");
      expect(tooltipIsShown).toBeTruthy();
    });

    it("should not display it's tooltip if 'tooltip' option is set to 'hide'", function() {
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        tooltip: 'hide'
      });
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");

      // Check for hidden tooltip before focus
      var tooltipIsHidden = $("#testSlider1").siblings("div.tooltip").hasClass("hide");
      expect(tooltipIsHidden).toBeTruthy();

      handle1.focus();

      // Tooltip should remain hidden after focus
      tooltipIsHidden = $("#testSlider1").siblings("div.tooltip").hasClass("hide");
      expect(tooltipIsHidden).toBeTruthy();
    });

    it("should not affect the tooltip display if 'tooltip' option is set to 'always'", function() {
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        tooltip: 'always'
      });
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");

      // Check for shown tooltip before focus
      var tooltipIsShown = $("#testSlider1").siblings("div.tooltip").hasClass("in");
      expect(tooltipIsShown).toBeTruthy();

      handle1.focus();

      // Tooltip should remain present after focus
      tooltipIsShown = $("#testSlider1").siblings("div.tooltip").hasClass("in");
      expect(tooltipIsShown).toBeTruthy();
    });
  });

  describe("For horizontal sliders where its handle has focus", function() {

    beforeEach(function() {
      // Initialize the slider
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        orientation: 'horizontal',
        min: initialMinVal,
        max: initialMaxVal,
        step: initialStepVal,
        value: initialSliderVal
      });
      // Focus on handle1
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
      handle1.focus();
    });

    it("moves to the left by the 'step' value when the LEFT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var leftArrowKeyDownEvent = $.Event('keydown');
      leftArrowKeyDownEvent.which = 37;
      handle1.trigger(leftArrowKeyDownEvent);
    });

    it("moves to the right by the 'step' value when the RIGHT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var rightArrowKeyDownEvent = $.Event('keydown');
      rightArrowKeyDownEvent.which = 39;
      handle1.trigger(rightArrowKeyDownEvent);
    });

    it("moves to the left by the 'step' value when the DOWN arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var downArrowKeyDownEvent = $.Event('keydown');
      downArrowKeyDownEvent.which = 40;
      handle1.trigger(downArrowKeyDownEvent);
    });

    it("moves to the right by the 'step' value when the UP arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var upArrowKeyDownEvent = $.Event('keydown');
      upArrowKeyDownEvent.which = 38;
      handle1.trigger(upArrowKeyDownEvent);
    });
  });

  describe("For vertical sliders where its handle has focus", function() {

    beforeEach(function() {
      // Initialize the slider
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        orientation: 'vertical',
        min: initialMinVal,
        max: initialMaxVal,
        step: initialStepVal,
        value: initialSliderVal
      });
      // Focus on handle1
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
      handle1.focus();
    });

    it("moves down by the 'step' value when the LEFT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var leftArrowKeyDownEvent = $.Event('keydown');
      leftArrowKeyDownEvent.which = 37;
      handle1.trigger(leftArrowKeyDownEvent);
    });

    it("moves up by the 'step' value when the RIGHT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var rightArrowKeyDownEvent = $.Event('keydown');
      rightArrowKeyDownEvent.which = 39;
      handle1.trigger(rightArrowKeyDownEvent);
    });

    it("moves down by the 'step' value when the DOWN arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var downArrowKeyDownEvent = $.Event('keydown');
      downArrowKeyDownEvent.which = 40;
      handle1.trigger(downArrowKeyDownEvent);
    });

    it("moves up by the 'step' value when the UP arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var upArrowKeyDownEvent = $.Event('keydown');
      upArrowKeyDownEvent.which = 38;
      handle1.trigger(upArrowKeyDownEvent);
    });
  });

  describe("For a reversed slider (regardless of 'orientation')", function() {

    beforeEach(function() {
      // Initialize the slider
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        reversed: true,
        min: initialMinVal,
        max: initialMaxVal,
        step: initialStepVal,
        value: initialSliderVal
      });
      // Focus on handle1
      handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
      handle1.focus();
    });

    it("moves to the left by the 'step' value when the LEFT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var leftArrowKeyDownEvent = $.Event('keydown');
      leftArrowKeyDownEvent.which = 37;
      handle1.trigger(leftArrowKeyDownEvent);
    });

    it("moves to the right by the 'step' value when the RIGHT arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var rightArrowKeyDownEvent = $.Event('keydown');
      rightArrowKeyDownEvent.which = 39;
      handle1.trigger(rightArrowKeyDownEvent);
    });

    it("moves to the left by the 'step' value when the DOWN arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal - initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var downArrowKeyDownEvent = $.Event('keydown');
      downArrowKeyDownEvent.which = 40;
      handle1.trigger(downArrowKeyDownEvent);
    });

    it("moves to the right by the 'step' value when the UP arrow key is pressed", function() {
      handle1.on("keydown", function() {
        var sliderValue = testSlider.slider('getValue');
        var expectedSliderValue = initialSliderVal + initialStepVal;

        expect(sliderValue).toBe(expectedSliderValue);
      });

      var upArrowKeyDownEvent = $.Event('keydown');
      upArrowKeyDownEvent.which = 38;
      handle1.trigger(upArrowKeyDownEvent);
    });
  });

  describe("For a range slider (regardless of 'orientation')", function() {

    beforeEach(function() {
      // Initialize the slider
      testSlider = $("#testSlider1").slider({
        id: 'testSlider',
        min: initialMinVal,
        max: initialMaxVal,
        step: initialStepVal,
        value: [initialSliderVal, initialSliderVal]
      });
    });

    describe("when handle1 tries to overtake handle2 from the left", function() {
      beforeEach(function() {
        handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
        handle1.focus();
      });

      it("handle2 moves to the right by the step value", function() {
        handle1.on("keydown", function() {
          var sliderValue = testSlider.slider('getValue');
          var expectedSliderValue = initialSliderVal + initialStepVal;

          expect(sliderValue[1]).toBe(expectedSliderValue);
        });

        var rightArrowKeyDownEvent = $.Event('keydown');
        rightArrowKeyDownEvent.which = 39;
        handle1.trigger(rightArrowKeyDownEvent);
      });

      it("handle1's value remains unchanged", function() {
        var sliderValue = testSlider.slider('getValue');

        handle1.on("keydown", function() {
          expect(sliderValue[0]).toBe(initialSliderVal);
        });

        var rightArrowKeyDownEvent = $.Event('keydown');
        rightArrowKeyDownEvent.which = 39;
        handle1.trigger(rightArrowKeyDownEvent);
      });
    });

    describe("when handle2 tries to overtake handle1 from the right", function() {
      beforeEach(function() {
        handle2 = $("#testSlider").find(".slider-track > .slider-handle:eq( 1 )");
        handle2.focus();
      });

      it("handle1 moves to the left by the step value", function() {
        handle2.on("keydown", function() {
          var sliderValue = testSlider.slider('getValue');
          var expectedSliderValue = initialSliderVal - initialStepVal;

          expect(sliderValue[0]).toBe(expectedSliderValue);
        });

        var leftArrowKeyDownEvent = $.Event('keydown');
        leftArrowKeyDownEvent.which = 37;
        handle2.trigger(leftArrowKeyDownEvent);
      });

      it("handle2's value remains unchanged", function() {
        var sliderValue = testSlider.slider('getValue');

        handle2.on("keydown", function() {
          expect(sliderValue[1]).toBe(initialSliderVal);
        });

        var leftArrowKeyDownEvent = $.Event('keydown');
        leftArrowKeyDownEvent.which = 37;
        handle2.trigger(leftArrowKeyDownEvent);
      });
    });
  });

  describe("For the natural arrow keys", function() {
    var testCases = [{
      reversed: false,
      keyEvent: 37,
      expectedSliderValue: initialSliderVal - initialStepVal,
      orientation: 'horizontal',
      key: 'left'
    }, {
      reversed: true,
      keyEvent: 37,
      expectedSliderValue: initialSliderVal + initialStepVal,
      orientation: 'horizontal',
      key: 'left'
    }, {
      reversed: false,
      keyEvent: 39,
      expectedSliderValue: initialSliderVal + initialStepVal,
      orientation: 'horizontal',
      key: 'right'
    }, {
      reversed: true,
      keyEvent: 39,
      expectedSliderValue: initialSliderVal - initialStepVal,
      orientation: 'horizontal',
      key: 'right'
    }, {
      reversed: false,
      keyEvent: 38,
      expectedSliderValue: initialSliderVal - initialStepVal,
      orientation: 'vertical',
      key: 'up'
    }, {
      reversed: true,
      keyEvent: 38,
      expectedSliderValue: initialSliderVal + initialStepVal,
      orientation: 'vertical',
      key: 'up'
    }, {
      reversed: false,
      keyEvent: 40,
      expectedSliderValue: initialSliderVal + initialStepVal,
      orientation: 'vertical',
      key: 'down'
    }, {
      reversed: true,
      keyEvent: 40,
      expectedSliderValue: initialSliderVal - initialStepVal,
      orientation: 'vertical',
      key: 'down'
    }];
    testCases.forEach(function(testCase) {
      describe("A"+((testCase.reversed)? " reversed" : "")+testCase.orientation+" slider is used for the arrow keys", function() {
        beforeEach(function() {
          // Initialize the slider
          testSlider = $("#testSlider1").slider({
            id: 'testSlider',
            min: initialMinVal,
            max: initialMaxVal,
            step: initialStepVal,
            value: initialSliderVal,
            naturalarrowkeys: true,
            reversed: testCase.reversed,
            orientation: testCase.orientation
          });
          handle1 = $("#testSlider").find(".slider-track > .slider-handle:first");
          handle1.focus();
        });

        it("moves to the "+testCase.key+" by the 'step' value when the "+testCase.key+" arrow key is pressed", function() {
          handle1.on("keydown", function() {
            var sliderValue = testSlider.slider('getValue');
            var expectedSliderValue = testCase.expectedSliderValue;

            expect(sliderValue).toBe(expectedSliderValue);
          });

          var leftArrowKeyDownEvent = $.Event('keydown');
          leftArrowKeyDownEvent.which = testCase.keyEvent;
          handle1.trigger(leftArrowKeyDownEvent);
        });
      });
    });
  });
  afterEach(function() {
    if(testSlider) { testSlider.slider('destroy'); }
  });
});<|endoftext|>