<commit_before>		(function () {
			define(["jquery", "d3", "jnj/chart", "common", "datatables", "datatables-colvis"], function ($, d3, jnj_chart, common) {
				var drug_era = {};
				var threshold;
				var datatable;

				// bind to all matching elements upon creation

				$(document).on('click', '#drugera_table tbody tr', function () {
					$('#drugera_table tbody tr.selected').removeClass('selected');
					$(this).addClass('selected');
					id = datatable.data()[datatable.row(this)[0]].concept_id;
					concept_name = datatable.data()[datatable.row(this)[0]].ingredient;
					drug_era.drilldown(id, concept_name);
				});

				$('#myTab a').click(function (e) {
					e.preventDefault();
					$(this).tab('show');
					$(window).trigger("resize");
				})

				boxplot_helper = function (data, target, xlabel, ylabel) {
					var boxplot = new jnj_chart.boxplot();
					var yMax = 0;
					bpseries = [];
					bpdata = data;

					if (Array.isArray(bpdata.CATEGORY)) {
						for (i = 0; i < bpdata.CATEGORY.length; i++) {
							bpseries.push({
								Category: bpdata.CATEGORY[i],
								min: bpdata.MIN_VALUE[i],
								max: bpdata.MAX_VALUE[i],
								median: bpdata.MEDIAN_VALUE[i],
								LIF: bpdata.P10_VALUE[i],
								q1: bpdata.P25_VALUE[i],
								q3: bpdata.P75_VALUE[i],
								UIF: bpdata.P90_VALUE[i]
							});
							yMax = Math.max(yMax, bpdata.P90_VALUE[i]);
						}
					} else {
							bpseries.push({
								Category: bpdata.CATEGORY,
								min: bpdata.MIN_VALUE,
								max: bpdata.MAX_VALUE,
								median: bpdata.MEDIAN_VALUE,
								LIF: bpdata.P10_VALUE,
								q1: bpdata.P25_VALUE,
								q3: bpdata.P75_VALUE,
								UIF: bpdata.P90_VALUE
							});
						yMax = bpdata.P90_VALUE;
					}

					boxplot.render(bpseries, target, 200, 200, {
						yMax: yMax,
						xLabel: xlabel,
						yLabel: ylabel
					});
				}

				drug_era.drilldown = function (concept_id, concept_name) {
					$('.drilldown svg').remove();
					$('#drugeraDrilldownTitle').text(concept_name);
					$('#reportDrugErasDrilldown').removeClass('hidden');

					$.ajax({
						type: "GET",
						url: 'data/' + page_vm.datasource().folder + '/drugeras/drug_' + concept_id + '.json',
						success: function (data) {

							// boxplots
							boxplot_helper(data.AGE_AT_FIRST_EXPOSURE, '#drugeraAgeAtFirstExposure', '', 'Age at First Exposure');
							boxplot_helper(data.LENGTH_OF_ERA,'#drugeraLengthOfEra', '', 'Days');

							// prevalence by month
							var byMonthSeries = common.mapMonthYearDataToSeries(data.PREVALENCE_BY_MONTH, {
								dateField: 'X_CALENDAR_MONTH',
								yValue: 'Y_PREVALENCE_1000PP',
								yPercent: 'Y_PREVALENCE_1000PP'
							});

							d3.selectAll("#reportDrugEras #drugPrevalenceByMonth svg").remove();
							var prevalenceByMonth = new jnj_chart.line();
							prevalenceByMonth.render(byMonthSeries, "#reportDrugEras #drugPrevalenceByMonth", 900, 250, {
								xScale: d3.time.scale().domain(d3.extent(byMonthSeries[0].values, function (d) {
									return d.xValue;
								})),
								tickFormat: d3.time.format("%Y"),
								tickPadding: 10,
								margin: {
									top: 5,
									right: 25,
									bottom: 5,
									left: 40
								},
								xLabel: "Date",
								yLabel: "Prevalence per 1000 People"
							});

							// render trellis
							trellisData = data.PREVALENCE_BY_GENDER_AGE_YEAR;

							var allDeciles = ["0-9", "10-19", "20-29", "30-39", "40-49", "50-59", "60-69", "70-79", "80-89", "90-99"];
							var allSeries = ["MALE", "FEMALE"];
							var minYear = d3.min(trellisData.X_CALENDAR_YEAR),
								maxYear = d3.max(trellisData.X_CALENDAR_YEAR);

							var seriesInitializer = function (tName, sName, x, y) {
								return {
									TRELLIS_NAME: tName,
									SERIES_NAME: sName,
									X_CALENDAR_YEAR: x,
									Y_PREVALENCE_1000PP: y
								};
							}

							var nestByDecile = d3.nest()
								.key(function (d) {
									return d.TRELLIS_NAME;
								})
								.key(function (d) {
									return d.SERIES_NAME;
								})
								.sortValues(function (a, b) {
									return a.X_CALENDAR_YEAR - b.X_CALENDAR_YEAR;
								});

							// map data into chartable form
							var normalizedSeries = trellisData.TRELLIS_NAME.map(function (d, i) {
								var item = {};
								var container = this;
								d3.keys(container).forEach(function (p) {
									item[p] = container[p][i];
								});
								return item;
							}, trellisData);

							var dataByDecile = nestByDecile.entries(normalizedSeries);
							// fill in gaps
							var yearRange = d3.range(minYear, maxYear, 1);

							dataByDecile.forEach(function (trellis) {
								trellis.values.forEach(function (series) {
									series.values = yearRange.map(function (year) {
										yearData = series.values.filter(function (f) {
											return f.X_CALENDAR_YEAR == year;
										})[0] || seriesInitializer(trellis.key, series.key, year, 0);
										yearData.date = new Date(year, 0, 1);
										return yearData;
									})
								})
							});

							// create svg with range bands based on the trellis names
							var chart = new jnj_chart.trellisline();
							chart.render(dataByDecile, "#reportDrugEras #trellisLinePlot", 1000, 300, {
								trellisSet: allDeciles,
								trellisLabel: "Age Decile",
								seriesLabel: "Year of Observation",
								yLabel: "Prevalence Per 1000 People",
								xFormat: d3.time.format("%Y"),
								yFormat: d3.format("0.2f"),
								tickPadding: 20,
								colors: d3.scale.ordinal()
									.domain(["MALE", "FEMALE"])
									.range(["#1f77b4", "#ff7f0e"])
							});
						}
					});
				}

				drug_era.render = function (folder) {
					format_pct = d3.format('.2%');
					format_fixed = d3.format('.2f');
					format_comma = d3.format(',');

					$('#reportDrugEras svg').remove();

					width = 1000;
					height = 250;
					minimum_area = 50;
					threshold = minimum_area / (width * height);

					$.ajax({
						type: "GET",
						url: 'data/' + folder + '/drugera_treemap.json',
						contentType: "application/json; charset=utf-8",
						success: function (data) {
							table_data = data.CONCEPT_PATH.map(function (d, i) {
								conceptDetails = this.CONCEPT_PATH[i].split('||');
								return {
									concept_id: this.CONCEPT_ID[i],
									atc1: conceptDetails[0],
									atc3: conceptDetails[1],
									atc5: conceptDetails[2],
									ingredient: conceptDetails[3],
									num_persons: format_comma(this.NUM_PERSONS[i]),
									percent_persons: format_pct(this.PERCENT_PERSONS[i]),
									length_of_era: format_fixed(this.LENGTH_OF_ERA[i])
								}
							}, data);

							datatable = $('#drugera_table').DataTable({
								order: [ 5, 'desc' ],
								dom: 'Clfrtip',
								data: table_data,
								columns: [
									{
										data: 'concept_id',
										visible: false
									},
									{
										data: 'atc1'
									},
									{
										data: 'atc3',
										visible: false
									},
									{
										data: 'atc5'
									},
									{
										data: 'ingredient'
									},
									{
										data: 'num_persons',
										className: 'numeric'
									},
									{
										data: 'percent_persons',
										className: 'numeric'
									},
									{
										data: 'length_of_era',
										className: 'numeric'
									}
								],
								pageLength: 5,
								lengthChange: false,
								deferRender: true,
								destroy: true
							});

							$('#reportDrugEras').show();

							tree = buildHierarchyFromJSON(data, threshold);
							var treemap = new jnj_chart.treemap();
							treemap.render(tree, '#reportDrugEras #treemap_container', width, height, {
								onclick: function (node) {
									drug_era.drilldown(node.id, node.name)
								},
								getsizevalue: function (node) {
									return node.num_persons;
								},
								getcolorvalue: function (node) {
									return node.length_of_era;
								},
								gettitle: function (node) {
									title = '';
									steps = node.path.split('||');
									for (i = 0; i < steps.length; i++) {
										if (i == steps.length - 1) {
											title += '
';
											title += '
' + steps[i] + '
';
											title += '
Prevalence: ' + format_pct(node.pct_persons) + '
';
											title += '
Number of People: ' + format_comma(node.num_persons) + '
';
											title += '
Length of Era: ' + format_fixed(node.length_of_era) + '
';
										} else {
											title += '
' + Array(i + 1).join(' &nbsp') + steps[i] + '
';
										}
									}
									return title;
								}
							});
						}

					});
				}

				function buildHierarchyFromJSON(data, threshold) {
					var total = 0;

					var root = {
						"name": "root",
						"children": []
					};

					for (i = 0; i < data.PERCENT_PERSONS.length; i++) {
						total += data.PERCENT_PERSONS[i];
					}

					for (var i = 0; i < data.CONCEPT_PATH.length; i++) {
						var parts = data.CONCEPT_PATH[i].split("||");
						var currentNode = root;
						for (var j = 0; j < parts.length; j++) {
							var children = currentNode["children"];
							var nodeName = parts[j];
							var childNode;
							if (j + 1 < parts.length) {
								// Not yet at the end of the path; move down the tree.
								var foundChild = false;
								for (var k = 0; k < children.length; k++) {
									if (children[k]["name"] == nodeName) {
										childNode = children[k];
										foundChild = true;
										break;
									}
								}
								// If we don't already have a child node for this branch, create it.
								if (!foundChild) {
									childNode = {
										"name": nodeName,
										"children": []
									};
									children.push(childNode);
								}
								currentNode = childNode;
							} else {
								// Reached the end of the path; create a leaf node.
								childNode = {
									"name": nodeName,
									"num_persons": data.NUM_PERSONS[i],
									"id": data.CONCEPT_ID[i],
									"path": data.CONCEPT_PATH[i],
									"pct_persons": data.PERCENT_PERSONS[i],
									"length_of_era": data.LENGTH_OF_ERA[i]
								};

								// we only include nodes with sufficient size in the treemap display
								// sufficient size is configurable in the calculation of threshold
								// which is a function of the number of pixels in the treemap display
								if ((data.PERCENT_PERSONS[i] / total) > threshold) {
									children.push(childNode);
								}
							}
						}
					}
					return root;
				};
				return drug_era;
			});
		})();
<commit_msg>Made drug era boxplots consistent with observation period boxplots where boxplots take up 50% of screen space.<commit_after>		(function () {
			define(["jquery", "d3", "jnj/chart", "common", "datatables", "datatables-colvis"], function ($, d3, jnj_chart, common) {
				var drug_era = {};
				var threshold;
				var datatable;

				// bind to all matching elements upon creation

				$(document).on('click', '#drugera_table tbody tr', function () {
					$('#drugera_table tbody tr.selected').removeClass('selected');
					$(this).addClass('selected');
					id = datatable.data()[datatable.row(this)[0]].concept_id;
					concept_name = datatable.data()[datatable.row(this)[0]].ingredient;
					drug_era.drilldown(id, concept_name);
				});

				$('#myTab a').click(function (e) {
					e.preventDefault();
					$(this).tab('show');
					$(window).trigger("resize");
				})

				boxplot_helper = function (data, target, width, height, xlabel, ylabel) {
					var boxplot = new jnj_chart.boxplot();
					var yMax = 0;
					bpseries = [];
					bpdata = data;

					if (Array.isArray(bpdata.CATEGORY)) {
						for (i = 0; i < bpdata.CATEGORY.length; i++) {
							bpseries.push({
								Category: bpdata.CATEGORY[i],
								min: bpdata.MIN_VALUE[i],
								max: bpdata.MAX_VALUE[i],
								median: bpdata.MEDIAN_VALUE[i],
								LIF: bpdata.P10_VALUE[i],
								q1: bpdata.P25_VALUE[i],
								q3: bpdata.P75_VALUE[i],
								UIF: bpdata.P90_VALUE[i]
							});
							yMax = Math.max(yMax, bpdata.P90_VALUE[i]);
						}
					} else {
							bpseries.push({
								Category: bpdata.CATEGORY,
								min: bpdata.MIN_VALUE,
								max: bpdata.MAX_VALUE,
								median: bpdata.MEDIAN_VALUE,
								LIF: bpdata.P10_VALUE,
								q1: bpdata.P25_VALUE,
								q3: bpdata.P75_VALUE,
								UIF: bpdata.P90_VALUE
							});
						yMax = bpdata.P90_VALUE;
					}

					boxplot.render(bpseries, target, width,height, {
						yMax: yMax,
						xLabel: xlabel,
						yLabel: ylabel
					});
				}

				drug_era.drilldown = function (concept_id, concept_name) {
					$('.drilldown svg').remove();
					$('#drugeraDrilldownTitle').text(concept_name);
					$('#reportDrugErasDrilldown').removeClass('hidden');

					$.ajax({
						type: "GET",
						url: 'data/' + page_vm.datasource().folder + '/drugeras/drug_' + concept_id + '.json',
						success: function (data) {

							// boxplots
							boxplot_helper(data.AGE_AT_FIRST_EXPOSURE, '#drugeraAgeAtFirstExposure', 360,200, '', 'Age at First Exposure');
							boxplot_helper(data.LENGTH_OF_ERA,'#drugeraLengthOfEra', 360,200, '', 'Days');

							// prevalence by month
							var byMonthSeries = common.mapMonthYearDataToSeries(data.PREVALENCE_BY_MONTH, {
								dateField: 'X_CALENDAR_MONTH',
								yValue: 'Y_PREVALENCE_1000PP',
								yPercent: 'Y_PREVALENCE_1000PP'
							});

							d3.selectAll("#reportDrugEras #drugPrevalenceByMonth svg").remove();
							var prevalenceByMonth = new jnj_chart.line();
							prevalenceByMonth.render(byMonthSeries, "#reportDrugEras #drugPrevalenceByMonth", 900, 250, {
								xScale: d3.time.scale().domain(d3.extent(byMonthSeries[0].values, function (d) {
									return d.xValue;
								})),
								tickFormat: d3.time.format("%Y"),
								tickPadding: 10,
								margin: {
									top: 5,
									right: 25,
									bottom: 5,
									left: 40
								},
								xLabel: "Date",
								yLabel: "Prevalence per 1000 People"
							});

							// render trellis
							trellisData = data.PREVALENCE_BY_GENDER_AGE_YEAR;

							var allDeciles = ["0-9", "10-19", "20-29", "30-39", "40-49", "50-59", "60-69", "70-79", "80-89", "90-99"];
							var allSeries = ["MALE", "FEMALE"];
							var minYear = d3.min(trellisData.X_CALENDAR_YEAR),
								maxYear = d3.max(trellisData.X_CALENDAR_YEAR);

							var seriesInitializer = function (tName, sName, x, y) {
								return {
									TRELLIS_NAME: tName,
									SERIES_NAME: sName,
									X_CALENDAR_YEAR: x,
									Y_PREVALENCE_1000PP: y
								};
							}

							var nestByDecile = d3.nest()
								.key(function (d) {
									return d.TRELLIS_NAME;
								})
								.key(function (d) {
									return d.SERIES_NAME;
								})
								.sortValues(function (a, b) {
									return a.X_CALENDAR_YEAR - b.X_CALENDAR_YEAR;
								});

							// map data into chartable form
							var normalizedSeries = trellisData.TRELLIS_NAME.map(function (d, i) {
								var item = {};
								var container = this;
								d3.keys(container).forEach(function (p) {
									item[p] = container[p][i];
								});
								return item;
							}, trellisData);

							var dataByDecile = nestByDecile.entries(normalizedSeries);
							// fill in gaps
							var yearRange = d3.range(minYear, maxYear, 1);

							dataByDecile.forEach(function (trellis) {
								trellis.values.forEach(function (series) {
									series.values = yearRange.map(function (year) {
										yearData = series.values.filter(function (f) {
											return f.X_CALENDAR_YEAR == year;
										})[0] || seriesInitializer(trellis.key, series.key, year, 0);
										yearData.date = new Date(year, 0, 1);
										return yearData;
									})
								})
							});

							// create svg with range bands based on the trellis names
							var chart = new jnj_chart.trellisline();
							chart.render(dataByDecile, "#reportDrugEras #trellisLinePlot", 1000, 300, {
								trellisSet: allDeciles,
								trellisLabel: "Age Decile",
								seriesLabel: "Year of Observation",
								yLabel: "Prevalence Per 1000 People",
								xFormat: d3.time.format("%Y"),
								yFormat: d3.format("0.2f"),
								tickPadding: 20,
								colors: d3.scale.ordinal()
									.domain(["MALE", "FEMALE"])
									.range(["#1f77b4", "#ff7f0e"])
							});
						}
					});
				}

				drug_era.render = function (folder) {
					format_pct = d3.format('.2%');
					format_fixed = d3.format('.2f');
					format_comma = d3.format(',');

					$('#reportDrugEras svg').remove();

					width = 1000;
					height = 250;
					minimum_area = 50;
					threshold = minimum_area / (width * height);

					$.ajax({
						type: "GET",
						url: 'data/' + folder + '/drugera_treemap.json',
						contentType: "application/json; charset=utf-8",
						success: function (data) {
							table_data = data.CONCEPT_PATH.map(function (d, i) {
								conceptDetails = this.CONCEPT_PATH[i].split('||');
								return {
									concept_id: this.CONCEPT_ID[i],
									atc1: conceptDetails[0],
									atc3: conceptDetails[1],
									atc5: conceptDetails[2],
									ingredient: conceptDetails[3],
									num_persons: format_comma(this.NUM_PERSONS[i]),
									percent_persons: format_pct(this.PERCENT_PERSONS[i]),
									length_of_era: format_fixed(this.LENGTH_OF_ERA[i])
								}
							}, data);

							datatable = $('#drugera_table').DataTable({
								order: [ 5, 'desc' ],
								dom: 'Clfrtip',
								data: table_data,
								columns: [
									{
										data: 'concept_id',
										visible: false
									},
									{
										data: 'atc1'
									},
									{
										data: 'atc3',
										visible: false
									},
									{
										data: 'atc5'
									},
									{
										data: 'ingredient'
									},
									{
										data: 'num_persons',
										className: 'numeric'
									},
									{
										data: 'percent_persons',
										className: 'numeric'
									},
									{
										data: 'length_of_era',
										className: 'numeric'
									}
								],
								pageLength: 5,
								lengthChange: false,
								deferRender: true,
								destroy: true
							});

							$('#reportDrugEras').show();

							tree = buildHierarchyFromJSON(data, threshold);
							var treemap = new jnj_chart.treemap();
							treemap.render(tree, '#reportDrugEras #treemap_container', width, height, {
								onclick: function (node) {
									drug_era.drilldown(node.id, node.name)
								},
								getsizevalue: function (node) {
									return node.num_persons;
								},
								getcolorvalue: function (node) {
									return node.length_of_era;
								},
								gettitle: function (node) {
									title = '';
									steps = node.path.split('||');
									for (i = 0; i < steps.length; i++) {
										if (i == steps.length - 1) {
											title += '
';
											title += '
' + steps[i] + '
';
											title += '
Prevalence: ' + format_pct(node.pct_persons) + '
';
											title += '
Number of People: ' + format_comma(node.num_persons) + '
';
											title += '
Length of Era: ' + format_fixed(node.length_of_era) + '
';
										} else {
											title += '
' + Array(i + 1).join(' &nbsp') + steps[i] + '
';
										}
									}
									return title;
								}
							});
						}

					});
				}

				function buildHierarchyFromJSON(data, threshold) {
					var total = 0;

					var root = {
						"name": "root",
						"children": []
					};

					for (i = 0; i < data.PERCENT_PERSONS.length; i++) {
						total += data.PERCENT_PERSONS[i];
					}

					for (var i = 0; i < data.CONCEPT_PATH.length; i++) {
						var parts = data.CONCEPT_PATH[i].split("||");
						var currentNode = root;
						for (var j = 0; j < parts.length; j++) {
							var children = currentNode["children"];
							var nodeName = parts[j];
							var childNode;
							if (j + 1 < parts.length) {
								// Not yet at the end of the path; move down the tree.
								var foundChild = false;
								for (var k = 0; k < children.length; k++) {
									if (children[k]["name"] == nodeName) {
										childNode = children[k];
										foundChild = true;
										break;
									}
								}
								// If we don't already have a child node for this branch, create it.
								if (!foundChild) {
									childNode = {
										"name": nodeName,
										"children": []
									};
									children.push(childNode);
								}
								currentNode = childNode;
							} else {
								// Reached the end of the path; create a leaf node.
								childNode = {
									"name": nodeName,
									"num_persons": data.NUM_PERSONS[i],
									"id": data.CONCEPT_ID[i],
									"path": data.CONCEPT_PATH[i],
									"pct_persons": data.PERCENT_PERSONS[i],
									"length_of_era": data.LENGTH_OF_ERA[i]
								};

								// we only include nodes with sufficient size in the treemap display
								// sufficient size is configurable in the calculation of threshold
								// which is a function of the number of pixels in the treemap display
								if ((data.PERCENT_PERSONS[i] / total) > threshold) {
									children.push(childNode);
								}
							}
						}
					}
					return root;
				};
				return drug_era;
			});
		})();
<|endoftext|>


<commit_before>/**
 *  @file       index.js
 *  @brief      The entry function of the httpd.
 *  @author     blue8787 (blue8877@gmail.com)
 *  @date       10/03/2017 created.
 *  @date       11/10/2017 last modified.
 *  @version    0.1.0
 *  @copyright  MIT, (C) 2017 Yiwei Chiao
 *  @details
 *
 *  The entry function of the httpd.
 */
'use strict';

let http = require('http');

const routingTable = {
  '/': {
      url: '../htdocs/index.html',
      type: 'text/html'
    },
  '/styles.css': {
      url: '../htdocs/assets/css/styles.css',
      type: 'text/css'
    },
  '/SokobanClone_byVellidragon.png': {
      url: '../htdocs/assets/png/SokobanClone_byVellidragon.png',
      type: 'image/png'
    },
};

/**
  * 利用 http.ServerResponse 物件回傳檔案內容
  *
  * @name serve
  * @function
  * @param response - http.ServerResponse 物件
  * @param fname - 要回傳的檔案名
  * @param datatype - 回傳檔案內容的 Mine-Type
  * @returns {undefined}
  */
let serve = (response, fname, datatype) => {
  let fs = require('fs');

  fs.readFile(fname, (err, data) => {
    if (err) {
      console.log('檔案讀取錯誤');
    }
    else {
      response.writeHead(200, {
        'Content-Type': datatype
      });

      response.write(data);
      response.end();
    }
  });
};

http.createServer((request, response) => {
  let fs = require('fs');

  let postData = '';

  // 利用 'data' event 消耗掉 data chunk;
  // 'end' event 才會被 fired
  request.on('data', (chunk) => {
    postData += chunk;

    console.log(
      '接收的 POST data 片段k: [' + chunk + '].'
    );
  });

  request.on('end', () => {
    if (request.url in routingTable) {
      let obj = routingTable[request.url];

      serve(response, obj.url, obj.type);
    }
    else {
      console.log('未定義的存取: ' + request.url);

      response.end();
    }
  });
}).listen(8088);

// log message to Console
console.log('伺服器啓動，連線 url:  http://127.0.0.1:8088/');

// index.js
<commit_msg>1110-2<commit_after>/**
 *  @file       index.js
 *  @brief      The entry function of the httpd.
 *  @author     blue8787 (blue8877@gmail.com)
 *  @date       10/03/2017 created.
 *  @date       11/10/2017 last modified.
 *  @version    0.1.0
 *  @copyright  MIT, (C) 2017 Yiwei Chiao
 *  @details
 *
 *  The entry function of the httpd.
 */
'use strict';

let http = require('http');

const routingTable = {
  '/': {
      url: '../htdocs/index.html',
      type: 'text/html'
    },
  '/styles.css': {
      url: '../htdocs/assets/css/styles.css',
      type: 'text/css'
    },
  '/SokobanClone_byVellidragon.png': {
      url: '../htdocs/assets/png/SokobanClone_byVellidragon.png',
      type: 'image/png'
    },
};

/**
  * 利用 http.ServerResponse 物件回傳檔案內容
  *
  * @name serve
  * @function
  * @param response - http.ServerResponse 物件
  * @param fname - 要回傳的檔案名
  * @param datatype - 回傳檔案內容的 Mine-Type
  * @returns {undefined}
  */
let serve = (response, fname, datatype) => {
  let fs = require('fs');

  fs.readFile(fname, (err, data) => {
    if (err) {
      console.log('檔案讀取錯誤');
    }
    else {
      response.writeHead(200, {
        'Content-Type': datatype
      });

      response.write(data);
      response.end();
    }
  });
};

http.createServer((request, response) => {
  let fs = require('fs');

  let postData = '';

  // 利用 'data' event 消耗掉 data chunk;
  // 'end' event 才會被 fired
  request.on('data', (chunk) => {
    postData += chunk;

    console.log(
      '接收的 POST data 片段k: [' + chunk + '].'
    );
  });

  request.on('end', () => {
    if (request.url in routingTable) {
      let obj = routingTable[request.url];

      serve(response, obj.url, obj.type);
    }
    else {
      console.log('未定義的存取: ' + request.url);

      response.end();
    }
  });
}).listen(8080);

// log message to Console
console.log('伺服器啓動，連線 url:  http://127.0.0.1:8088/');

// index.js
<|endoftext|>


<commit_before>var $ = require('../')

$.import('Foundation')
$.import('ScriptingBridge')

var pool = $.NSAutoreleasePool('new')

var bi = $.NSString('stringWithUTF8String', 'com.apple.iTunes')
  , iTunes = $.SBApplication('applicationWithBundleIdentifier', bi)

console.log(iTunes.toString());
console.log(iTunes('name').toString());
<commit_msg>Update tests/sb<commit_after>var $ = require('../')

$.import('Foundation')
$.import('ScriptingBridge')

var pool = $.NSAutoreleasePool('new')

var bi = $.NSString('stringWithUTF8String', 'com.apple.iTunes')
  , iTunes = $.SBApplication('applicationWithBundleIdentifier', bi)

console.log(iTunes);
console.log(iTunes('name'));
<|endoftext|>


<commit_before>function pageViewModel(gvm) {
    // projecttitle
    gvm.projecttitle = ko.observable("");
    gvm.projectId = $("#projectHeader").data('value');

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);
    gvm.menuLanguage = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("MenuLanguage");}, gvm);
    gvm.savePage = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("SaveBtn");}, gvm);

    gvm.competences = ko.observableArray([]);

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + gvm.projectId, function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
        });
    };

    gvm.getAllData = function() {
         $.getJSON('/api/project/getAllData/' + gvm.projectId, function(data) {
            console.log(data);
         });
    };

    gvm.updateCompetence = function(id, code, description, max, weight) {
        var comp = new Competence(this, id, code, description, weight);
        gvm.competences.push(comp);
        return comp;
    };

    gvm.clearStructure = function() {
        gvm.competences.destroyAll();
    };
}

function initPage() {
    fetchProjectStructure();
    viewModel.getProjectInfo();

    $(".savePageBtn").click(function(){
        saveProjectScore();
    });
}

function fetchProjectScore()
{
    $.getJSON("/api/projectscore/" + projectid + "/" + studentid, function(data)
    {
        $.each(viewModel.competences(), function(i,item){
                    $.each(item.subcompetences(), function(i, subcomp)
                    {
                        $.each(subcomp.indicators(),function(i,indic)
                        {
                            for(var i = 0; i < data.length; i++) {
                            if (indic.id() == data[i].indicator) {
                                indic.score(data[i].score);
                                indic.scoreid(data[i].id);
                            }
                    }
                });
            });
        });
        console.log(viewModel.competences()[1].subcompetences()[0].indicators()[4].scoreid());

    });
}

function saveProjectScore()
{
    //console.log(viewModel.competences()[1].subcompetences()[0].indicators()[4].scoreid());
    //console.log(viewModel.competences()[0].subcompetences()[0].indicators()[0].score());
    $.ajax({
        type: "POST",
        url: "/api/projectscore/" + projectid + "/" + studentid,
        data: ko.toJSON(viewModel.competences),
        success: function(data){
            // TODO make multilangual and with modals
            alert("Saved projectscore to server");
            console.log(data);
            //window.location = "/assess/project/" + projectid + "/students";
        }
    });
}

function fetchProjectStructure() {
    viewModel.clearStructure();

    $.getJSON("/api/projectstructure/" + projectid, function(data){
        $.each(data, function(i, item){
            var competence = viewModel.updateCompetence(item.id, item.code, item.description);

            $.each(item.subcompetences, function(i, subcomp){
                var subcompetence = new SubCompetence(competence, subcomp.id, subcomp.code, subcomp.description);
                competence.subcompetences.push(subcompetence);

                $.each(subcomp.indicators, function(i, indic){
                    subcompetence.indicators.push(new Indicator(subcompetence, indic.id, indic.name, indic.description, 0,indic.id, indic.pointType));
                });
            });
        });
        fetchProjectScore();
    });
}

function Competence(viewmodel, id, code, name, subcompetences) {
    return {
        id: ko.observable(id),
        code: ko.observable(code),
        name: ko.observable(name),
        subcompetences: ko.observableArray(subcompetences)
    };
}

function SubCompetence(parent, id, code, name, indicators) {
    return {
        id: ko.observable(id),
        code: ko.observable(code),
        name: ko.observable(name),
        indicators: ko.observableArray(indicators)
    };
}

function Indicator(parent, id, name, description, score, scoreid,pointType, checked) {
    return {
        id: ko.observable(id),
        name: ko.observable(name),
        description: ko.observable(description),
        score: ko.observable(score),
        scoreid: ko.observable(scoreid || 0),
        pointType: ko.observable(pointType),
        checked : ko.observable(checked)
    };
}<commit_msg>Fixing save grades<commit_after>function pageViewModel(gvm) {
    // projecttitle
    gvm.projecttitle = ko.observable("");
    gvm.projectId = $("#projectHeader").data('value');

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);
    gvm.menuLanguage = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("MenuLanguage");}, gvm);
    gvm.savePage = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("SaveBtn");}, gvm);

    gvm.competences = ko.observableArray([]);

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + gvm.projectId, function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
        });
    };

    gvm.getAllData = function() {
         $.getJSON('/api/project/getAllData/' + gvm.projectId, function(data) {
            console.log(data);
         });
    };

    gvm.updateCompetence = function(id, code, description, max, weight) {
        var comp = new Competence(this, id, code, description, weight);
        gvm.competences.push(comp);
        return comp;
    };

    gvm.clearStructure = function() {
        gvm.competences.destroyAll();
    };
}

function initPage() {
    fetchProjectStructure();
    viewModel.getProjectInfo();

    $(".savePageBtn").click(function(){
        saveProjectScore();
    });
}

function fetchProjectScore()
{
    $.getJSON("/api/projectscore/" + projectid + "/" + studentid, function(data)
    {
        $.each(viewModel.competences(), function(i,item){
                    $.each(item.subcompetences(), function(i, subcomp)
                    {
                        $.each(subcomp.indicators(),function(i,indic)
                        {
                            for(var i = 0; i < data.length; i++) {
                                console.log(data[i].indicator);
                            if (indic.id() == data[i].indicator) {
                                indic.score(data[i].score);
                                indic.scoreid(data[i].id);
                            }
                    }
                });
            });
        });
        console.log(viewModel.competences()[1].subcompetences()[0].indicators()[4].scoreid());

    });
}

function saveProjectScore()
{
    //console.log(viewModel.competences()[1].subcompetences()[0].indicators()[4].scoreid());
    //console.log(viewModel.competences()[0].subcompetences()[0].indicators()[0].score());
    $.ajax({
        type: "POST",
        url: "/api/projectscore/" + projectid + "/" + studentid,
        data: ko.toJSON(viewModel.competences),
        success: function(data){
            // TODO make multilangual and with modals
            alert("Saved projectscore to server");
            console.log(data);
            //window.location = "/assess/project/" + projectid + "/students";
        }
    });
}

function fetchProjectStructure() {
    viewModel.clearStructure();

    $.getJSON("/api/projectstructure/" + projectid, function(data){
        $.each(data, function(i, item){
            var competence = viewModel.updateCompetence(item.id, item.code, item.description);

            $.each(item.subcompetences, function(i, subcomp){
                var subcompetence = new SubCompetence(competence, subcomp.id, subcomp.code, subcomp.description);
                competence.subcompetences.push(subcompetence);

                $.each(subcomp.indicators, function(i, indic){
                    subcompetence.indicators.push(new Indicator(subcompetence, indic.id, indic.name, indic.description, 0,indic.id, indic.pointType));
                });
            });
        });
        fetchProjectScore();
    });
}

function Competence(viewmodel, id, code, name, subcompetences) {
    return {
        id: ko.observable(id),
        code: ko.observable(code),
        name: ko.observable(name),
        subcompetences: ko.observableArray(subcompetences)
    };
}

function SubCompetence(parent, id, code, name, indicators) {
    return {
        id: ko.observable(id),
        code: ko.observable(code),
        name: ko.observable(name),
        indicators: ko.observableArray(indicators)
    };
}

function Indicator(parent, id, name, description, score, scoreid,pointType, checked) {
    return {
        id: ko.observable(id),
        name: ko.observable(name),
        description: ko.observable(description),
        score: ko.observable(score),
        scoreid: ko.observable(scoreid || 0),
        pointType: ko.observable(pointType),
        checked : ko.observable(checked)
    };
}<|endoftext|>


<commit_before>import { createElement as h, Component } from '../../src';

/** @jsx h */

/**
 * Setup the test environment
 * @returns {HTMLDivElement}
 */
export function setupScratch() {
	const scratch = document.createElement('div');
	(document.body || document.documentElement).appendChild(scratch);
	return scratch;
}

/**
 * Setup a rerender function that will drain the queue of pending renders
 * @returns {() => void}
 */
export function setupRerender() {
	Component.__test__previousDebounce = Component.debounce;
	Component.debounce = cb => Component.__test__drainQueue = cb;

	return () => Component.__test__drainQueue && Component.__test__drainQueue();
}

/**
 * Teardown test environment and reset preact's internal state
 * @param {HTMLDivElement} scratch
 */
export function teardown(scratch) {
	scratch.parentNode.removeChild(scratch);

	if (Component.__test__drainQueue) {
		// Flush any pending updates leftover by test
		Component.__test__drainQueue();
		delete Component.__test__drainQueue;
	}

	if (typeof Component.__test__previousDebounce !== 'undefined') {
		Component.debounce = Component.__test__previousDebounce;
		delete Component.__test__previousDebounce;
	}
}

const Foo = () => 'd';
export const getMixedArray = () => (
	// Make it a function so each test gets a new copy of the array
	[0, 'a', 'b', c, <Foo />, null, undefined, false, ['e', 'f'], 1]
);
export const mixedArrayHTML = '0abcdef1';

/**
 * Serialize an object
 * @param {Object} obj
 * @return {string}
 */
export function serialize(obj) {
	if (obj instanceof Text) return '#text';
	if (obj instanceof Element) return `<${obj.localName}>${obj.textContent}`;
	if (obj === document) return 'document';
	return Object.prototype.toString.call(obj).replace(/(^\[object |\]$)/g, '');
}

let log = {};

/**
 * Modify obj's original method to log calls and arguments on logger object
 * @param {object} obj
 * @param {string} method
 */
export function logCall(obj, method) {
	let old = obj[method];
	obj[method] = function() {
		let c = '';
		for (let i=0; i<arguments.length; i++) {
			if (c) c += ', ';
			c += serialize(arguments[i]);
		}
		const key = `${serialize(this)}.${method}(${c})`;
		log[key] = (log[key] || 0) + 1;
		return old.apply(this, arguments);
	};
}

/**
 * Return log object
 * @return {object} log
 */
export function getLog() {
	return log;
}

/** Clear log object */
export function clearLog() {
	log = {};
}

/**
 * Reset obj to empty to keep reference
 * @param {object} obj
 */
export function clear(obj) {
	Object.keys(obj).forEach(key => delete obj[key]);
}
<commit_msg>Add id to scratch element<commit_after>import { createElement as h, Component } from '../../src';

/** @jsx h */

/**
 * Setup the test environment
 * @returns {HTMLDivElement}
 */
export function setupScratch() {
	const scratch = document.createElement('div');
	scratch.id = 'scratch';
	(document.body || document.documentElement).appendChild(scratch);
	return scratch;
}

/**
 * Setup a rerender function that will drain the queue of pending renders
 * @returns {() => void}
 */
export function setupRerender() {
	Component.__test__previousDebounce = Component.debounce;
	Component.debounce = cb => Component.__test__drainQueue = cb;

	return () => Component.__test__drainQueue && Component.__test__drainQueue();
}

/**
 * Teardown test environment and reset preact's internal state
 * @param {HTMLDivElement} scratch
 */
export function teardown(scratch) {
	scratch.parentNode.removeChild(scratch);

	if (Component.__test__drainQueue) {
		// Flush any pending updates leftover by test
		Component.__test__drainQueue();
		delete Component.__test__drainQueue;
	}

	if (typeof Component.__test__previousDebounce !== 'undefined') {
		Component.debounce = Component.__test__previousDebounce;
		delete Component.__test__previousDebounce;
	}
}

const Foo = () => 'd';
export const getMixedArray = () => (
	// Make it a function so each test gets a new copy of the array
	[0, 'a', 'b', c, <Foo />, null, undefined, false, ['e', 'f'], 1]
);
export const mixedArrayHTML = '0abcdef1';

/**
 * Serialize an object
 * @param {Object} obj
 * @return {string}
 */
export function serialize(obj) {
	if (obj instanceof Text) return '#text';
	if (obj instanceof Element) return `<${obj.localName}>${obj.textContent}`;
	if (obj === document) return 'document';
	return Object.prototype.toString.call(obj).replace(/(^\[object |\]$)/g, '');
}

let log = {};

/**
 * Modify obj's original method to log calls and arguments on logger object
 * @param {object} obj
 * @param {string} method
 */
export function logCall(obj, method) {
	let old = obj[method];
	obj[method] = function() {
		let c = '';
		for (let i=0; i<arguments.length; i++) {
			if (c) c += ', ';
			c += serialize(arguments[i]);
		}
		const key = `${serialize(this)}.${method}(${c})`;
		log[key] = (log[key] || 0) + 1;
		return old.apply(this, arguments);
	};
}

/**
 * Return log object
 * @return {object} log
 */
export function getLog() {
	return log;
}

/** Clear log object */
export function clearLog() {
	log = {};
}

/**
 * Reset obj to empty to keep reference
 * @param {object} obj
 */
export function clear(obj) {
	Object.keys(obj).forEach(key => delete obj[key]);
}
<|endoftext|>


<commit_before>import { createElement as h, Component, options } from '../../src';
import { assign } from '../../src/util';
import { clearLog, getLog } from './logCall';

/** @jsx h */

const VOID_ELEMENTS = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;

const el = document.createElement('div');
const innerHtmlDef = Object.getOwnPropertyDescriptor(el.ownerDocument.defaultView.Element.prototype, 'innerHTML');
function encodeEntities(str) {
	el.textContent = str;
	return innerHtmlDef.get.call(el);
}

/**
 * Normalize svg paths spacing. Some browsers insert spaces around letters,
 * others do not.
 * @param {string} str
 * @returns {string}
 */
function normalizePath(str) {
	let len = str.length;
	let out = '';
	for (let i = 0; i < len; i++) {
		const char = str[i];
		if (/[A-Za-z]/.test(char)) {
			if (i==0) out+= char + ' ';
			else out+= (str[i-1]==' ' ? '' : ' ') + char + (i < len -1 ? ' ' : '');
		}
		else if (char=='-' && str[i-1]!==' ') out+= ' ' + char;
		else out += char;
	}

	return out.replace(/\s\s+/g, ' ').replace(/z/g, 'Z');
}

/**
 * Serialize a DOM tree.
 * Uses deterministic sorting where necessary to ensure consistent tests.
 * @param {Element|Node} node	The root node to serialize
 * @returns {String} html
 */
function serializeDomTree(node) {
	if (node.nodeType === 3) {
		return encodeEntities(node.data);
	}
	else if (node.nodeType === 8) {
		return '';
	}
	else if (node.nodeType === 1 || node.nodeType === 9) {
		let str = '<' + node.localName;
		const attrs = [];
		for (let i=0; i<node.attributes.length; i++) {
			attrs.push(node.attributes[i].name);
		}
		attrs.sort();
		for (let i=0; i<attrs.length; i++) {
			const name = attrs[i];
			let value = node.getAttribute(name);
			if (value == null) continue;
			if (!value && name==='class') continue;
			str += ' ' + name;
			value = encodeEntities(value);

			// normalize svg <path d="value">
			if (node.localName==='path' && name==='d') {
				value = normalizePath(value);
			}
			str += '="' + value + '"';
		}
		str += '>';
		if (!VOID_ELEMENTS.test(node.localName)) {
			let child = node.firstChild;
			while (child) {
				str += serializeDomTree(child);
				child = child.nextSibling;
			}
			str += '</' + node.localName + '>';
		}
		return str;
	}
}

function getInnerHTML() {
	let str = '';
	let child = this.firstChild;
	while (child) {
		str += serializeDomTree(child);
		child = child.nextSibling;
	}
	return str;
}

function setupDeterministicInnerHTML(element) {
	const doc = element.ownerDocument || window.document;
	const elementProto = doc.defaultView.Element.prototype;
	let def = Object.getOwnPropertyDescriptor(elementProto, 'innerHTML');
	if (def.get !== getInnerHTML) {
		def = Object.assign({}, def, { get: getInnerHTML });
		Object.defineProperty(elementProto, 'innerHTML', def);
	}
}

/**
 * Sort a cssText alphabetically.
 * @param {string} cssText
 */
export function sortCss(cssText) {
	return cssText.split(';')
		.filter(Boolean)
		.sort((a, b) => a[0]==='-' ? -1 : b[0]==='-' ? 1 : a - b)
		.join(';') + ';';
}

/**
 * Setup the test environment
 * @returns {HTMLDivElement}
 */
export function setupScratch() {
	const scratch = document.createElement('div');
	scratch.id = 'scratch';
	(document.body || document.documentElement).appendChild(scratch);
	setupDeterministicInnerHTML(scratch);
	return scratch;
}

let oldOptions = null;
export function clearOptions() {
	oldOptions = assign({}, options);
	delete options.vnode;
	delete options.diff;
	delete options.diffed;
	delete options.commit;
	delete options.unmount;
}

/**
 * Teardown test environment and reset preact's internal state
 * @param {HTMLDivElement} scratch
 */
export function teardown(scratch) {
	scratch.parentNode.removeChild(scratch);

	if (oldOptions != null) {
		assign(options, oldOptions);
		oldOptions = null;
	}

	if (Component.__test__drainQueue) {
		// Flush any pending updates leftover by test
		Component.__test__drainQueue();
		delete Component.__test__drainQueue;
	}

	if (typeof Component.__test__previousDebounce !== 'undefined') {
		options.debounceRendering = Component.__test__previousDebounce;
		delete Component.__test__previousDebounce;
	}

	if (getLog().length > 0) {
		clearLog();
	}
}

const Foo = () => 'd';
export const getMixedArray = () => (
	// Make it a function so each test gets a new copy of the array
	[0, 'a', 'b', c, <Foo />, null, undefined, false, ['e', 'f'], 1]
);
export const mixedArrayHTML = '0abcdef1';

/**
 * Reset obj to empty to keep reference
 * @param {object} obj
 */
export function clear(obj) {
	Object.keys(obj).forEach(key => delete obj[key]);
}

/**
 * Hacky normalization of attribute order across browsers.
 * @param {string} html
 */
export function sortAttributes(html) {
	return html.replace(/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi, (s, pre, attrs, after) => {
		let list = attrs.match(/\s[a-z0-9:_.-]+=".*?"/gi).sort( (a, b) => a>b ? 1 : -1 );
		if (~after.indexOf('/')) after = '></'+pre+'>';
		return '<' + pre + list.join('') + after;
	});
}
<commit_msg>Fix parentNode error in IE11<commit_after>import { createElement as h, Component, options } from '../../src';
import { assign } from '../../src/util';
import { clearLog, getLog } from './logCall';

/** @jsx h */

const VOID_ELEMENTS = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;

const el = document.createElement('div');
const innerHtmlDef = Object.getOwnPropertyDescriptor(el.ownerDocument.defaultView.Element.prototype, 'innerHTML');
function encodeEntities(str) {
	el.textContent = str;
	return innerHtmlDef.get.call(el);
}

/**
 * Normalize svg paths spacing. Some browsers insert spaces around letters,
 * others do not.
 * @param {string} str
 * @returns {string}
 */
function normalizePath(str) {
	let len = str.length;
	let out = '';
	for (let i = 0; i < len; i++) {
		const char = str[i];
		if (/[A-Za-z]/.test(char)) {
			if (i==0) out+= char + ' ';
			else out+= (str[i-1]==' ' ? '' : ' ') + char + (i < len -1 ? ' ' : '');
		}
		else if (char=='-' && str[i-1]!==' ') out+= ' ' + char;
		else out += char;
	}

	return out.replace(/\s\s+/g, ' ').replace(/z/g, 'Z');
}

/**
 * Serialize a DOM tree.
 * Uses deterministic sorting where necessary to ensure consistent tests.
 * @param {Element|Node} node	The root node to serialize
 * @returns {String} html
 */
function serializeDomTree(node) {
	if (node.nodeType === 3) {
		return encodeEntities(node.data);
	}
	else if (node.nodeType === 8) {
		return '';
	}
	else if (node.nodeType === 1 || node.nodeType === 9) {
		let str = '<' + node.localName;
		const attrs = [];
		for (let i=0; i<node.attributes.length; i++) {
			attrs.push(node.attributes[i].name);
		}
		attrs.sort();
		for (let i=0; i<attrs.length; i++) {
			const name = attrs[i];
			let value = node.getAttribute(name);
			if (value == null) continue;
			if (!value && name==='class') continue;
			str += ' ' + name;
			value = encodeEntities(value);

			// normalize svg <path d="value">
			if (node.localName==='path' && name==='d') {
				value = normalizePath(value);
			}
			str += '="' + value + '"';
		}
		str += '>';
		if (!VOID_ELEMENTS.test(node.localName)) {
			let child = node.firstChild;
			while (child) {
				str += serializeDomTree(child);
				child = child.nextSibling;
			}
			str += '</' + node.localName + '>';
		}
		return str;
	}
}

function getInnerHTML() {
	let str = '';
	let child = this.firstChild;
	while (child) {
		str += serializeDomTree(child);
		child = child.nextSibling;
	}
	return str;
}

function setupDeterministicInnerHTML(element) {
	const doc = element.ownerDocument || window.document;
	const elementProto = doc.defaultView.Element.prototype;
	let def = Object.getOwnPropertyDescriptor(elementProto, 'innerHTML');
	if (def.get !== getInnerHTML) {
		def = Object.assign({}, def, { get: getInnerHTML });
		Object.defineProperty(elementProto, 'innerHTML', def);
	}
}

/**
 * Sort a cssText alphabetically.
 * @param {string} cssText
 */
export function sortCss(cssText) {
	return cssText.split(';')
		.filter(Boolean)
		.sort((a, b) => a[0]==='-' ? -1 : b[0]==='-' ? 1 : a - b)
		.join(';') + ';';
}

/**
 * Setup the test environment
 * @returns {HTMLDivElement}
 */
export function setupScratch() {
	const scratch = document.createElement('div');
	scratch.id = 'scratch';
	(document.body || document.documentElement).appendChild(scratch);
	setupDeterministicInnerHTML(scratch);
	return scratch;
}

let oldOptions = null;
export function clearOptions() {
	oldOptions = assign({}, options);
	delete options.vnode;
	delete options.diff;
	delete options.diffed;
	delete options.commit;
	delete options.unmount;
}

/**
 * Teardown test environment and reset preact's internal state
 * @param {HTMLDivElement} scratch
 */
export function teardown(scratch) {
	if (scratch) {
		scratch.parentNode.removeChild(scratch);
	}

	if (oldOptions != null) {
		assign(options, oldOptions);
		oldOptions = null;
	}

	if (Component.__test__drainQueue) {
		// Flush any pending updates leftover by test
		Component.__test__drainQueue();
		delete Component.__test__drainQueue;
	}

	if (typeof Component.__test__previousDebounce !== 'undefined') {
		options.debounceRendering = Component.__test__previousDebounce;
		delete Component.__test__previousDebounce;
	}

	if (getLog().length > 0) {
		clearLog();
	}
}

const Foo = () => 'd';
export const getMixedArray = () => (
	// Make it a function so each test gets a new copy of the array
	[0, 'a', 'b', c, <Foo />, null, undefined, false, ['e', 'f'], 1]
);
export const mixedArrayHTML = '0abcdef1';

/**
 * Reset obj to empty to keep reference
 * @param {object} obj
 */
export function clear(obj) {
	Object.keys(obj).forEach(key => delete obj[key]);
}

/**
 * Hacky normalization of attribute order across browsers.
 * @param {string} html
 */
export function sortAttributes(html) {
	return html.replace(/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi, (s, pre, attrs, after) => {
		let list = attrs.match(/\s[a-z0-9:_.-]+=".*?"/gi).sort( (a, b) => a>b ? 1 : -1 );
		if (~after.indexOf('/')) after = '></'+pre+'>';
		return '<' + pre + list.join('') + after;
	});
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'com.google.ow',
  name: 'Client',

  requires: [
    'MDAO',
    'com.google.ow.IdGenerator',
    'com.google.ow.content.OrderStream',
    'com.google.ow.content.Video',
    'com.google.ow.dao.VideoOffloadDAO',
    'com.google.ow.examples.VideoB',
    'com.google.ow.model.ColorableProduct',
    'com.google.ow.model.Envelope',
    'com.google.ow.model.ProductAd',
    'com.google.ow.ui.EnvelopeCitationView',
    'com.google.ow.ui.EnvelopeDetailView',
    'com.google.ow.ui.MenuView',
    'com.google.ow.ui.OrderCitationView',
    'com.google.ow.ui.OrderDetailView',
    'com.google.ow.ui.OrderSummaryView',
    'com.google.ow.ui.UpdateStreamCitationView',
    'com.google.ow.ui.UpdateStreamDetailView',
    'com.google.plus.Circle',
    'com.google.plus.Person',
    'com.google.plus.ShareList',
    'foam.browser.BrowserConfig',
    'foam.browser.ui.DAOController',
    'foam.core.dao.AuthenticatedWebSocketDAO',
    'foam.dao.EasyClientDAO',
    'foam.dao.EasyDAO',
    'foam.dao.FutureDAO',
    'foam.dao.LoggingDAO',
    'foam.dao.ProxyDAO',
    'foam.dao.ProxyDAO',
    'foam.mlang.CannedQuery',
    'foam.mlang.PropertySequence',
    'foam.tutorials.todo.model.Todo',
    'foam.tutorials.todo.ui.TodoCitationView',
    'foam.u2.DAOListView',
    'foam.u2.md.DAOListView',
    'foam.u2.DetailView',
    'foam.u2.md.ActionButton',
    'foam.ui.DAOListView',
    'foam.ui.TextFieldView',
    'foam.ui.Tooltip',
    'foam.ui.md.CannedQueryCitationView',
    'foam.ui.md.CheckboxView',
    'foam.ui.md.PopupView',
    'foam.ui.md.UpdateDetailView',
    'com.google.ow.content.PreviewStream',

    // TODO(markdittmer): Bring this back once we fully u2-ify our MD styles.
    // 'foam.u2.md.SharedStyles',
  ],
  imports: [
    'document',
  ],
  exports: [
    'streamDAO',
    'personDAO',
    'circleDAO', // Note: proxy for currentUser.circles
    'contactsDAO', // Note: proxy for currentUser.contacts
    'currentUser$',
    'createStreamItem',
  ],

  properties: [
    {
      name: 'idGenerator',
      lazyFactory: function() {
        return this.IdGenerator.create(null, this.Y);
      },
    },
    ['titleText', 'Lifestream'],
    ['backgroundColor', '#ffffff'],
    ['headerColor', '#3e50b4'],
    {
      name: 'browserConfig',
      lazyFactory: function() {
        var UpdateDetailView = this.UpdateDetailView;
        var browserConfig = this.BrowserConfig.create({
          title$: this.titleText$,
          model: this.Envelope,
          backgroundColor: this.backgroundColor,
          headerColor: this.headerColor,
          dao: this.streamDAO.where(OR(
              NOT(HAS(this.Envelope.SID)),
              EQ(this.Envelope.PROMOTED, true))),
          listView: 'foam.u2.md.DAOListView',
          cannedQueryDAO: [
            this.CannedQuery.create({
              label: 'All',
              expression: TRUE,
            }),
          ],
          menuFactory: function() {
            var view = this.MenuView.create({ data: this }, this.Y);
            view.subscribe(view.MENU_CLOSE, function() {
              browserConfig.publish(browserConfig.MENU_CLOSE);
            });
            return view;
          }.bind(this),
          detailView: function(args, X) {
            var v = UpdateDetailView.create(args, X);
            v.title = (args.data && args.data.data) ? args.data.data.titleText
                        : this.title;
            v.liveEdit = true;
            return v;
          },
          innerDetailView: function(args, X) {
            // TODO(markdittmer): This should be more robust.
            var envelope = args.data || args.data$.get();
            return envelope.toDetailE ? envelope.toDetailE(X) :
                this.DetailView.create({ data: envelope }, X);
          }.bind(this),
          createFunction: function() { },
          showAdd: false,
        });
        return browserConfig;
      },
    },
    {
      model_: 'StringProperty',
      name: 'currentUserId',
      postSet: function(old, nu) {
        if ( old === nu ) return;
        if ( ! this.currentUser || nu !== this.currentUser.id ) {
          // There's a delay on boot that caused the fine() to fail. TODO: This listener is pointless
          // after boot, once we have the user loaded.
          this.personDAO.where(EQ(this.Person.ID, nu)).pipe({ put: function(user) {
            this.currentUser = user;
          }.bind(this) });
        }
      },
    },
    {
      name: 'currentUser',
      postSet: function(old, nu) {
        if ( old === nu ) return;
        if ( nu ) {
          if ( nu.id !== this.currentUserId ) this.currentUserId = nu.id;
          this.circleDAO.delegate = nu.circles;
          this.contactsDAO.delegate = nu.contacts;
        }
      }
    },
    {
      name: 'streamDAO',
      lazyFactory: function() {
        var dao = this.EasyDAO.create({
          daoType: 'MDAO',
          model: this.Envelope,
          guid: true,
          cloning: true,
          contextualize: true,
          dedup: true,
          sockets: true,
          syncWithServer: true,
          // logging: true,
        }).orderBy(this.Envelope.TIMESTAMP);
        dao.listen({
          put: function(e) {
            console.log('Put', e.toString());
          },
        });
        return dao;
      }
    },
    {
      name: 'personDAO',
      lazyFactory: function() {
        // TODO(markdittmer): This should be an authorized collection of peopl
        // the current user may know about.
        return this.EasyDAO.create({
          daoType: 'MDAO',
          model: this.Person,
          cloning: true,
          contextualize: true,
          dedup: true,
          sockets: true,
          syncWithServer: true,
        });
      }
    },
    {
      name: 'circleDAO',
      type: 'com.google.plus.Circle',
      factory: function() {
        return this.ProxyDAO.create({ model: this.Circle, delegate: [].dao }, this.Y);
      },
    },
    {
      name: 'contactsDAO',
      type: 'com.google.plus.Person',
      factory: function() {
        return this.ProxyDAO.create({ model: this.Person, delegate: [].dao }, this.Y);
      },
    },
    {
      model_: 'FunctionProperty',
      name: 'createStreamItem',
      hidden: true,
      factory: function() {
        return function(source, data, opt_sid) {
          var srcId = source.id || source;
          var tgtId = this.currentUser.id;
          return this.Envelope.create({
            owner: tgtId,
            source: srcId,
            data: data,
            sid: opt_sid || data.sid || '',
            substreams: data.substreams || [],
          });
        }.bind(this);
      },
    },

  ],

  methods: [
    function init() {
      this.Y.registerModel(this.ActionButton, 'foam.u2.ActionButton');
      this.SUPER();
      var WebSocket = this.AuthenticatedWebSocketDAO.xbind({
        authToken$: this.currentUserId$,
      });
      this.Y.registerModel(WebSocket, 'foam.core.dao.WebSocketDAO');

      // hack to fix missing updates when the server creates new streamDAO items
      // var pollEnvelope = this.Envelope.create({ owner: this.currentUser.id, id: 'pollEnv1212', data: {id:'fake' }, sid: 'fakeSID34343434434' });
//       this.X.setInterval(function() {
//         pollEnvelope.owner = this.currentUser.id;
//         this.streamDAO.put(pollEnvelope)
//       }.bind(this), 1000);

      // TODO(markdittmer): Bring this back once we fully u2-ify our MD styles.
      // this.SharedStyles.create(null, this.Y);
    },
  ],
});
<commit_msg>OW: Integrate new order views<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'com.google.ow',
  name: 'Client',

  requires: [
    'MDAO',
    'com.google.ow.IdGenerator',
    'com.google.ow.content.OrderStream',
    'com.google.ow.content.Video',
    'com.google.ow.dao.VideoOffloadDAO',
    'com.google.ow.examples.VideoB',
    'com.google.ow.model.ColorableProduct',
    'com.google.ow.model.Envelope',
    'com.google.ow.model.ProductAd',
    'com.google.ow.ui.CustomerOrderCitationView',
    'com.google.ow.ui.CustomerOrderDetailView',
    'com.google.ow.ui.CustomerOrderSummaryView',
    'com.google.ow.ui.EnvelopeCitationView',
    'com.google.ow.ui.EnvelopeDetailView',
    'com.google.ow.ui.MenuView',
    'com.google.ow.ui.MerchantOrderCitationView',
    'com.google.ow.ui.MerchantOrderDetailView',
    'com.google.ow.ui.MerchantOrderSummaryView',
    'com.google.ow.ui.UpdateStreamCitationView',
    'com.google.ow.ui.UpdateStreamDetailView',
    'com.google.plus.Circle',
    'com.google.plus.Person',
    'com.google.plus.ShareList',
    'foam.browser.BrowserConfig',
    'foam.browser.ui.DAOController',
    'foam.core.dao.AuthenticatedWebSocketDAO',
    'foam.dao.EasyClientDAO',
    'foam.dao.EasyDAO',
    'foam.dao.FutureDAO',
    'foam.dao.LoggingDAO',
    'foam.dao.ProxyDAO',
    'foam.dao.ProxyDAO',
    'foam.mlang.CannedQuery',
    'foam.mlang.PropertySequence',
    'foam.tutorials.todo.model.Todo',
    'foam.tutorials.todo.ui.TodoCitationView',
    'foam.u2.DAOListView',
    'foam.u2.DetailView',
    'foam.u2.md.ActionButton',
    'foam.u2.md.DAOListView',
    'foam.ui.DAOListView',
    'foam.ui.TextFieldView',
    'foam.ui.Tooltip',
    'foam.ui.md.CannedQueryCitationView',
    'foam.ui.md.CheckboxView',
    'foam.ui.md.PopupView',
    'foam.ui.md.UpdateDetailView',
    'com.google.ow.content.PreviewStream',

    // TODO(markdittmer): Bring this back once we fully u2-ify our MD styles.
    // 'foam.u2.md.SharedStyles',
  ],
  imports: [
    'document',
  ],
  exports: [
    'streamDAO',
    'personDAO',
    'circleDAO', // Note: proxy for currentUser.circles
    'contactsDAO', // Note: proxy for currentUser.contacts
    'currentUser$',
    'createStreamItem',
  ],

  properties: [
    {
      name: 'idGenerator',
      lazyFactory: function() {
        return this.IdGenerator.create(null, this.Y);
      },
    },
    ['titleText', 'Lifestream'],
    ['backgroundColor', '#ffffff'],
    ['headerColor', '#3e50b4'],
    {
      name: 'browserConfig',
      lazyFactory: function() {
        var UpdateDetailView = this.UpdateDetailView;
        var browserConfig = this.BrowserConfig.create({
          title$: this.titleText$,
          model: this.Envelope,
          backgroundColor: this.backgroundColor,
          headerColor: this.headerColor,
          dao: this.streamDAO.where(OR(
              NOT(HAS(this.Envelope.SID)),
              EQ(this.Envelope.PROMOTED, true))),
          listView: 'foam.u2.md.DAOListView',
          cannedQueryDAO: [
            this.CannedQuery.create({
              label: 'All',
              expression: TRUE,
            }),
          ],
          menuFactory: function() {
            var view = this.MenuView.create({ data: this }, this.Y);
            view.subscribe(view.MENU_CLOSE, function() {
              browserConfig.publish(browserConfig.MENU_CLOSE);
            });
            return view;
          }.bind(this),
          detailView: function(args, X) {
            var v = UpdateDetailView.create(args, X);
            v.title = (args.data && args.data.data) ? args.data.data.titleText
                        : this.title;
            v.liveEdit = true;
            return v;
          },
          innerDetailView: function(args, X) {
            // TODO(markdittmer): This should be more robust.
            var envelope = args.data || args.data$.get();
            return envelope.toDetailE ? envelope.toDetailE(X) :
                this.DetailView.create({ data: envelope }, X);
          }.bind(this),
          createFunction: function() { },
          showAdd: false,
        });
        return browserConfig;
      },
    },
    {
      model_: 'StringProperty',
      name: 'currentUserId',
      postSet: function(old, nu) {
        if ( old === nu ) return;
        if ( ! this.currentUser || nu !== this.currentUser.id ) {
          // There's a delay on boot that caused the fine() to fail. TODO: This listener is pointless
          // after boot, once we have the user loaded.
          this.personDAO.where(EQ(this.Person.ID, nu)).pipe({ put: function(user) {
            this.currentUser = user;
          }.bind(this) });
        }
      },
    },
    {
      name: 'currentUser',
      postSet: function(old, nu) {
        if ( old === nu ) return;
        if ( nu ) {
          if ( nu.id !== this.currentUserId ) this.currentUserId = nu.id;
          this.circleDAO.delegate = nu.circles;
          this.contactsDAO.delegate = nu.contacts;
        }
      }
    },
    {
      name: 'streamDAO',
      lazyFactory: function() {
        var dao = this.EasyDAO.create({
          daoType: 'MDAO',
          model: this.Envelope,
          guid: true,
          cloning: true,
          contextualize: true,
          dedup: true,
          sockets: true,
          syncWithServer: true,
          // logging: true,
        }).orderBy(this.Envelope.TIMESTAMP);
        dao.listen({
          put: function(e) {
            console.log('Put', e.toString());
          },
        });
        return dao;
      }
    },
    {
      name: 'personDAO',
      lazyFactory: function() {
        // TODO(markdittmer): This should be an authorized collection of peopl
        // the current user may know about.
        return this.EasyDAO.create({
          daoType: 'MDAO',
          model: this.Person,
          cloning: true,
          contextualize: true,
          dedup: true,
          sockets: true,
          syncWithServer: true,
        });
      }
    },
    {
      name: 'circleDAO',
      type: 'com.google.plus.Circle',
      factory: function() {
        return this.ProxyDAO.create({ model: this.Circle, delegate: [].dao }, this.Y);
      },
    },
    {
      name: 'contactsDAO',
      type: 'com.google.plus.Person',
      factory: function() {
        return this.ProxyDAO.create({ model: this.Person, delegate: [].dao }, this.Y);
      },
    },
    {
      model_: 'FunctionProperty',
      name: 'createStreamItem',
      hidden: true,
      factory: function() {
        return function(source, data, opt_sid) {
          var srcId = source.id || source;
          var tgtId = this.currentUser.id;
          return this.Envelope.create({
            owner: tgtId,
            source: srcId,
            data: data,
            sid: opt_sid || data.sid || '',
            substreams: data.substreams || [],
          });
        }.bind(this);
      },
    },

  ],

  methods: [
    function init() {
      this.Y.registerModel(this.ActionButton, 'foam.u2.ActionButton');
      this.SUPER();
      var WebSocket = this.AuthenticatedWebSocketDAO.xbind({
        authToken$: this.currentUserId$,
      });
      this.Y.registerModel(WebSocket, 'foam.core.dao.WebSocketDAO');

      // hack to fix missing updates when the server creates new streamDAO items
      // var pollEnvelope = this.Envelope.create({ owner: this.currentUser.id, id: 'pollEnv1212', data: {id:'fake' }, sid: 'fakeSID34343434434' });
//       this.X.setInterval(function() {
//         pollEnvelope.owner = this.currentUser.id;
//         this.streamDAO.put(pollEnvelope)
//       }.bind(this), 1000);

      // TODO(markdittmer): Bring this back once we fully u2-ify our MD styles.
      // this.SharedStyles.create(null, this.Y);
    },
  ],
});
<|endoftext|>


<commit_before>/*  Copyright (c) 2012 Sven "FuzzYspo0N" Bergström,
                  2013 Robert XD Hawkins

    written by : http://underscorediscovery.com
    written for : http://buildnewgames.com/real-time-multiplayer/

    modified for collective behavior experiments on Amazon Mechanical Turk

    MIT Licensed.
*/

/*
   THE FOLLOWING FUNCTIONS MAY NEED TO BE CHANGED
*/

// A window global for our game root variable.
var globalGame = {};
// Keeps track of whether player is paying attention...
var visible;
var active_keys = [];
var speed_change = "none";
var started = false;
var ending = false;

function getSelf () {
  return globalGame.get_player(globalGame.my_id);
};

function client_on_click(game, newX, newY ) {
  // Auto-correcting input, but only between rounds

  var self = getSelf();
  var oldX = self.pos.x;
  var oldY = self.pos.y;
  var dx = newX - oldX;
  var dy = newY - oldY;

  self.destination = {x : Math.round(newX), y : Math.round(newY)};
  self.angle = Math.round((Math.atan2(dy,dx) * 180 / Math.PI) + 90);

  var info_packet = ("c." + self.angle +
		     "."  + self.destination.x +
		     "."  + self.destination.y);
  game.socket.send(info_packet);
};

function client_ondisconnect(data) {
  // Redirect to exit survey
  console.log("server booted");
  var self = getSelf();
  var URL;
  if(self.kicked) {
    URL = 'http://projects.csail.mit.edu/ci/turk/forms/away.html';
  } else if(self.inactive) {
    URL = 'http://projects.csail.mit.edu/ci/turk/forms/error.html?id=' + this.my_id;
  } else if(self.lagging) {
    URL = 'http://projects.csail.mit.edu/ci/turk/forms/latency.html?id=' + this.my_id;
  } else {
    //URL = './index.html?id=' + this.my_id;
    //URL = 'http://projects.csail.mit.edu/ci/turk/forms/end.html?id=' + this.my_id;
    URL = 'http://projects.csail.mit.edu/ci/turk/forms/next.html?id=' + this.my_id;
  }
  //window.location.replace(URL);
};

function client_onserverupdate_received(data){
  // Update client versions of variables with data received from
  // server_send_update function in game.core.js
  if(data.players) {
    _.forEach(_.zip(data.players, globalGame.players), function(z){
      z[1].id = z[0].id;
      if (z[0].player == null) {
        z[1].player = null;
      } else {
        var s_player = z[0].player;
        var l_player = z[1].player;
        if(z[0].id != globalGame.my_id || l_player.angle == null) {
    	  l_player.angle = s_player.angle;
    	}
    	if(l_player.destination == null) {
    	  l_player.destination = s_player.destination;
    	}
        l_player.curr_background = s_player.cbg;
    	l_player.total_points = s_player.tot;
        l_player.pos = globalGame.pos(s_player.pos);
        l_player.speed = s_player.speed;
    	l_player.onwall = s_player.onwall;
        l_player.kicked = s_player.kicked;
        l_player.inactive = s_player.inactive;
        l_player.lagging = s_player.lagging;
      }
    });
  }

  globalGame.game_started = data.gs;
  globalGame.players_threshold = data.pt;
  globalGame.player_count = data.pc;
  globalGame.waiting_remaining = data.wr;

};

// This is where clients parse socket.io messages from the server. If
// you want to add another event (labeled 'x', say), just add another
// case here, then call

//          this.instance.player_host.send("s.x. <data>")

// The corresponding function where the server parses messages from
// clients, look for "server_onMessage" in game.server.js.
function client_onMessage(data) {

  var commands = data.split('.');
  var command = commands[0];
  var subcommand = commands[1] || null;
  var commanddata = commands[2] || null;

  switch(command) {
  case 's': //server message

    switch(subcommand) {
    case 'end' :
      // Redirect to exit survey
      console.log("received end message...");
      var URL = 'http://projects.csail.mit.edu/ci/turk/forms/end.html?id=' + this.my_id;
      window.location.replace(URL); break;
    case 'alert' : // Not in database, so you can't play...
      alert('You did not enter an ID');
      window.location.replace('http://nodejs.org'); break;
    case 'join' : //join a game requested
      var num_players = commanddata;
      client_onjoingame(num_players); break;
    case 'add_player' : // New player joined... Need to add them to our list.
      console.log("adding player" + commanddata);
      this.players.push({id: commanddata, player: new game_player(this,null,false)}); break;
    case 'begin_game' :
      client_newgame(); break;
    case 'blink' : //blink title
      flashTitle("GO!");  break;
    }
    break;
  }
};

// Restarts things on the client side. Necessary for iterated games.
function client_newgame() {
  started = true;
  client_countdown();
};

function client_countdown() {
  var self = getSelf();
  self.message = 'Begin in 3...';
  setTimeout(function(){self.message = 'Begin in 2...';},
             1000);
  setTimeout(function(){self.message = 'Begin in 1...';},
             2000);
  setTimeout(function(){
    self.message = 'GO!';
    globalGame.start_time = new Date();},
	     3000);
  setTimeout(function(){self.message = '';},
             4000);
}

function client_update() {
  var self = getSelf();

  //Clear the screen area
  globalGame.ctx.clearRect(0,0,485,280);

  // Alter speeds
  if (speed_change != "none") {
    self.speed = speed_change == "up" ? globalGame.max_speed : globalGame.min_speed;
    globalGame.socket.send("s." + String(self.speed).replace(/\./g,'-'));
    speed_change = "none";
  }

  //Draw opponent next
  _.map(globalGame.get_others(globalGame.my_id), function(p){
    draw_player(globalGame, p.player);
    draw_label(globalGame, p.player, "Player " + p.id.slice(0,4));
  });

  // Draw points scoreboard
  $("#cumulative_bonus").html("Total bonus so far: $" + (self.total_points).fixed(2));

  if(self.onwall) {
    $("#curr_bonus").html("Current Score: 0%");
  } else {
    if(globalGame.game_started) {
      $("#curr_bonus").html("Current Score: " + Math.floor(self.curr_background*100) + "%");
    } else {
      $("#curr_bonus").html("Current Score: ---");
    }
  }

  if(!started) {
    var left = timeRemaining(globalGame.waiting_remaining, globalGame.waiting_room_limit);
    var diff = globalGame.players_threshold - globalGame.player_count;
  }

  if(globalGame.game_started) {
    var left = new Date() - globalGame.start_time;
  //   if((globalGame.round_length*60 - Math.floor(left/1000)) < 6) {
  //     var remainder = globalGame.round_length*60 - Math.floor(left/1000);
  //     if(remainder < 0)
	// remainder = 0;
  //     self.message = 'Ending in ' + remainder;
  //   }
    left = timeRemaining(left, globalGame.round_length);
    // Draw time remaining
    $("#time").html("Time remaining: " + left['t'] + " " + left['unit']);
  } else {
    $("#time").html('You will join the game soon.');
  }

  //And then we draw ourself so we're always in front
  if(globalGame.game_started && self.pos) {
    draw_destination(globalGame, self);
    draw_player(globalGame, self);
    draw_label(globalGame, self, "YOU");
  }
};

var timeRemaining = function(remaining, limit) {
  var time_remaining = limit - Math.floor(remaining / (1000*60));
  if(time_remaining > 1) {
    return {t: time_remaining, unit: 'minutes', actual: (limit - remaining/1000)}
  } else {
    time_remaining = 60 * limit - Math.floor(remaining / 1000)
    //time_remaining = Math.max(Math.floor(time_remaining*6)*10, 10)
    return {t: time_remaining, unit: 'seconds', actual: (limit - remaining/1000)}
  }

}

var percentColors = [
  //    { pct: 0.0, color: { r: 0xff, g: 0x00, b: 0 } },
  { pct: 0.0, color: { r: 0xff, g: 0xff, b: 0xff } },
  { pct: 1.0, color: { r: 0x00, g: 0xff, b: 0 } } ];

var getColorForPercentage = function(pct) {
  for (var i = 0; i < percentColors.length; i++) {
    if (pct <= percentColors[i].pct) {
      var lower = percentColors[i - 1] || { pct: 0.1, color: { r: 0x0, g: 0x00, b: 0 } };
      var upper = percentColors[i];
      var range = upper.pct - lower.pct;
      var rangePct = (pct - lower.pct) / range;
      var pctLower = 1 - rangePct;
      var pctUpper = rangePct;
      var color = {
        r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper),
        g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper),
        b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper)
      };
      return 'rgb(' + [color.r, color.g, color.b].join(',') + ')';
    }
  }
}

/*
 The following code should NOT need to be changed
 */

// When loading the page, we store references to our
// drawing canvases, and initiate a game instance.
window.onload = function(){
  //Create our game client instance.
  globalGame = new game_core({server: false, numBots : 4});

  //Connect to the socket.io server!
  client_connect_to_server(globalGame);

  //Fetch the viewport
  globalGame.viewport = document.getElementById('viewport');

  //Adjust its size
  globalGame.viewport.width = globalGame.world.width;
  globalGame.viewport.height = globalGame.world.height;

  $('#viewport').click(function(e){
    e.preventDefault();
    // e.pageX is relative to whole page -- we want
    // relative to GAME WORLD (i.e. viewport)
    var offset = $(this).offset();
    var relX = e.pageX - offset.left;
    var relY = e.pageY - offset.top;
    client_on_click(globalGame, relX, relY);
  });

  KeyboardJS.on('space',
		function(){speed_change = "up";},
		function(){speed_change = "down";});

  //Fetch the rendering contexts
  globalGame.ctx = globalGame.viewport.getContext('2d');

  //Set the draw style for the font
  globalGame.ctx.font = '11px "Helvetica"';

  //Finally, start the loop
  globalGame.update();
};

// Associates callback functions corresponding to different socket messages
function client_connect_to_server(game) {
  //Store a local reference to our connection to the server
  game.socket = io.connect();

  //When we connect, we are not 'connected' until we have a server id
  //and are placed in a game by the server. The server sends us a message for that.
  game.socket.on('connect', function(){
    //        game.state = 'connecting';
  }.bind(game));

  // game.socket.on('ping', function(data){
  //   game.socket.send('pong.' + data.sendTime + "." + data.tick_num);
  // });
  //Sent when we are disconnected (network, server down, etc)
  game.socket.on('disconnect', client_ondisconnect.bind(game));
  //Sent each tick of the server simulation. This is our authoritive update
  game.socket.on('onserverupdate', client_onserverupdate_received.bind(game));
  //Handle when we connect to the server, showing state and storing id's.
  game.socket.on('onconnected', client_onconnected.bind(game));
  //On message from the server, we parse the commands and send it to the handlers
  game.socket.on('message', client_onMessage.bind(game));
};

function client_onconnected(data) {
  //The server responded that we are now in a game,
  //this lets us store the information about ourselves
  // so that we remember who we are.
  console.log("setting id to " + data.id);
  this.my_id = data.id;
  this.players[0].id = data.id;
};

function client_onjoingame(num_players) {
  // Set self color, leave others default white
  var self = getSelf();
  self.color = globalGame.self_color;
  self.speed = globalGame.min_speed;
  self.message = '';
  //self.message = 'Please remain active while you wait.';
};

// Automatically registers whether user has switched tabs...
(function() {
  document.hidden = hidden = "hidden";

  // Standards:
  if (hidden in document)
    document.addEventListener("visibilitychange", onchange);
  else if ((hidden = "mozHidden") in document)
    document.addEventListener("mozvisibilitychange", onchange);
  else if ((hidden = "webkitHidden") in document)
    document.addEventListener("webkitvisibilitychange", onchange);
  else if ((hidden = "msHidden") in document)
    document.addEventListener("msvisibilitychange", onchange);
  // IE 9 and lower:
  else if ('onfocusin' in document)
    document.onfocusin = document.onfocusout = onchange;
  // All others:
  else
    window.onpageshow = window.onpagehide = window.onfocus
    = window.onblur = onchange;
})();

function onchange (evt) {
  var v = 'visible', h = 'hidden',
      evtMap = {
        focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
      };
  evt = evt || window.event;
  if (evt.type in evtMap) {
    document.body.className = evtMap[evt.type];
  } else {
    document.body.className = evt.target.hidden ? "hidden" : "visible";
  }
  visible = document.body.className;
  globalGame.socket.send("h." + document.body.className);
};

// Flashes title to notify user that game has started
(function () {

  var original = document.title;
  var timeout;

  window.flashTitle = function (newMsg, howManyTimes) {
    function step() {
      document.title = (document.title == original) ? newMsg : original;
      if (visible == "hidden") {
        timeout = setTimeout(step, 500);
      } else {
        document.title = original;
      }
    };
    cancelFlashTitle(timeout);
    step();
  };

  window.cancelFlashTitle = function (timeout) {
    clearTimeout(timeout);
    document.title = original;
  };

}());
<commit_msg>Minor bug fix<commit_after>/*  Copyright (c) 2012 Sven "FuzzYspo0N" Bergström,
                  2013 Robert XD Hawkins

    written by : http://underscorediscovery.com
    written for : http://buildnewgames.com/real-time-multiplayer/

    modified for collective behavior experiments on Amazon Mechanical Turk

    MIT Licensed.
*/

/*
   THE FOLLOWING FUNCTIONS MAY NEED TO BE CHANGED
*/

// A window global for our game root variable.
var globalGame = {};
// Keeps track of whether player is paying attention...
var visible;
var active_keys = [];
var speed_change = "none";
var started = false;
var ending = false;

function getSelf () {
  return globalGame.get_player(globalGame.my_id);
};

function client_on_click(game, newX, newY ) {
  // Auto-correcting input, but only between rounds

  var self = getSelf();
  var oldX = self.pos.x;
  var oldY = self.pos.y;
  var dx = newX - oldX;
  var dy = newY - oldY;

  self.destination = {x : Math.round(newX), y : Math.round(newY)};
  self.angle = Math.round((Math.atan2(dy,dx) * 180 / Math.PI) + 90);

  var info_packet = ("c." + self.angle +
		     "."  + self.destination.x +
		     "."  + self.destination.y);
  game.socket.send(info_packet);
};

function client_ondisconnect(data) {
  // Redirect to exit survey
  console.log("server booted");
  var self = getSelf();
  var URL;
  if(self.kicked) {
    URL = 'http://projects.csail.mit.edu/ci/turk/forms/away.html';
  } else if(self.inactive) {
    URL = 'http://projects.csail.mit.edu/ci/turk/forms/error.html?id=' + this.my_id;
  } else if(self.lagging) {
    URL = 'http://projects.csail.mit.edu/ci/turk/forms/latency.html?id=' + this.my_id;
  } else {
    //URL = './index.html?id=' + this.my_id;
    //URL = 'http://projects.csail.mit.edu/ci/turk/forms/end.html?id=' + this.my_id;
    URL = 'http://projects.csail.mit.edu/ci/turk/forms/next.html?id=' + this.my_id;
  }
  window.location.replace(URL);
};

function client_onserverupdate_received(data){
  // Update client versions of variables with data received from
  // server_send_update function in game.core.js
  if(data.players) {
    _.forEach(_.zip(data.players, globalGame.players), function(z){
      z[1].id = z[0].id;
      if (z[0].player == null) {
        z[1].player = null;
      } else {
        var s_player = z[0].player;
        var l_player = z[1].player;
        if(z[0].id != globalGame.my_id || l_player.angle == null) {
    	  l_player.angle = s_player.angle;
    	}
    	if(l_player.destination == null) {
    	  l_player.destination = s_player.destination;
    	}
        l_player.curr_background = s_player.cbg;
    	l_player.total_points = s_player.tot;
        l_player.pos = globalGame.pos(s_player.pos);
        l_player.speed = s_player.speed;
    	l_player.onwall = s_player.onwall;
        l_player.kicked = s_player.kicked;
        l_player.inactive = s_player.inactive;
        l_player.lagging = s_player.lagging;
      }
    });
  }

  globalGame.game_started = data.gs;
  globalGame.players_threshold = data.pt;
  globalGame.player_count = data.pc;
  globalGame.waiting_remaining = data.wr;

};

// This is where clients parse socket.io messages from the server. If
// you want to add another event (labeled 'x', say), just add another
// case here, then call

//          this.instance.player_host.send("s.x. <data>")

// The corresponding function where the server parses messages from
// clients, look for "server_onMessage" in game.server.js.
function client_onMessage(data) {

  var commands = data.split('.');
  var command = commands[0];
  var subcommand = commands[1] || null;
  var commanddata = commands[2] || null;

  switch(command) {
  case 's': //server message

    switch(subcommand) {
    case 'end' :
      // Redirect to exit survey
      console.log("received end message...");
      var URL = 'http://projects.csail.mit.edu/ci/turk/forms/end.html?id=' + this.my_id;
      window.location.replace(URL); break;
    case 'alert' : // Not in database, so you can't play...
      alert('You did not enter an ID');
      window.location.replace('http://nodejs.org'); break;
    case 'join' : //join a game requested
      var num_players = commanddata;
      client_onjoingame(num_players); break;
    case 'add_player' : // New player joined... Need to add them to our list.
      console.log("adding player" + commanddata);
      this.players.push({id: commanddata, player: new game_player(this,null,false)}); break;
    case 'begin_game' :
      client_newgame(); break;
    case 'blink' : //blink title
      flashTitle("GO!");  break;
    }
    break;
  }
};

// Restarts things on the client side. Necessary for iterated games.
function client_newgame() {
  started = true;
  client_countdown();
};

function client_countdown() {
  var self = getSelf();
  self.message = 'Begin in 3...';
  setTimeout(function(){self.message = 'Begin in 2...';},
             1000);
  setTimeout(function(){self.message = 'Begin in 1...';},
             2000);
  setTimeout(function(){
    self.message = 'GO!';
    globalGame.start_time = new Date();},
	     3000);
  setTimeout(function(){self.message = '';},
             4000);
}

function client_update() {
  var self = getSelf();

  //Clear the screen area
  globalGame.ctx.clearRect(0,0,485,280);

  // Alter speeds
  if (speed_change != "none") {
    self.speed = speed_change == "up" ? globalGame.max_speed : globalGame.min_speed;
    globalGame.socket.send("s." + String(self.speed).replace(/\./g,'-'));
    speed_change = "none";
  }

  //Draw opponent next
  _.map(globalGame.get_others(globalGame.my_id), function(p){
    draw_player(globalGame, p.player);
    draw_label(globalGame, p.player, "Player " + p.id.slice(0,4));
  });

  // Draw points scoreboard
  $("#cumulative_bonus").html("Total bonus so far: $" + (self.total_points).fixed(2));

  if(self.onwall) {
    $("#curr_bonus").html("Current Score: 0%");
  } else {
    if(globalGame.game_started) {
      $("#curr_bonus").html("Current Score: " + Math.floor(self.curr_background*100) + "%");
    } else {
      $("#curr_bonus").html("Current Score: ---");
    }
  }

  if(!started) {
    var left = timeRemaining(globalGame.waiting_remaining, globalGame.waiting_room_limit);
    var diff = globalGame.players_threshold - globalGame.player_count;
  }

  if(globalGame.game_started) {
    var left = new Date() - globalGame.start_time;
  //   if((globalGame.round_length*60 - Math.floor(left/1000)) < 6) {
  //     var remainder = globalGame.round_length*60 - Math.floor(left/1000);
  //     if(remainder < 0)
	// remainder = 0;
  //     self.message = 'Ending in ' + remainder;
  //   }
    left = timeRemaining(left, globalGame.round_length);
    // Draw time remaining
    $("#time").html("Time remaining: " + left['t'] + " " + left['unit']);
  } else {
    $("#time").html('You will join the game soon.');
  }

  //And then we draw ourself so we're always in front
  if(globalGame.game_started && self.pos) {
    draw_destination(globalGame, self);
    draw_player(globalGame, self);
    draw_label(globalGame, self, "YOU");
  }
};

var timeRemaining = function(remaining, limit) {
  var time_remaining = limit - Math.floor(remaining / (1000*60));
  if(time_remaining > 1) {
    return {t: time_remaining, unit: 'minutes', actual: (limit - remaining/1000)}
  } else {
    time_remaining = 60 * limit - Math.floor(remaining / 1000)
    //time_remaining = Math.max(Math.floor(time_remaining*6)*10, 10)
    return {t: time_remaining, unit: 'seconds', actual: (limit - remaining/1000)}
  }

}

var percentColors = [
  //    { pct: 0.0, color: { r: 0xff, g: 0x00, b: 0 } },
  { pct: 0.0, color: { r: 0xff, g: 0xff, b: 0xff } },
  { pct: 1.0, color: { r: 0x00, g: 0xff, b: 0 } } ];

var getColorForPercentage = function(pct) {
  for (var i = 0; i < percentColors.length; i++) {
    if (pct <= percentColors[i].pct) {
      var lower = percentColors[i - 1] || { pct: 0.1, color: { r: 0x0, g: 0x00, b: 0 } };
      var upper = percentColors[i];
      var range = upper.pct - lower.pct;
      var rangePct = (pct - lower.pct) / range;
      var pctLower = 1 - rangePct;
      var pctUpper = rangePct;
      var color = {
        r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper),
        g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper),
        b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper)
      };
      return 'rgb(' + [color.r, color.g, color.b].join(',') + ')';
    }
  }
}

/*
 The following code should NOT need to be changed
 */

// When loading the page, we store references to our
// drawing canvases, and initiate a game instance.
window.onload = function(){
  //Create our game client instance.
  globalGame = new game_core({server: false, numBots : 4});

  //Connect to the socket.io server!
  client_connect_to_server(globalGame);

  //Fetch the viewport
  globalGame.viewport = document.getElementById('viewport');

  //Adjust its size
  globalGame.viewport.width = globalGame.world.width;
  globalGame.viewport.height = globalGame.world.height;

  $('#viewport').click(function(e){
    e.preventDefault();
    // e.pageX is relative to whole page -- we want
    // relative to GAME WORLD (i.e. viewport)
    var offset = $(this).offset();
    var relX = e.pageX - offset.left;
    var relY = e.pageY - offset.top;
    client_on_click(globalGame, relX, relY);
  });

  KeyboardJS.on('space',
		function(){speed_change = "up";},
		function(){speed_change = "down";});

  //Fetch the rendering contexts
  globalGame.ctx = globalGame.viewport.getContext('2d');

  //Set the draw style for the font
  globalGame.ctx.font = '11px "Helvetica"';

  //Finally, start the loop
  globalGame.update();
};

// Associates callback functions corresponding to different socket messages
function client_connect_to_server(game) {
  //Store a local reference to our connection to the server
  game.socket = io.connect();

  //When we connect, we are not 'connected' until we have a server id
  //and are placed in a game by the server. The server sends us a message for that.
  game.socket.on('connect', function(){
    //        game.state = 'connecting';
  }.bind(game));

  // game.socket.on('ping', function(data){
  //   game.socket.send('pong.' + data.sendTime + "." + data.tick_num);
  // });
  //Sent when we are disconnected (network, server down, etc)
  game.socket.on('disconnect', client_ondisconnect.bind(game));
  //Sent each tick of the server simulation. This is our authoritive update
  game.socket.on('onserverupdate', client_onserverupdate_received.bind(game));
  //Handle when we connect to the server, showing state and storing id's.
  game.socket.on('onconnected', client_onconnected.bind(game));
  //On message from the server, we parse the commands and send it to the handlers
  game.socket.on('message', client_onMessage.bind(game));
};

function client_onconnected(data) {
  //The server responded that we are now in a game,
  //this lets us store the information about ourselves
  // so that we remember who we are.
  console.log("setting id to " + data.id);
  this.my_id = data.id;
  this.players[0].id = data.id;
};

function client_onjoingame(num_players) {
  // Set self color, leave others default white
  var self = getSelf();
  self.color = globalGame.self_color;
  self.speed = globalGame.min_speed;
  self.message = '';
  //self.message = 'Please remain active while you wait.';
};

// Automatically registers whether user has switched tabs...
(function() {
  document.hidden = hidden = "hidden";

  // Standards:
  if (hidden in document)
    document.addEventListener("visibilitychange", onchange);
  else if ((hidden = "mozHidden") in document)
    document.addEventListener("mozvisibilitychange", onchange);
  else if ((hidden = "webkitHidden") in document)
    document.addEventListener("webkitvisibilitychange", onchange);
  else if ((hidden = "msHidden") in document)
    document.addEventListener("msvisibilitychange", onchange);
  // IE 9 and lower:
  else if ('onfocusin' in document)
    document.onfocusin = document.onfocusout = onchange;
  // All others:
  else
    window.onpageshow = window.onpagehide = window.onfocus
    = window.onblur = onchange;
})();

function onchange (evt) {
  var v = 'visible', h = 'hidden',
      evtMap = {
        focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
      };
  evt = evt || window.event;
  if (evt.type in evtMap) {
    document.body.className = evtMap[evt.type];
  } else {
    document.body.className = evt.target.hidden ? "hidden" : "visible";
  }
  visible = document.body.className;
  globalGame.socket.send("h." + document.body.className);
};

// Flashes title to notify user that game has started
(function () {

  var original = document.title;
  var timeout;

  window.flashTitle = function (newMsg, howManyTimes) {
    function step() {
      document.title = (document.title == original) ? newMsg : original;
      if (visible == "hidden") {
        timeout = setTimeout(step, 500);
      } else {
        document.title = original;
      }
    };
    cancelFlashTitle(timeout);
    step();
  };

  window.cancelFlashTitle = function (timeout) {
    clearTimeout(timeout);
    document.title = original;
  };

}());
<|endoftext|>