<commit_before>const base = require('./_base')
const path = require('path')
const url = require('url')
const { tail } = _

function render(data) {
  const events = []

  if (data.typing) {
    events.push({
      type: 'typing',
      value: data.typing
    })
  }

  return [
    ...events,
    {
      type: 'file',
      title: data.title,
      url: url.resolve(data.BOT_URL, data.image)
    }
  ]
}

function renderMessenger(data) {
  const events = []

  if (data.typing) {
    events.push({
      type: 'typing',
      value: data.typing
    })
  }

  return [
    ...events,
    {
      attachment: {
        type: 'image',
        payload: {
          is_reusable: true,
          url: url.resolve(data.BOT_URL, data.image)
        }
      }
    }
  ]
}

function renderTelegram(data) {
  const events = []

  if (data.typing) {
    events.push({
      type: 'typing',
      value: data.typing
    })
  }

  return [
    ...events,
    {
      type: 'image',
      url: url.resolve(data.BOT_URL, data.image)
    }
  ]
}

function renderSlack(data) {
  const events = []

  if (data.typing) {
    events.push({
      type: 'typing',
      value: data.typing
    })
  }

  return [
    ...events,
    {
      type: 'image',
      title: data.title && {
        type: 'plain_text',
        text: data.title
      },
      image_url: url.resolve(data.BOT_URL, data.image),
      alt_text: 'image'
    }
  ]
}

function renderTeams(data) {
  const events = []

  if (data.typing) {
    events.push({
      type: 'typing'
    })
  }

  return [
    ...events,
    {
      type: 'message',
      attachments: [
        {
          name: data.title,
          contentType: 'image/png',
          contentUrl: url.resolve(data.BOT_URL, data.image)
        }
      ]
    }
  ]
}

function renderElement(data, channel) {
  if (channel === 'messenger') {
    return renderMessenger(data)
  } else if (channel === 'telegram') {
    return renderTelegram(data)
  } else if (channel === 'slack') {
    return renderSlack(data)
  } else if (channel === 'teams') {
    return renderTeams(data)
  } else {
    return render(data)
  }
}

module.exports = {
  id: 'builtin_image',
  group: 'Built-in Messages',
  title: 'Image',

  jsonSchema: {
    description: 'A message showing an image with an optional title',
    type: 'object',
    required: ['image'],
    properties: {
      image: {
        type: 'string',
        $subtype: 'media',
        $filter: '.jpg, .png, .jpeg, .gif, .bmp, .tif, .tiff|image/*',
        title: 'Image'
      },
      title: {
        type: 'string',
        description: 'Some platforms require to name the images.',
        title: 'Title (optional)'
      },
      ...base.typingIndicators
    }
  },

  uiSchema: {
    title: {
      'ui:field': 'i18n_field'
    }
  },

  computePreviewText: formData => {
    if (!formData.image) {
      return
    }

    let fileName = path.basename(formData.image)
    if (fileName.includes('-')) {
      fileName = tail(fileName.split('-')).join('-')
    }
    const title = formData.title ? ' | ' + formData.title : ''

    return `Image [![${formData.title || ''}](${formData.image})](${formData.image}) - (${fileName}) ${title}`
  },

  renderElement: renderElement
}
<commit_msg>fix(cms): fix image preview when name has space<commit_after>const base = require('./_base')
const path = require('path')
const url = require('url')
const { tail } = _

function render(data) {
  const events = []

  if (data.typing) {
    events.push({
      type: 'typing',
      value: data.typing
    })
  }

  return [
    ...events,
    {
      type: 'file',
      title: data.title,
      url: url.resolve(data.BOT_URL, data.image)
    }
  ]
}

function renderMessenger(data) {
  const events = []

  if (data.typing) {
    events.push({
      type: 'typing',
      value: data.typing
    })
  }

  return [
    ...events,
    {
      attachment: {
        type: 'image',
        payload: {
          is_reusable: true,
          url: url.resolve(data.BOT_URL, data.image)
        }
      }
    }
  ]
}

function renderTelegram(data) {
  const events = []

  if (data.typing) {
    events.push({
      type: 'typing',
      value: data.typing
    })
  }

  return [
    ...events,
    {
      type: 'image',
      url: url.resolve(data.BOT_URL, data.image)
    }
  ]
}

function renderSlack(data) {
  const events = []

  if (data.typing) {
    events.push({
      type: 'typing',
      value: data.typing
    })
  }

  return [
    ...events,
    {
      type: 'image',
      title: data.title && {
        type: 'plain_text',
        text: data.title
      },
      image_url: url.resolve(data.BOT_URL, data.image),
      alt_text: 'image'
    }
  ]
}

function renderTeams(data) {
  const events = []

  if (data.typing) {
    events.push({
      type: 'typing'
    })
  }

  return [
    ...events,
    {
      type: 'message',
      attachments: [
        {
          name: data.title,
          contentType: 'image/png',
          contentUrl: url.resolve(data.BOT_URL, data.image)
        }
      ]
    }
  ]
}

function renderElement(data, channel) {
  if (channel === 'messenger') {
    return renderMessenger(data)
  } else if (channel === 'telegram') {
    return renderTelegram(data)
  } else if (channel === 'slack') {
    return renderSlack(data)
  } else if (channel === 'teams') {
    return renderTeams(data)
  } else {
    return render(data)
  }
}

module.exports = {
  id: 'builtin_image',
  group: 'Built-in Messages',
  title: 'Image',

  jsonSchema: {
    description: 'A message showing an image with an optional title',
    type: 'object',
    required: ['image'],
    properties: {
      image: {
        type: 'string',
        $subtype: 'media',
        $filter: '.jpg, .png, .jpeg, .gif, .bmp, .tif, .tiff|image/*',
        title: 'Image'
      },
      title: {
        type: 'string',
        description: 'Some platforms require to name the images.',
        title: 'Title (optional)'
      },
      ...base.typingIndicators
    }
  },

  uiSchema: {
    title: {
      'ui:field': 'i18n_field'
    }
  },

  computePreviewText: formData => {
    if (!formData.image) {
      return
    }

    let fileName = path.basename(formData.image)
    if (fileName.includes('-')) {
      fileName = tail(fileName.split('-')).join('-')
    }
    const title = formData.title ? ' | ' + formData.title : ''
    return `Image: [![${formData.title || ''}](<${formData.image}>)](<${formData.image}>) - (${fileName}) ${title}`
  },

  renderElement: renderElement
}
<|endoftext|>


<commit_before>var WebElementRecorder = function() {};
WebElementRecorder.prototype.findInArray = function (ar, el) {
	var len = ar.length;
	for (var i = 0; i < len; i++) {
		if (ar[i] == el) return i;
	}
	return -1;
};
WebElementRecorder.prototype.getElementsByTagName = function(tagName, doc){
    return doc.getElementsByTagName(tagName.toLowerCase());
};
WebElementRecorder.prototype.getXPath = function(element) {
	var n = element;
	var s = "";
	while (true){
	    var p = n.parentNode;
	    if (p == n || p == null) break;
	    var ix = this.findInArray(this.getElementsByTagName(n.tagName, p), n);
	    var sfx = ix > 0 ? "["+(ix+1)+"]" : "";
	    s = n.tagName.toLowerCase() + sfx + (s == "" ? "" : ("/" + s));
	    n = p;
	}
	return "/" + s;
};
WebElementRecorder.prototype.onClickCallback = function() {
	var event = arguments[0];
	prompt(event.type + ";,local.clickOnWebElement(By.xpath(\"" + record.getXPath(event.target) + "\"));");
};
WebElementRecorder.prototype.onChangeCallback = function() {
	var event = arguments[0];
	prompt(event.type + ";,local.enterTextInWebElement(By.xpath(\"" + record.getXPath(event.target) + "\"), \"" + event.target.value + "\");");
};
WebElementRecorder.prototype.finished = function() {
	prompt('WebElementRecorder-finished');
}
document.removeEventListener('click', WebElementRecorder.prototype.onClickCallback);
document.removeEventListener('change', WebElementRecorder.prototype.onChangeCallback);

document.addEventListener('click', WebElementRecorder.prototype.onClickCallback, false);
document.addEventListener('change', WebElementRecorder.prototype.onChangeCallback, false);
<commit_msg>Update WebElementRecorder.js<commit_after>function findInArray(ar, el) {
	var len = ar.length;
	for (var i = 0; i < len; i++) {
		if (ar[i] == el) return i;
	}
	return -1;
}
function getElementsByTagName(tagName, doc){
    return doc.getElementsByTagName(tagName.toLowerCase());
}
function getXPath(element) {
	var n = element;
	var s = "";
	while (true){
	    var p = n.parentNode;
	    if (p == n || p == null) break;
	    var ix = this.findInArray(this.getElementsByTagName(n.tagName, p), n);
	    var sfx = ix > 0 ? "["+(ix+1)+"]" : "";
	    s = n.tagName.toLowerCase() + sfx + (s == "" ? "" : ("/" + s));
	    n = p;
	}
	return "/" + s;
}
function onClickCallback() {
	var event = arguments[0];
	prompt(event.type + ";,local.clickOnWebElement(By.xpath(\"" + getXPath(event.target) + "\"));");
	finished();
};
function onChangeCallback() {
	var event = arguments[0];
	prompt(event.type + ";,local.enterTextInWebElement(By.xpath(\"" + getXPath(event.target) + "\"), \"" + event.target.value + "\");");
	finished();
};
function finished() {
	prompt('WebElementRecorder-finished');
};

document.removeEventListener('click', onClickCallback);
document.removeEventListener('change', onChangeCallback);

document.addEventListener('click', onClickCallback, false);
document.addEventListener('change', onChangeCallback, false);
<|endoftext|>


<commit_before>{
	"translatorID":"cde4428-5434-437f-9cd9-2281d14dbf9",
	"translatorType":4,
	"label":"Ovid",
	"creator":"Simon Kornblith and Michael Berkowitz",
	"target":"/(gw2|spa|spb|sp....)/ovidweb\\.cgi",
	"minVersion":"1.0.0b3.r1",
	"maxVersion":"",
	"priority":100,
	"inRepository":true,
	"lastUpdated":"2009-12-23 18:00:00"
}

function detectWeb(doc, url) {
	var namespace = doc.documentElement.namespaceURI;
	var nsResolver = namespace ? function(prefix) {
		if (prefix == 'x') return namespace; else return null;
	} : null;

	var results = doc.evaluate('//div[@class="bibheader-resultsrange"]/b', doc, nsResolver,
		XPathResult.ANY_TYPE, null).iterateNext();

	if(!doc.evaluate('//span[contains(./text(), "Results Manager")]', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext()) {
		return false;
	}

	if(results) {
		results = Zotero.Utilities.trimInternal(results.textContent);
		if(results.indexOf("-") != -1) {
			return "multiple";
		} else {
			return "journalArticle";
		}
	}

	return false;
}

function senCase(string) {
	var words = string.split(/\b/);
	for (var i = 0 ; i < words.length ; i++) {
		if (words[i].match(/[A-Z]/)) {
			words[i] = words[i][0] + words[i].substring(1).toLowerCase();
		}
	}
	return words.join("");
}

function doWeb(doc, url) {
	var namespace = doc.documentElement.namespaceURI;
	var nsResolver = namespace ? function(prefix) {
		if (prefix == 'x') return namespace; else return null;
	} : null;

	var results = Zotero.Utilities.cleanString(doc.evaluate('//div[@class="bibheader-resultsrange"]/b', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext().textContent);
	var post = "S="+doc.evaluate('.//input[@name="S"]', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext().value;

	if(results.indexOf("-") != -1) {
		var items = new Object();

		// Go through table rows
		if (doc.evaluate('/html/body/form/div[substring(@class, 1, 10)="titles-row"]', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext()) {
			var tableRows = doc.evaluate('/html/body/form/div[substring(@class, 1, 10)="titles-row"]', doc, nsResolver, XPathResult.ANY_TYPE, null);
		} else if (doc.evaluate('//div[@id="titles-records"]/table[@class="titles-row"]', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext()) {
			var tableRows = doc.evaluate('//div[@id="titles-records"]/table[@class="titles-row"]', doc, nsResolver, XPathResult.ANY_TYPE, null);
		}
		var tableRow;
		while(tableRow = tableRows.iterateNext()) {
			var id = doc.evaluate('.//input[@name="R"]', tableRow, nsResolver, XPathResult.ANY_TYPE,
				null).iterateNext().value;
			items[id] = Zotero.Utilities.cleanString(doc.evaluate('.//span[@class="titles-title"]', tableRow,
				nsResolver, XPathResult.ANY_TYPE, null).iterateNext().textContent);
		}

		var items = Zotero.selectItems(items);
		if(!items) return true;

		for(var i in items) {
			post += "&R="+i;
		}
	} else {
		var id = doc.evaluate('.//input[@name="R"]', doc, nsResolver, XPathResult.ANY_TYPE,
			null).iterateNext().value;
		post += "&R="+id;
	}

	if (detectWeb(doc, url) == "multiple") {
		var selectvar = doc.evaluate('.//input[@name="SELECT"]', doc, nsResolver, XPathResult.ANY_TYPE, null);
		var nextselect = selectvar.iterateNext().value;
		if (next = selectvar.iterateNext()) {
			post += "&SELECT=" + next.value;
		} else {
			post += "&SELECT="+ nextselect;
		}
	} else {
		post += "&SELECT=" + doc.evaluate('.//input[@name="SELECT"]', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext().value;
	}
	post += "&CitMan="+doc.evaluate('.//input[@name="CitMan"]', doc, nsResolver, XPathResult.ANY_TYPE,
		null).iterateNext().value;
	post += "&CitManPrev="+doc.evaluate('.//input[@name="CitManPrev"]', doc, nsResolver, XPathResult.ANY_TYPE,
		null).iterateNext().value;
	post += "&cmRecordSelect=SELECTED&cmFields=ALL&cmFormat=export&cmsave.x=12&cmsave.y=7&doSave=1";

	Zotero.Utilities.HTTP.doPost(url, post, function(text) {
		var lines = text.split("\n");
		var haveStarted = false;
		var newItemRe = /^<[0-9]+>/;

		var newItem = new Zotero.Item("journalArticle");

		for(var i in lines) {
			if(lines[i].substring(0,3) == "<1>") {
				haveStarted = true;
			} else if(newItemRe.test(lines[i])) {
				newItem.complete();

				newItem = new Zotero.Item("journalArticle");
			} else if(lines[i].substr(2, 4) == "  - " && haveStarted) {
				var fieldCode = lines[i].substr(0, 2);
				var fieldContent = Zotero.Utilities.cleanString(lines[i].substr(6));
				if(fieldCode == "TI") {
					newItem.title = fieldContent.replace(/\. \[\w+\]$/, "");
				} else if(fieldCode == "AU") {
					var names = fieldContent.split(", ");

					if(names.length >= 2) {
						// get rid of the weird field codes
						if(names.length == 2) {
							names[1] = names[1].replace(/ [\+\*\S\[\]]+$/, "");
						}
						names[1] = names[1].replace(/ (?:MD|PhD|[BM]Sc|[BM]A|MPH|MB)$/i, "");

						newItem.creators.push({firstName:names[1], lastName:names[0], creatorType:"author"});
					} else if (fieldContent.match(/^(.*) [A-Z]{1,3}$/)) {
						names = fieldContent.match(/^(.*) ([A-Z]{1,3})$/);
					  	newItem.creators.push({firstName:names[2], lastName:names[1], creatorType:"author"});
					} else {
						newItem.creators.push({lastName:names[0], isInstitution:true, creatorType:"author"});
					}
				} else if(fieldCode == "SO") {
					if (fieldContent.match(/\d{4}/)) {
						newItem.date = fieldContent.match(/\d{4}/)[0];
					}
					if (fieldContent.match(/(\d+)\((\d+)\)/)) {
						var voliss = fieldContent.match(/(\d+)\((\d+)\)/);
						newItem.volume = voliss[1];
						newItem.issue = voliss[2];
					}
					if (fieldContent.match(/vol\.\s*(\d+)/)) {
						newItem.volume = fieldContent.match(/vol\.\s*(\d+)/)[1];
					}
					if (fieldContent.match(/vol\.\s*\d+\s*,\s*no\.\s*(\d+)/)) {
						newItem.issue = fieldContent.match(/vol\.\s*\d+\s*,\s*no\.\s*(\d+)/)[1];
					}
					if (fieldContent.match(/\d+\-\d+/))
						newItem.pages = fieldContent.match(/\d+\-\d+/)[0];
			  		if (fieldContent.match(/pp\.\s*(\d+\-\d+)/))
						newItem.pages = fieldContent.match(/pp\.\s*(\d+\-\d+)/)[1];
					if (fieldContent.match(/[J|j]ournal/)) {
						newItem.publicationTitle = fieldContent.match(/[J|j]ournal[-\s\w]+/)[0];
					} else {
						newItem.publicationTitle = Zotero.Utilities.trimInternal(fieldContent.split(/(\.|;|(,\s*vol\.))/)[0]);
					}
				} else if(fieldCode == "SB") {
					newItem.tags.push(Zotero.Utilities.superCleanString(fieldContent));
				} else if(fieldCode == "KW") {
					newItem.tags.push(fieldContent.split(/; +/));
				} else if(fieldCode == "DB") {
					newItem.repository = "Ovid ("+fieldContent+")";
				} else if(fieldCode == "DI") {
					newItem.DOI = fieldContent;
				} else if(fieldCode == "DO") {
					newItem.DOI = fieldContent;
				} else if(fieldCode == "DP") {
					newItem.date = fieldContent;
				} else if(fieldCode == "IS") {
					newItem.ISSN = fieldContent;
				} else if(fieldCode == "AB") {
					newItem.abstractNote = fieldContent;
				}
			}
		}

		// last item is complete
		if(haveStarted) {
			newItem.complete();
		}
	});
	Zotero.wait();
}<commit_msg>Pushed updated Ovid translator that supports non-English interfaces<commit_after>{
	"translatorID":"cde4428-5434-437f-9cd9-2281d14dbf9",
	"translatorType":4,
	"label":"Ovid",
	"creator":"Simon Kornblith and Michael Berkowitz",
	"target":"/(gw2|spa|spb|sp....)/ovidweb\\.cgi",
	"minVersion":"1.0.0b3.r1",
	"maxVersion":"",
	"priority":100,
	"inRepository":true,
	"lastUpdated":"2010-01-07 10:00:00"
}

function detectWeb(doc, url) {
	var namespace = doc.documentElement.namespaceURI;
	var nsResolver = namespace ? function(prefix) {
		if (prefix == 'x') return namespace; else return null;
	} : null;

	var rmXPath = '//span[';
	var rmText = [
		"Results Manager",
		"Gestionnaire des résultats",
		"Ergebnismanager",
		"檢索結果管理員",
		"Administrador de resultados",
		"检索结果管理员"
	];
	var contains = [];
	for each(var str in rmText) {
		contains.push('contains(./text(), "' + str + '")');
	}
	rmXPath += contains.join(' or ') + ']'
	if(!doc.evaluate(rmXPath, doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext()) {
		return false;
	}

	var results = doc.evaluate('//div[@class="bibheader-resultsrange"]/b', doc, nsResolver, XPathResult.ANY_TYPE, null);
	var first = results.iterateNext();
	if(first) {
		first = Zotero.Utilities.trimInternal(first.textContent);
		var second = results.iterateNext();
		second = Zotero.Utilities.trimInternal(second.textContent);
		if(first.indexOf("-") != -1 || second.indexOf("-") != -1) {
			return "multiple";
		} else {
			return "journalArticle";
		}
	}

	return false;
}

function senCase(string) {
	var words = string.split(/\b/);
	for (var i = 0 ; i < words.length ; i++) {
		if (words[i].match(/[A-Z]/)) {
			words[i] = words[i][0] + words[i].substring(1).toLowerCase();
		}
	}
	return words.join("");
}

function doWeb(doc, url) {
	var namespace = doc.documentElement.namespaceURI;
	var nsResolver = namespace ? function(prefix) {
		if (prefix == 'x') return namespace; else return null;
	} : null;

	var results = Zotero.Utilities.cleanString(doc.evaluate('//div[@class="bibheader-resultsrange"]/b', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext().textContent);
	var post = "S="+doc.evaluate('.//input[@name="S"]', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext().value;

	if(results.indexOf("-") != -1) {
		var items = new Object();

		// Go through table rows
		if (doc.evaluate('/html/body/form/div[substring(@class, 1, 10)="titles-row"]', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext()) {
			var tableRows = doc.evaluate('/html/body/form/div[substring(@class, 1, 10)="titles-row"]', doc, nsResolver, XPathResult.ANY_TYPE, null);
		} else if (doc.evaluate('//div[@id="titles-records"]/table[@class="titles-row"]', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext()) {
			var tableRows = doc.evaluate('//div[@id="titles-records"]/table[@class="titles-row"]', doc, nsResolver, XPathResult.ANY_TYPE, null);
		}
		var tableRow;
		while(tableRow = tableRows.iterateNext()) {
			var id = doc.evaluate('.//input[@name="R"]', tableRow, nsResolver, XPathResult.ANY_TYPE,
				null).iterateNext().value;
			items[id] = Zotero.Utilities.cleanString(doc.evaluate('.//span[@class="titles-title"]', tableRow,
				nsResolver, XPathResult.ANY_TYPE, null).iterateNext().textContent);
		}

		var items = Zotero.selectItems(items);
		if(!items) return true;

		for(var i in items) {
			post += "&R="+i;
		}
	} else {
		var id = doc.evaluate('.//input[@name="R"]', doc, nsResolver, XPathResult.ANY_TYPE,
			null).iterateNext().value;
		post += "&R="+id;
	}

	if (detectWeb(doc, url) == "multiple") {
		var selectvar = doc.evaluate('.//input[@name="SELECT"]', doc, nsResolver, XPathResult.ANY_TYPE, null);
		var nextselect = selectvar.iterateNext().value;
		if (next = selectvar.iterateNext()) {
			post += "&SELECT=" + next.value;
		} else {
			post += "&SELECT="+ nextselect;
		}
	} else {
		post += "&SELECT=" + doc.evaluate('.//input[@name="SELECT"]', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext().value;
	}
	post += "&CitMan="+doc.evaluate('.//input[@name="CitMan"]', doc, nsResolver, XPathResult.ANY_TYPE,
		null).iterateNext().value;
	post += "&CitManPrev="+doc.evaluate('.//input[@name="CitManPrev"]', doc, nsResolver, XPathResult.ANY_TYPE,
		null).iterateNext().value;
	post += "&cmRecordSelect=SELECTED&cmFields=ALL&cmFormat=export&cmsave.x=12&cmsave.y=7&doSave=1";

	Zotero.Utilities.HTTP.doPost(url, post, function(text) {
		var lines = text.split("\n");
		var haveStarted = false;
		var newItemRe = /^<[0-9]+>/;

		var newItem = new Zotero.Item("journalArticle");

		for(var i in lines) {
			if(lines[i].substring(0,3) == "<1>") {
				haveStarted = true;
			} else if(newItemRe.test(lines[i])) {
				newItem.complete();

				newItem = new Zotero.Item("journalArticle");
			} else if(lines[i].substr(2, 4) == "  - " && haveStarted) {
				var fieldCode = lines[i].substr(0, 2);
				var fieldContent = Zotero.Utilities.cleanString(lines[i].substr(6));
				if(fieldCode == "TI") {
					newItem.title = fieldContent.replace(/\. \[\w+\]$/, "");
				} else if(fieldCode == "AU") {
					var names = fieldContent.split(", ");

					if(names.length >= 2) {
						// get rid of the weird field codes
						if(names.length == 2) {
							names[1] = names[1].replace(/ [\+\*\S\[\]]+$/, "");
						}
						names[1] = names[1].replace(/ (?:MD|PhD|[BM]Sc|[BM]A|MPH|MB)$/i, "");

						newItem.creators.push({firstName:names[1], lastName:names[0], creatorType:"author"});
					} else if (fieldContent.match(/^(.*) [A-Z]{1,3}$/)) {
						names = fieldContent.match(/^(.*) ([A-Z]{1,3})$/);
					  	newItem.creators.push({firstName:names[2], lastName:names[1], creatorType:"author"});
					} else {
						newItem.creators.push({lastName:names[0], isInstitution:true, creatorType:"author"});
					}
				} else if(fieldCode == "SO") {
					if (fieldContent.match(/\d{4}/)) {
						newItem.date = fieldContent.match(/\d{4}/)[0];
					}
					if (fieldContent.match(/(\d+)\((\d+)\)/)) {
						var voliss = fieldContent.match(/(\d+)\((\d+)\)/);
						newItem.volume = voliss[1];
						newItem.issue = voliss[2];
					}
					if (fieldContent.match(/vol\.\s*(\d+)/)) {
						newItem.volume = fieldContent.match(/vol\.\s*(\d+)/)[1];
					}
					if (fieldContent.match(/vol\.\s*\d+\s*,\s*no\.\s*(\d+)/)) {
						newItem.issue = fieldContent.match(/vol\.\s*\d+\s*,\s*no\.\s*(\d+)/)[1];
					}
					if (fieldContent.match(/\d+\-\d+/))
						newItem.pages = fieldContent.match(/\d+\-\d+/)[0];
			  		if (fieldContent.match(/pp\.\s*(\d+\-\d+)/))
						newItem.pages = fieldContent.match(/pp\.\s*(\d+\-\d+)/)[1];
					if (fieldContent.match(/[J|j]ournal/)) {
						newItem.publicationTitle = fieldContent.match(/[J|j]ournal[-\s\w]+/)[0];
					} else {
						newItem.publicationTitle = Zotero.Utilities.trimInternal(fieldContent.split(/(\.|;|(,\s*vol\.))/)[0]);
					}
				} else if(fieldCode == "SB") {
					newItem.tags.push(Zotero.Utilities.superCleanString(fieldContent));
				} else if(fieldCode == "KW") {
					newItem.tags.push(fieldContent.split(/; +/));
				} else if(fieldCode == "DB") {
					newItem.repository = "Ovid ("+fieldContent+")";
				} else if(fieldCode == "DI") {
					newItem.DOI = fieldContent;
				} else if(fieldCode == "DO") {
					newItem.DOI = fieldContent;
				} else if(fieldCode == "DP") {
					newItem.date = fieldContent;
				} else if(fieldCode == "IS") {
					newItem.ISSN = fieldContent;
				} else if(fieldCode == "AB") {
					newItem.abstractNote = fieldContent;
				}
			}
		}

		// last item is complete
		if(haveStarted) {
			newItem.complete();
		}
	}, function () { Zotero.done(); });
	Zotero.wait();
}<|endoftext|>


<commit_before>import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { blur, click, triggerEvent, render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('select-box (clicking outside)', function(hooks) {
  setupRenderingTest(hooks);

  test('clicking outside', async function(assert) {
    assert.expect(4);

    let count = 0;

    this.set('clickedOutside', () => {
      count++;
    });

    await render(hbs`

      <SelectBox @onClickOutside={{this.clickedOutside}}>

      </SelectBox>
    `);

    await click('.select-box');

    assert.equal(count, 0, 'clicking the select box is not outside');

    await click('.inside');

    assert.equal(count, 0, 'clicking inside the select box is not outside');

    await click('.outside');

    assert.equal(
      count,
      1,
      'clicking outside the select box sends the onClickOutside action, when the select box was focused'
    );

    await blur('.select-box');

    await click('.outside');

    assert.equal(
      count,
      2,
      "clicking outside the select box still sends the onClickOutside action, even if it was wasn't in use"
    );
  });

  test('touchstart outside', async function(assert) {
    assert.expect(2);

    this.set('handleClickOutside', () => {
      assert.step('handle click outside');
    });

    this.set('handleExternalClick', () => {
      assert.step('handle external click');
    });

    await render(hbs`
      <button
        type="button"
        class="external-click"
        {{on "click" this.handleExternalClick}}
      ></button>

      <SelectBox @onClickOutside={{this.handleClickOutside}} />
    `);

    await triggerEvent('.external-click', 'touchstart');

    assert.verifySteps(
      ['handle click outside'],
      'touchstart will be treated as a click for detecting whether the ' +
        'select box was clicked-outside-of'
    );
  });

  test('clicking outside to close but programatically opening', async function(assert) {
    assert.expect(7);

    let sb;

    this.set('handleClickOutside', () => {
      sb.close();
      assert.step('handle click outside');
    });

    this.set('handleReady', api => {
      sb = api;
      assert.step('handle ready');
      sb.open();
    });

    this.set('handleExternalOpen', () => {
      assert.step('handle external click');
      sb.open();
    });

    this.set('handleOpen', () => {
      assert.step('handle open');
    });

    this.set('handleClose', () => {
      assert.step('handle close');
    });

    await render(hbs`
      <button
        type="button"
        class="external-open"
        {{on "click" this.handleExternalOpen}}
      ></button>

      <SelectBox
        @onReady={{this.handleReady}}
        @onClickOutside={{this.handleClickOutside}}
        @onOpen={{this.handleOpen}}
        @onClose={{this.handleClose}} />
    `);

    await click('.external-open', 'touchstart');

    assert.verifySteps(
      [
        'handle ready',
        'handle open',
        'handle close',
        'handle click outside',
        'handle external click',
        'handle open'
      ],
      'select box will be closed, because something outside of it was ' +
        'clicked. then, it will be opened. This ordering is important!'
    );
  });
});
<commit_msg>correct function call<commit_after>import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { blur, click, triggerEvent, render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('select-box (clicking outside)', function(hooks) {
  setupRenderingTest(hooks);

  test('clicking outside', async function(assert) {
    assert.expect(4);

    let count = 0;

    this.set('clickedOutside', () => {
      count++;
    });

    await render(hbs`

      <SelectBox @onClickOutside={{this.clickedOutside}}>

      </SelectBox>
    `);

    await click('.select-box');

    assert.equal(count, 0, 'clicking the select box is not outside');

    await click('.inside');

    assert.equal(count, 0, 'clicking inside the select box is not outside');

    await click('.outside');

    assert.equal(
      count,
      1,
      'clicking outside the select box sends the onClickOutside action, when the select box was focused'
    );

    await blur('.select-box');

    await click('.outside');

    assert.equal(
      count,
      2,
      "clicking outside the select box still sends the onClickOutside action, even if it was wasn't in use"
    );
  });

  test('touchstart outside', async function(assert) {
    assert.expect(2);

    this.set('handleClickOutside', () => {
      assert.step('handle click outside');
    });

    this.set('handleExternalClick', () => {
      assert.step('handle external click');
    });

    await render(hbs`
      <button
        type="button"
        class="external-click"
        {{on "click" this.handleExternalClick}}
      ></button>

      <SelectBox @onClickOutside={{this.handleClickOutside}} />
    `);

    await triggerEvent('.external-click', 'touchstart');

    assert.verifySteps(
      ['handle click outside'],
      'touchstart will be treated as a click for detecting whether the ' +
        'select box was clicked-outside-of'
    );
  });

  test('clicking outside to close but programatically opening', async function(assert) {
    assert.expect(7);

    let sb;

    this.set('handleClickOutside', () => {
      sb.close();
      assert.step('handle click outside');
    });

    this.set('handleReady', api => {
      sb = api;
      assert.step('handle ready');
      sb.open();
    });

    this.set('handleExternalOpen', () => {
      assert.step('handle external click');
      sb.open();
    });

    this.set('handleOpen', () => {
      assert.step('handle open');
    });

    this.set('handleClose', () => {
      assert.step('handle close');
    });

    await render(hbs`
      <button
        type="button"
        class="external-open"
        {{on "click" this.handleExternalOpen}}
      ></button>

      <SelectBox
        @onReady={{this.handleReady}}
        @onClickOutside={{this.handleClickOutside}}
        @onOpen={{this.handleOpen}}
        @onClose={{this.handleClose}} />
    `);

    await click('.external-open');

    assert.verifySteps(
      [
        'handle ready',
        'handle open',
        'handle close',
        'handle click outside',
        'handle external click',
        'handle open'
      ],
      'select box will be closed, because something outside of it was ' +
        'clicked. then, it will be opened. This ordering is important!'
    );
  });
});
<|endoftext|>


<commit_before>/*
 *  Copyright 2014 TWO SIGMA OPEN SOURCE, LLC
 *
 *  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.
 */

var BeakerPageObject = require('../../beaker.po.js');
var beakerPO;

describe('Table Display (Groovy API)', function () {

    beforeAll(function(done){
        beakerPO = new BeakerPageObject();
        browser.get(beakerPO.baseURL + "beaker/#/open?uri=file:config%2Ftutorials%2Ftable-api.bkr&readOnly=true").then(done);
        beakerPO.waitUntilLoadingCellOutput();
        browser.driver.manage().window().maximize();
    });

    afterAll(function(done){
        beakerPO.createScreenshot('tableGroovyTutorial');
        done();
    });

    function getArrayTdElements(idCell, indx){
        return beakerPO.getDataTablesTBodyByIdCell(idCell).get(indx).all(by.css('td'));
    }

    describe('Programmatic control of visual formatting', function(){
        it('Built-in formatters', function () {
            var idCell = "codeaWwWKg";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table', 'tableGroovyBuiltInFormatters', 60000);
            beakerPO.checkDtContainerByIdCell(idCell);
            beakerPO.checkDataTableHeadByIdCell(idCell, 'm3\ny30\ny1\nm6\ny2\ny10\ny3\ntime\ny5\ny7');
            beakerPO.checkDataTableBodyByIdCell(idCell, 25, '0 8 8.258571 7.920952 7.956190 8.085238 8.206667 :) 1990-01-31 8.119524 8.196190');
        });
        it('Cell renderer', function () {
            var idCell = "codeROe5NG";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');
            beakerPO.checkDtContainerByIdCell(idCell);

            var arrTd = getArrayTdElements(idCell, 0);
            expect(arrTd.count()).toBe(11);
            beakerPO.checkSubString(arrTd.get(1), '7.8981', 0, 6);
            expect(arrTd.get(1).element(by.css('.dt-bar-data')).isPresent()).toBe(true);
            expect(arrTd.get(1).element(by.css('.dt-cell-text')).isPresent()).toBe(true);
        });
        it('Controlling column display', function () {
            var idCell = "codenPyPvP";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');
            beakerPO.checkDtContainerByIdCell(idCell);
            beakerPO.checkDataTableHeadByIdCell(idCell, 'm3\ny1\ny5\ntime\ny2');
            var arrTd = getArrayTdElements(idCell, 0);
            expect(arrTd.count()).toBe(6);
            beakerPO.checkSubString(arrTd.get(1), '7.898095', 0, 8);
            beakerPO.checkSubString(arrTd.get(2), '7.92095', 0, 7);
            beakerPO.checkSubString(arrTd.get(3), '8.11952', 0, 7);
            beakerPO.checkSubString(arrTd.get(5), '8.085238', 0, 8);
        });
        it('Save as csv', function () {
            var idCell = "codenPyPvP";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.checkSaveAsCsvByIdCell(idCell, 'tableGroovy');
        });
        describe('Сell highlighters', function() {
            it('Color the entire table like a heatmap based on column value', function () {
                var idCell = "codeWyrMwb";
                beakerPO.scrollToBkCellByIdCell(idCell);
                beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');
                beakerPO.checkDtContainerByIdCell(idCell);

                var arrTd = getArrayTdElements(idCell, 0);
                expect(arrTd.count()).toBe(11);
                beakerPO.checkSubString(arrTd.get(1), '7.8981', 0, 6);
                expect(arrTd.get(1).getCssValue('background-color')).toBe('rgba(236, 91, 88, 1)');

                var arrTd20 = getArrayTdElements(idCell, 20);
                expect(arrTd20.count()).toBe(11);
                beakerPO.checkSubString(arrTd20.get(1), '5.3735', 0, 6);
                expect(arrTd20.get(1).getCssValue('background-color')).toBe('rgba(190, 114, 130, 1)');
            });
            it('Use a closure to highlight specific values', function () {
                var idCell = "code7zZ3sT";
                beakerPO.scrollToBkCellByIdCell(idCell);
                beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');
                beakerPO.checkDtContainerByIdCell(idCell);

                var arrTd = getArrayTdElements(idCell, 0);
                expect(arrTd.count()).toBe(4);
                beakerPO.checkSubString(arrTd.get(3), '3', 0, 1);
                expect(arrTd.get(3).getCssValue('background-color')).toBe('rgba(255, 0, 0, 1)');

                var arrTd1 = getArrayTdElements(idCell, 1);
                expect(arrTd1.count()).toBe(4);
                beakerPO.checkSubString(arrTd1.get(3), '6', 0, 1);
                expect(arrTd1.get(3).getCssValue('background-color')).toBe('rgba(0, 255, 0, 1)');
            });
            it('Heatmap with custom min/max values and colors', function () {
                var idCell = "codetHTtD2";
                beakerPO.scrollToBkCellByIdCell(idCell);
                beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');
                beakerPO.checkDtContainerByIdCell(idCell);

                var arrTd = getArrayTdElements(idCell, 0);
                expect(arrTd.count()).toBe(11);
                beakerPO.checkSubString(arrTd.get(3), '7.9210', 0, 6);
                expect(arrTd.get(3).getCssValue('background-color')).toBe('rgba(105, 190, 120, 1)');
                beakerPO.checkSubString(arrTd.get(4), '7.9562', 0, 6);
                expect(arrTd.get(4).getCssValue('background-color')).toBe('rgba(249, 171, 171, 1)');

                var arrTd20 = getArrayTdElements(idCell, 20);
                expect(arrTd20.count()).toBe(11);
                beakerPO.checkSubString(arrTd20.get(3), '5.5725', 0, 6);
                expect(arrTd20.get(3).getCssValue('background-color')).toBe('rgba(241, 194, 87, 1)');
                beakerPO.checkSubString(arrTd20.get(4), '5.4790', 0, 6);
                expect(arrTd20.get(4).getCssValue('background-color')).toBe('rgba(0, 0, 0, 1)');
            });
            it('UniqueEntriesHighlighter', function () {
                var idCell = "code2KwSKV";
                beakerPO.scrollToBkCellByIdCell(idCell);
                beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');
                beakerPO.checkDtContainerByIdCell(idCell);

                var rgb0 = 'rgba(245, 188, 188, 1)'
                var arrTd0 = getArrayTdElements(idCell, 0);
                expect(arrTd0.count()).toBe(4);
                beakerPO.checkSubString(arrTd0.get(1), '1', 0, 1);
                expect(arrTd0.get(1).getCssValue('background-color')).toBe(rgb0);
                beakerPO.checkSubString(arrTd0.get(2), '2', 0, 1);
                expect(arrTd0.get(2).getCssValue('background-color')).toBe(rgb0);

                var rgb1 = 'rgba(188, 245, 188, 1)';
                var arrTd1 = getArrayTdElements(idCell, 1);
                expect(arrTd1.count()).toBe(4);
                beakerPO.checkSubString(arrTd1.get(1), '3', 0, 1);
                expect(arrTd1.get(1).getCssValue('background-color')).toBe(rgb1);
                beakerPO.checkSubString(arrTd1.get(2), '4', 0, 1);
                expect(arrTd1.get(2).getCssValue('background-color')).toBe(rgb1);
            });
        });
        it('Adding custom actions', function () {
            var idCell = "codex9hziv";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');
            beakerPO.scrollHeaderElement();
            beakerPO.checkDtContainerByIdCell(idCell);

            var arrTd0 = getArrayTdElements(idCell, 0);
            expect(arrTd0.count()).toBe(4);
            beakerPO.checkSubString(arrTd0.get(1), '1', 0, 1);
            beakerPO.doubleClickElementWithHandlingError(arrTd0.get(1), 'tdElement');
            beakerPO.checkSubString(arrTd0.get(1), '6', 0, 1);
            beakerPO.scrollHeaderElement();
            browser.sleep(1000);
            arrTd0 = getArrayTdElements(idCell, 0);
            browser.actions().mouseMove(arrTd0.get(1)).perform();
            browser.actions().click(protractor.Button.RIGHT).perform().then(null,
                function(error){
                    beakerPO.scrollHeaderElement();
                    browser.actions().mouseMove(arrTd0.get(1)).perform();
                    browser.actions().click(protractor.Button.RIGHT).perform();
                });
            var contextMenu =  element(by.css('ul.context-menu-list[style*="z-index: 2"]'));
            var negate = contextMenu.element(by.cssContainingText('span', 'negate'));
            browser.actions().mouseMove(negate).perform();
            beakerPO.clickElementWithHandlingError(negate, 'spanElement');
            beakerPO.checkSubString(arrTd0.get(1), '-6', 0, 2);

            browser.actions().mouseMove(arrTd0.get(1)).perform();
            browser.actions().click(protractor.Button.RIGHT).perform();
            var run_misc_formatting = contextMenu.element(by.cssContainingText('span', 'run misc_formatting'));
            browser.actions().mouseMove(run_misc_formatting).perform();
            run_misc_formatting.click().then(function(){
                beakerPO.waitUntilLoadingFinished();
            });
        });
        it('Misc formatting', function () {
            var idCell = "code0Xkn1E";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');
            beakerPO.checkDtContainerByIdCell(idCell);
            var arrTd = getArrayTdElements(idCell, 0);
            expect(arrTd.count()).toBe(4);
            beakerPO.checkSubString(arrTd.get(1), '1', 0, 1);
            expect(arrTd.get(1).getCssValue('color')).toBe('rgba(192, 192, 192, 1)');
            expect(arrTd.get(1).getCssValue('font-size')).toBe('15px');
            expect(arrTd.get(1).getAttribute('title')).toBe('The value is: 1');

            var thHeader = beakerPO.getDataTablesTHeadByIdCell(idCell).get(0).all(by.css('th'));
            expect(thHeader.get(1).getCssValue('vertical-align')).toBe('bottom');
            expect(thHeader.get(1).getCssValue('font-size')).toBe('30px');
        });
        it('Run tags', function () {
            var idCell = "codeYEb1OS";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');
            beakerPO.checkDtContainerByIdCell(idCell);
            var arrTd0 = getArrayTdElements(idCell, 0);
            expect(arrTd0.count()).toBe(4);
            beakerPO.checkSubString(arrTd0.get(2), '2', 0, 1);
            beakerPO.doubleClickElementWithHandlingError(arrTd0.get(2), 'tdElement');
            browser.sleep(1000);
            beakerPO.waitUntilLoadingFinished();

            idCell = "codeC8S3M3";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Results');
            beakerPO.scrollToCodeCellOutputByIdCell(idCell);
            beakerPO.checkCellOutputTextByIdCell(idCell, 'You clicked on the cell [0, 1]');
        });
    });

    describe('EscapeHTML function', function(){
        it('Downloaded file should contain only regular ascii', function(){
            var idCell = "codeb4y5Z8";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');
            beakerPO.checkDownloadCSV(idCell);
        });
    });
});<commit_msg>fix tests<commit_after>/*
 *  Copyright 2014 TWO SIGMA OPEN SOURCE, LLC
 *
 *  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.
 */

var BeakerPageObject = require('../../beaker.po.js');
var beakerPO;

describe('Table Display (Groovy API)', function () {

    beforeAll(function(done){
        beakerPO = new BeakerPageObject();
        browser.get(beakerPO.baseURL + "beaker/#/open?uri=file:config%2Ftutorials%2Ftable-api.bkr&readOnly=true").then(done);
        beakerPO.waitUntilLoadingCellOutput();
        browser.driver.manage().window().maximize();
    });

    afterAll(function(done){
        beakerPO.createScreenshot('tableGroovyTutorial');
        done();
    });

    function getArrayTdElements(idCell, indx){
        return beakerPO.getDataTablesTBodyByIdCell(idCell).get(indx).all(by.css('td'));
    }

    describe('Programmatic control of visual formatting', function(){
        it('Built-in formatters', function () {
            var idCell = "codeaWwWKg";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table', 'tableGroovyBuiltInFormatters', 60000);

            beakerPO.checkDataTableHeadByIdCell(idCell, 'm3\ny30\ny1\nm6\ny2\ny10\ny3\ntime\ny5\ny7');
            beakerPO.checkDataTableBodyByIdCell(idCell, 25, '0 8 8.258571 7.920952 7.956190 8.085238 8.206667 :) 1990-01-31 8.119524 8.196190');
        });
        it('Cell renderer', function () {
            var idCell = "codeROe5NG";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');

            var arrTd = getArrayTdElements(idCell, 0);
            expect(arrTd.count()).toBe(11);
            beakerPO.checkSubString(arrTd.get(1), '7.8981', 0, 6);
            expect(arrTd.get(1).element(by.css('.dt-bar-data')).isPresent()).toBe(true);
            expect(arrTd.get(1).element(by.css('.dt-cell-text')).isPresent()).toBe(true);
        });
        it('Controlling column display', function () {
            var idCell = "codenPyPvP";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');

            beakerPO.checkDataTableHeadByIdCell(idCell, 'm3\ny1\ny5\ntime\ny2');
            var arrTd = getArrayTdElements(idCell, 0);
            expect(arrTd.count()).toBe(6);
            beakerPO.checkSubString(arrTd.get(1), '7.898095', 0, 8);
            beakerPO.checkSubString(arrTd.get(2), '7.92095', 0, 7);
            beakerPO.checkSubString(arrTd.get(3), '8.11952', 0, 7);
            beakerPO.checkSubString(arrTd.get(5), '8.085238', 0, 8);
        });
        it('Save as csv', function () {
            var idCell = "codenPyPvP";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.checkSaveAsCsvByIdCell(idCell, 'tableGroovy');
        });
        describe('Сell highlighters', function() {
            it('Color the entire table like a heatmap based on column value', function () {
                var idCell = "codeWyrMwb";
                beakerPO.scrollToBkCellByIdCell(idCell);
                beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');

                var arrTd = getArrayTdElements(idCell, 0);
                expect(arrTd.count()).toBe(11);
                beakerPO.checkSubString(arrTd.get(1), '7.8981', 0, 6);
                expect(arrTd.get(1).getCssValue('background-color')).toBe('rgba(236, 91, 88, 1)');

                var arrTd20 = getArrayTdElements(idCell, 20);
                expect(arrTd20.count()).toBe(11);
                beakerPO.checkSubString(arrTd20.get(1), '5.3735', 0, 6);
                expect(arrTd20.get(1).getCssValue('background-color')).toBe('rgba(190, 114, 130, 1)');
            });
            it('Use a closure to highlight specific values', function () {
                var idCell = "code7zZ3sT";
                beakerPO.scrollToBkCellByIdCell(idCell);
                beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');

                var arrTd = getArrayTdElements(idCell, 0);
                expect(arrTd.count()).toBe(4);
                beakerPO.checkSubString(arrTd.get(3), '3', 0, 1);
                expect(arrTd.get(3).getCssValue('background-color')).toBe('rgba(255, 0, 0, 1)');

                var arrTd1 = getArrayTdElements(idCell, 1);
                expect(arrTd1.count()).toBe(4);
                beakerPO.checkSubString(arrTd1.get(3), '6', 0, 1);
                expect(arrTd1.get(3).getCssValue('background-color')).toBe('rgba(0, 255, 0, 1)');
            });
            it('Heatmap with custom min/max values and colors', function () {
                var idCell = "codetHTtD2";
                beakerPO.scrollToBkCellByIdCell(idCell);
                beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');

                var arrTd = getArrayTdElements(idCell, 0);
                expect(arrTd.count()).toBe(11);
                beakerPO.checkSubString(arrTd.get(3), '7.9210', 0, 6);
                expect(arrTd.get(3).getCssValue('background-color')).toBe('rgba(105, 190, 120, 1)');
                beakerPO.checkSubString(arrTd.get(4), '7.9562', 0, 6);
                expect(arrTd.get(4).getCssValue('background-color')).toBe('rgba(249, 171, 171, 1)');

                var arrTd20 = getArrayTdElements(idCell, 20);
                expect(arrTd20.count()).toBe(11);
                beakerPO.checkSubString(arrTd20.get(3), '5.5725', 0, 6);
                expect(arrTd20.get(3).getCssValue('background-color')).toBe('rgba(241, 194, 87, 1)');
                beakerPO.checkSubString(arrTd20.get(4), '5.4790', 0, 6);
                expect(arrTd20.get(4).getCssValue('background-color')).toBe('rgba(0, 0, 0, 1)');
            });
            it('UniqueEntriesHighlighter', function () {
                var idCell = "code2KwSKV";
                beakerPO.scrollToBkCellByIdCell(idCell);
                beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');

                var rgb0 = 'rgba(245, 188, 188, 1)'
                var arrTd0 = getArrayTdElements(idCell, 0);
                expect(arrTd0.count()).toBe(4);
                beakerPO.checkSubString(arrTd0.get(1), '1', 0, 1);
                expect(arrTd0.get(1).getCssValue('background-color')).toBe(rgb0);
                beakerPO.checkSubString(arrTd0.get(2), '2', 0, 1);
                expect(arrTd0.get(2).getCssValue('background-color')).toBe(rgb0);

                var rgb1 = 'rgba(188, 245, 188, 1)';
                var arrTd1 = getArrayTdElements(idCell, 1);
                expect(arrTd1.count()).toBe(4);
                beakerPO.checkSubString(arrTd1.get(1), '3', 0, 1);
                expect(arrTd1.get(1).getCssValue('background-color')).toBe(rgb1);
                beakerPO.checkSubString(arrTd1.get(2), '4', 0, 1);
                expect(arrTd1.get(2).getCssValue('background-color')).toBe(rgb1);
            });
        });
        it('Adding custom actions', function () {
            var idCell = "codex9hziv";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');

            var arrTd0 = getArrayTdElements(idCell, 0);
            expect(arrTd0.count()).toBe(4);
            beakerPO.checkSubString(arrTd0.get(1), '1', 0, 1);
            beakerPO.doubleClickElementWithHandlingError(arrTd0.get(1), 'tdElement');
            beakerPO.checkSubString(arrTd0.get(1), '6', 0, 1);
            browser.actions().mouseMove(arrTd0.get(1)).perform();
            browser.actions().click(protractor.Button.RIGHT).perform();
            var contextMenu =  element(by.css('ul.context-menu-list[style*="z-index: 2"]'));
            var negate = contextMenu.element(by.cssContainingText('span', 'negate'));
            browser.actions().mouseMove(negate).perform();
            beakerPO.clickElementWithHandlingError(negate, 'spanElement');
            beakerPO.checkSubString(arrTd0.get(1), '-6', 0, 2);

            browser.actions().mouseMove(arrTd0.get(1)).perform();
            browser.actions().click(protractor.Button.RIGHT).perform();
            var run_misc_formatting = contextMenu.element(by.cssContainingText('span', 'run misc_formatting'));
            browser.actions().mouseMove(run_misc_formatting).perform();
            run_misc_formatting.click().then(function(){
                browser.sleep(1000);
                beakerPO.waitUntilLoadingFinished();
            });
        });
        it('Misc formatting', function () {
            var idCell = "code0Xkn1E";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');

            var arrTd = getArrayTdElements(idCell, 0);
            expect(arrTd.count()).toBe(4);
            beakerPO.checkSubString(arrTd.get(1), '1', 0, 1);
            expect(arrTd.get(1).getCssValue('color')).toBe('rgba(192, 192, 192, 1)');
            expect(arrTd.get(1).getCssValue('font-size')).toBe('15px');
            expect(arrTd.get(1).getAttribute('title')).toBe('The value is: 1');

            var thHeader = beakerPO.getDataTablesTHeadByIdCell(idCell).get(0).all(by.css('th'));
            expect(thHeader.get(1).getCssValue('vertical-align')).toBe('bottom');
            expect(thHeader.get(1).getCssValue('font-size')).toBe('30px');
        });
        it('Run tags', function () {
            var idCell = "codeYEb1OS";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');

            var arrTd0 = getArrayTdElements(idCell, 0);
            expect(arrTd0.count()).toBe(4);
            beakerPO.checkSubString(arrTd0.get(2), '2', 0, 1);
            beakerPO.doubleClickElementWithHandlingError(arrTd0.get(2), 'tdElement');
            browser.sleep(1000);
            beakerPO.waitUntilLoadingFinished();

            idCell = "codeC8S3M3";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Results');
            beakerPO.scrollToCodeCellOutputByIdCell(idCell);
            beakerPO.checkCellOutputTextByIdCell(idCell, 'You clicked on the cell [0, 1]');
        });
    });

    describe('EscapeHTML function', function(){
        it('Downloaded file should contain only regular ascii', function(){
            var idCell = "codeb4y5Z8";
            beakerPO.scrollToBkCellByIdCell(idCell);
            beakerPO.clickCodeCellInputButtonByIdCell(idCell, 'Table');
            beakerPO.checkDownloadCSV(idCell);
        });
    });
});<|endoftext|>


<commit_before>import * as I from 'immutable';
import * as S from '../common/lazyseq';

import { floatMatrices } from '../arithmetic/types';
const ops = floatMatrices;

let _timers = null;

export const useTimers = timers => { _timers = timers };

const surfToJS = ({ faces, pos, isFixed }) => ({
  faces: faces.toJS(),
  pos: pos.toArray(),
  isFixed: isFixed.toJS()
});

const surfFromJS = ({ faces, pos, isFixed }) => ({
  faces: I.fromJS(faces),
  pos: I.List(pos),
  isFixed: I.fromJS(isFixed)
});

const pairs = as => S.seq(as).consecCirc(2).map(s => s.toArray()).toArray();
const sum = vs => vs.reduce((v, w) => ops.plus(v, w));
const corners = pos => idcs => idcs.map(i => pos[i]);
const centroid = pos => ops.div(sum(pos), S.seq(pos).length);
const normalized = v => ops.div(v, ops.norm(v));

const edgeIndexes = faces => {
  const edges = [];
  const edgeIndex = {};
  const lookup = {};

  for (let f = 0; f < faces.length; ++f) {
    const ps = pairs(faces[f]);
    lookup[f] = {};

    for (let i = 0; i < ps.length; ++i) {
      const [v, w] = ps[i].slice().sort();
      if (edgeIndex[[v, w]] == null) {
        edgeIndex[[v, w]] = edges.length;
        edges.push([v, w]);
      }
      lookup[f][i] = edgeIndex[[v, w]];
    }
  }

  return { edges, lookup };
};

const adjustedPositions = (faces, pos, isFixed) => {
  const coord = (face, idx, factor) => ops.times(factor, pos[face[idx]]);

  const facesByVertex = pos.map(_ => []);
  for (const f of faces)
    facesByVertex[f[0]].push(f);

  return pos.map((p, i) => {
    if (isFixed[i] || facesByVertex[i].length == 0)
      return p;

    const m = facesByVertex[i].length;

    let t = ops.times(0, pos[0]);
    for (const f of facesByVertex[i])
      t = ops.plus(t, sum([coord(f, 1, 2), coord(f, 3, 2), coord(f, 2, -1)]));

    return ops.plus(ops.times(1/(m*m), t), ops.times((m-3)/m, p));
  });
};

export const subD = ({ faces, pos, isFixed }) => {
  const n = pos.length;
  const m = faces.length;
  const { edges, lookup } = edgeIndexes(faces);

  const newFaces = [];
  for (let f = 0; f < faces.length; ++f) {
    const vs = faces[f];
    const edge = i => n + m + lookup[f][i];
    const prev = i => (i + vs.length - 1) % vs.length;

    for (let i = 0; i < vs.length; ++i)
      newFaces.push([vs[i], edge(i), n + f, edge(prev(i))]);
  }

  const ffix = Array(faces.length).fill(false);
  const fpos = faces.map(corners(pos)).map(centroid);

  const facesByEdge = {};
  for (const f of Object.keys(lookup)) {
    for (const e of Object.values(lookup[f])) {
      if (facesByEdge[e] == null)
        facesByEdge[e] = [f];
      else
        facesByEdge[e].push(f);
    }
  }

  const efix = edges.map(([v, w]) => isFixed[v] && isFixed[w]);
  const epos = edges.map(([v, w], i) => centroid(
    (efix[i] ? [] : facesByEdge[i].map(v => fpos[v]))
      .concat([pos[v], pos[w]])));

  return {
    faces: newFaces,
    pos: adjustedPositions(newFaces, pos.concat(fpos, epos), isFixed),
    isFixed: isFixed.concat(ffix, efix)
  };
};

const withCenterFaces = ({ faces, pos, isFixed }, fn) => {
  const centerFaces = faces.map(corners(pos.toArray())).map(fn);
  const extraPositions = centerFaces.flatten(1);

  const offsets = S.seq(centerFaces)
    .reductions((a, vs) => a + vs.size, pos.size).toArray();

  const newFaces = faces.flatMap((is, f) => {
    if (centerFaces.get(f).size == 0)
      return I.List([is]);

    const k = offsets[f];
    const m = is.size;
    const center = I.List(I.Range(k, k+m));
    const gallery = I.Range(0, m).map(i => {
      const j = (i + 1) % m;
      return I.List([is.get(i), is.get(j), j+k, i+k]);
    });
    return I.List(gallery).push(center);
  });

  return {
    faces  : newFaces,
    pos    : pos.concat(extraPositions),
    isFixed: isFixed.concat(extraPositions.map(p => false))
  };
};

const faceNormal = vs => normalized(sum(
  pairs(vs).map(([v, w]) => ops.crossProduct(v, w))));

const projection = (normal, origin = ops.times(0, normal)) => p => {
  const d = ops.minus(p, origin);
  return ops.plus(origin, ops.minus(d, ops.times(ops.times(normal, d), normal)));
};

const flattenedOrSuppressedFace = vs => {
  const ws = vs.map(projection(faceNormal(vs), centroid(vs)));

  for (let i = 0; i < vs.size; ++i) {
    if (ops.norm(ops.minus(vs.get(i), ws.get(i))) > 0.01 * ops.norm(ws.get(i)))
      return ws;
  }

  return I.List();
};

const scaled = (f, vs) => {
  if (vs.size == 0)
    return vs;

  const c = centroid(vs);
  return vs.map(v => ops.plus(ops.times(f, v), ops.times(1-f, c)));
};

export const withFlattenedCenterFaces = surface =>
  surfToJS(withCenterFaces(
    surfFromJS(surface),
    vs => scaled(0.5, flattenedOrSuppressedFace(vs))));

const insetPoint = (corner, wd, left, right, center) => {
  const lft = normalized(ops.minus(left, corner));
  const rgt = normalized(ops.minus(right, corner));
  const dia = ops.plus(lft, rgt);

  if (ops.norm(dia) < 0.01) {
    const s = normalized(ops.minus(center, corner));
    const t = projection(lft)(s);
    return ops.plus(corner, ops.times(wd / ops.norm(t), s));
  }
  else if (ops.norm(ops.crossProduct(lft, rgt)) < 0.01) {
    return ops.plus(corner, ops.times(wd, lft));
  }
  else {
    const len = wd * ops.norm(dia) / ops.norm(projection(lft)(dia));
    const s   = normalized(ops.crossProduct(dia, ops.crossProduct(lft, rgt)));
    const t   = projection(s)(ops.minus(center, corner));
    const f   = len / ops.norm(t);
    return ops.plus(corner, ops.times(f, t));
  }
};

const nextHalfEdgeAtVertex = faces => {
  const edgeLoc = I.Map().asMutable();

  faces.forEach((is, f) => {
    is.forEach((v, k) => {
      const w = is.get((k + 1) % is.size);
      edgeLoc.setIn([v, w], [f, k]);
    });
  });

  return ([f, i]) => {
    const is = faces.get(f);
    const v = is.get(i);
    const w = is.get((i + is.size - 1) % is.size);
    return edgeLoc.getIn([v, w]);
  };
};

const shrunkAt = ({ faces, pos, isFixed }, wd, isCorner) => {
  const nextIndex = (f, i) => (i + 1) % faces.get(f).size;
  const endIndex  = (f, i) => faces.get(f).get(nextIndex(f, i));
  const isSplit   = ([f, i]) => isCorner.get(endIndex(f, i));
  const nextAtVtx = nextHalfEdgeAtVertex(faces);

  const newVertexForStretch = (v, hs) => {
    const ends = hs.map(([f, i]) => pos.get(endIndex(f, i)));
    const c    = centroid(ends.length > 2 ?
                          ends.slice(1, -1) :
                          corners(pos.toArray())(faces.get(hs[0][0])));
    return insetPoint(pos.get(v), wd, ends[0], ends[ends.length-1], c);
  };

  const edgeCycle = ([f, k]) => {
    let e = [f, k];
    const hs = [];
    do {
      hs.push(e);
      e = nextAtVtx(e);
    } while(e[0] != f || e[1] != k);
    return hs;
  };

  const stretches = hs => {
    const splits = hs
      .map((e, i) => isSplit(e) ? i : null)
      .filter(i => i != null);
    return splits.map((k, i) => (
      i == 0 ?
        hs.slice(splits[splits.length-1]).concat(hs.slice(0, splits[0]+1)) :
        hs.slice(splits[i-1], splits[i]+1)));
  };

  const seen   = I.Set().asMutable();
  const mods   = I.Map().asMutable();
  const newPos = [];

  faces.forEach((is, f) => {
    is.forEach((v, k) => {
      if (seen.contains(v) || !isCorner.get(v))
        return;
      seen.add(v);

      stretches(edgeCycle([f, k])).forEach(stretch => {
        newPos.push(newVertexForStretch(v, stretch));
        for (let j = 0; j < stretch.length - 1; ++j)
          mods.set(I.List(stretch[j]), pos.size + newPos.length - 1);
      });
    });
  });

  const newFaces = faces.map(
    (is, f) => is.map((v, i) => mods.get(I.List([f, i])) || v));

  return {
    pos  : newPos,
    faces: newFaces
  };
};

const connectors = (oldFaces, newFaces) => {
  const result = I.List().asMutable();
  oldFaces.forEach((isOld, i) => {
    const isNew = newFaces.get(i);
    isOld.forEach((vo, k) => {
      const k1 = (k + 1) % isOld.size;
      if (vo != isNew.get(k) && isOld.get(k1) != isNew.get(k1))
        result.push([[vo, isOld.get(k1)], [isNew.get(k), isNew.get(k1)]]);
    });
  });
  return result.asImmutable();
};

export const insetAt = (surf, wd, isCorner) => {
  const { faces, pos, isFixed } = surfFromJS(surf);
  const { pos: newPos, faces: modifiedFaces } =
    shrunkAt({ faces, pos, isFixed }, wd, I.fromJS(isCorner));

  const newFaces = connectors(faces, modifiedFaces)
    .map(([[vo, wo], [vn, wn]]) => I.List([vo, wo, wn, vn]));

  const result = {
    pos    : pos.concat(newPos),
    isFixed: isFixed.concat(newPos.map(i => true)),
    faces  : modifiedFaces.concat(newFaces)
  };

  return surfToJS(result);
};

const cycles = m => {
  const seen = {};
  const faces = [];

  for (const k of Object.keys(m)) {
    if (!seen[k]) {
      let i = parseInt(k);
      const f = [];
      while (!seen[i]) {
        seen[i] = true;
        f.push(i);
        i = m[i];
      }
      faces.push(f);
    }
  }

  return faces;
};

export const beveledAt = (surf, wd, isCorner) => {
  const { faces, pos, isFixed } = surfFromJS(surf);
  const { pos: newPos, faces: modifiedFaces } =
    shrunkAt({ faces, pos, isFixed }, wd, I.fromJS(isCorner));

  const edgeFaces = I.List(
    connectors(faces, modifiedFaces)
      .map(([eo, en]) => [I.List(eo).sort(), I.List(en)])
      .groupBy(([eo]) => eo)
      .map(([[, a], [, b]]) => a.concat(b).reverse())
      .valueSeq());

  const m = {};
  for (const is of edgeFaces) {
    const [a, b, c, d] = is.toArray();
    m[c] = b;
    m[a] = d;
  }

  const result = {
    pos    : pos.concat(newPos),
    isFixed: isFixed.concat(newPos.map(i => true)),
    faces  : modifiedFaces.concat(edgeFaces).concat(cycles(m))
  };

  return surfToJS(result);
};

if (require.main == module) {
  Array.prototype.toString = function() {
    return 'List [ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const cube = {
    pos: [[0,0,0], [0,0,1], [0,1,0], [0,1,1],
          [1,0,0], [1,0,1], [1,1,0], [1,1,1]],
    faces: [[0,1,3,2],[5,4,6,7],
            [1,0,4,5],[2,3,7,6],
            [0,2,6,4],[3,1,5,7]],
    isFixed: Array(8).fill(0).map((_, i) => i < 4)
  };

  const t = withFlattenedCenterFaces(cube);

  console.log(insetAt(t, 0.1, Array(8).fill(true)));
  console.log();
  console.log(beveledAt(cube, 0.1, Array(8).fill(true)));
  console.log();
  console.log(subD(cube));
}
<commit_msg>convert withFlattenedCenterFaces() function to plain JS data structures<commit_after>import * as I from 'immutable';
import * as S from '../common/lazyseq';

import { floatMatrices } from '../arithmetic/types';
const ops = floatMatrices;

let _timers = null;

export const useTimers = timers => { _timers = timers };

const surfToJS = ({ faces, pos, isFixed }) => ({
  faces: faces.toJS(),
  pos: pos.toArray(),
  isFixed: isFixed.toJS()
});

const surfFromJS = ({ faces, pos, isFixed }) => ({
  faces: I.fromJS(faces),
  pos: I.List(pos),
  isFixed: I.fromJS(isFixed)
});

const pairs = as => S.seq(as).consecCirc(2).map(s => s.toArray()).toArray();
const sum = vs => vs.reduce((v, w) => ops.plus(v, w));
const corners = pos => idcs => idcs.map(i => pos[i]);
const centroid = pos => ops.div(sum(pos), S.seq(pos).length);
const normalized = v => ops.div(v, ops.norm(v));

const edgeIndexes = faces => {
  const edges = [];
  const edgeIndex = {};
  const lookup = {};

  for (let f = 0; f < faces.length; ++f) {
    const ps = pairs(faces[f]);
    lookup[f] = {};

    for (let i = 0; i < ps.length; ++i) {
      const [v, w] = ps[i].slice().sort();
      if (edgeIndex[[v, w]] == null) {
        edgeIndex[[v, w]] = edges.length;
        edges.push([v, w]);
      }
      lookup[f][i] = edgeIndex[[v, w]];
    }
  }

  return { edges, lookup };
};

const adjustedPositions = (faces, pos, isFixed) => {
  const coord = (face, idx, factor) => ops.times(factor, pos[face[idx]]);

  const facesByVertex = pos.map(_ => []);
  for (const f of faces)
    facesByVertex[f[0]].push(f);

  return pos.map((p, i) => {
    if (isFixed[i] || facesByVertex[i].length == 0)
      return p;

    const m = facesByVertex[i].length;

    let t = ops.times(0, pos[0]);
    for (const f of facesByVertex[i])
      t = ops.plus(t, sum([coord(f, 1, 2), coord(f, 3, 2), coord(f, 2, -1)]));

    return ops.plus(ops.times(1/(m*m), t), ops.times((m-3)/m, p));
  });
};

export const subD = ({ faces, pos, isFixed }) => {
  const n = pos.length;
  const m = faces.length;
  const { edges, lookup } = edgeIndexes(faces);

  const newFaces = [];
  for (let f = 0; f < faces.length; ++f) {
    const vs = faces[f];
    const edge = i => n + m + lookup[f][i];
    const prev = i => (i + vs.length - 1) % vs.length;

    for (let i = 0; i < vs.length; ++i)
      newFaces.push([vs[i], edge(i), n + f, edge(prev(i))]);
  }

  const ffix = Array(faces.length).fill(false);
  const fpos = faces.map(corners(pos)).map(centroid);

  const facesByEdge = {};
  for (const f of Object.keys(lookup)) {
    for (const e of Object.values(lookup[f])) {
      if (facesByEdge[e] == null)
        facesByEdge[e] = [f];
      else
        facesByEdge[e].push(f);
    }
  }

  const efix = edges.map(([v, w]) => isFixed[v] && isFixed[w]);
  const epos = edges.map(([v, w], i) => centroid(
    (efix[i] ? [] : facesByEdge[i].map(v => fpos[v]))
      .concat([pos[v], pos[w]])));

  return {
    faces: newFaces,
    pos: adjustedPositions(newFaces, pos.concat(fpos, epos), isFixed),
    isFixed: isFixed.concat(ffix, efix)
  };
};

const withCenterFaces = ({ faces, pos, isFixed }, fn) => {
  const newFaces = [];
  const newPos = pos.slice();
  const newIsFixed = isFixed.slice();

  for (const is of faces) {
    const centerFace = fn(is.map(i => pos[i]));

    if (centerFace == null)
      newFaces.push(is);
    else {
      const k = newPos.length;
      const m = centerFace.length;

      for (const v of centerFace) {
        newPos.push(v);
        newIsFixed.push(false);
      }

      newFaces.push(S.range(k, k + m).toArray());
      for (let i = 0; i < m; ++i) {
        const j = (i + 1) % m;
        newFaces.push([is[i], is[j], j + k, i + k]);
      }
    }
  }

  return { faces: newFaces, pos: newPos, isFixed: newIsFixed };
};

const faceNormal = vs => normalized(sum(
  pairs(vs).map(([v, w]) => ops.crossProduct(v, w))));

const projection = (normal, origin=ops.times(0, normal)) => p => {
  const d = ops.minus(p, origin);
  return ops.plus(origin, ops.minus(d, ops.times(ops.times(normal, d), normal)));
};

const flattenedOrSuppressedFace = vs => {
  const ws = vs.map(projection(faceNormal(vs), centroid(vs)));

  for (let i = 0; i < vs.length; ++i) {
    if (ops.norm(ops.minus(vs[i], ws[i])) > 0.01 * ops.norm(ws[i]))
      return ws;
  }

  return null;
};

const scaled = (f, vs) => {
  if (vs == null)
    return vs;

  const c = centroid(vs);
  return vs.map(v => ops.plus(ops.times(f, v), ops.times(1-f, c)));
};

export const withFlattenedCenterFaces = surface =>
  withCenterFaces(surface, vs => scaled(0.5, flattenedOrSuppressedFace(vs)));

const insetPoint = (corner, wd, left, right, center) => {
  const lft = normalized(ops.minus(left, corner));
  const rgt = normalized(ops.minus(right, corner));
  const dia = ops.plus(lft, rgt);

  if (ops.norm(dia) < 0.01) {
    const s = normalized(ops.minus(center, corner));
    const t = projection(lft)(s);
    return ops.plus(corner, ops.times(wd / ops.norm(t), s));
  }
  else if (ops.norm(ops.crossProduct(lft, rgt)) < 0.01) {
    return ops.plus(corner, ops.times(wd, lft));
  }
  else {
    const len = wd * ops.norm(dia) / ops.norm(projection(lft)(dia));
    const s   = normalized(ops.crossProduct(dia, ops.crossProduct(lft, rgt)));
    const t   = projection(s)(ops.minus(center, corner));
    const f   = len / ops.norm(t);
    return ops.plus(corner, ops.times(f, t));
  }
};

const nextHalfEdgeAtVertex = faces => {
  const edgeLoc = I.Map().asMutable();

  faces.forEach((is, f) => {
    is.forEach((v, k) => {
      const w = is.get((k + 1) % is.size);
      edgeLoc.setIn([v, w], [f, k]);
    });
  });

  return ([f, i]) => {
    const is = faces.get(f);
    const v = is.get(i);
    const w = is.get((i + is.size - 1) % is.size);
    return edgeLoc.getIn([v, w]);
  };
};

const shrunkAt = ({ faces, pos, isFixed }, wd, isCorner) => {
  const nextIndex = (f, i) => (i + 1) % faces.get(f).size;
  const endIndex  = (f, i) => faces.get(f).get(nextIndex(f, i));
  const isSplit   = ([f, i]) => isCorner.get(endIndex(f, i));
  const nextAtVtx = nextHalfEdgeAtVertex(faces);

  const newVertexForStretch = (v, hs) => {
    const ends = hs.map(([f, i]) => pos.get(endIndex(f, i)));
    const c    = centroid(ends.length > 2 ?
                          ends.slice(1, -1) :
                          corners(pos.toArray())(faces.get(hs[0][0])));
    return insetPoint(pos.get(v), wd, ends[0], ends[ends.length-1], c);
  };

  const edgeCycle = ([f, k]) => {
    let e = [f, k];
    const hs = [];
    do {
      hs.push(e);
      e = nextAtVtx(e);
    } while(e[0] != f || e[1] != k);
    return hs;
  };

  const stretches = hs => {
    const splits = hs
      .map((e, i) => isSplit(e) ? i : null)
      .filter(i => i != null);
    return splits.map((k, i) => (
      i == 0 ?
        hs.slice(splits[splits.length-1]).concat(hs.slice(0, splits[0]+1)) :
        hs.slice(splits[i-1], splits[i]+1)));
  };

  const seen   = I.Set().asMutable();
  const mods   = I.Map().asMutable();
  const newPos = [];

  faces.forEach((is, f) => {
    is.forEach((v, k) => {
      if (seen.contains(v) || !isCorner.get(v))
        return;
      seen.add(v);

      stretches(edgeCycle([f, k])).forEach(stretch => {
        newPos.push(newVertexForStretch(v, stretch));
        for (let j = 0; j < stretch.length - 1; ++j)
          mods.set(I.List(stretch[j]), pos.size + newPos.length - 1);
      });
    });
  });

  const newFaces = faces.map(
    (is, f) => is.map((v, i) => mods.get(I.List([f, i])) || v));

  return {
    pos  : newPos,
    faces: newFaces
  };
};

const connectors = (oldFaces, newFaces) => {
  const result = I.List().asMutable();
  oldFaces.forEach((isOld, i) => {
    const isNew = newFaces.get(i);
    isOld.forEach((vo, k) => {
      const k1 = (k + 1) % isOld.size;
      if (vo != isNew.get(k) && isOld.get(k1) != isNew.get(k1))
        result.push([[vo, isOld.get(k1)], [isNew.get(k), isNew.get(k1)]]);
    });
  });
  return result.asImmutable();
};

export const insetAt = (surf, wd, isCorner) => {
  const { faces, pos, isFixed } = surfFromJS(surf);
  const { pos: newPos, faces: modifiedFaces } =
    shrunkAt({ faces, pos, isFixed }, wd, I.fromJS(isCorner));

  const newFaces = connectors(faces, modifiedFaces)
    .map(([[vo, wo], [vn, wn]]) => I.List([vo, wo, wn, vn]));

  const result = {
    pos    : pos.concat(newPos),
    isFixed: isFixed.concat(newPos.map(i => true)),
    faces  : modifiedFaces.concat(newFaces)
  };

  return surfToJS(result);
};

const cycles = m => {
  const seen = {};
  const faces = [];

  for (const k of Object.keys(m)) {
    if (!seen[k]) {
      let i = parseInt(k);
      const f = [];
      while (!seen[i]) {
        seen[i] = true;
        f.push(i);
        i = m[i];
      }
      faces.push(f);
    }
  }

  return faces;
};

export const beveledAt = (surf, wd, isCorner) => {
  const { faces, pos, isFixed } = surfFromJS(surf);
  const { pos: newPos, faces: modifiedFaces } =
    shrunkAt({ faces, pos, isFixed }, wd, I.fromJS(isCorner));

  const edgeFaces = I.List(
    connectors(faces, modifiedFaces)
      .map(([eo, en]) => [I.List(eo).sort(), I.List(en)])
      .groupBy(([eo]) => eo)
      .map(([[, a], [, b]]) => a.concat(b).reverse())
      .valueSeq());

  const m = {};
  for (const is of edgeFaces) {
    const [a, b, c, d] = is.toArray();
    m[c] = b;
    m[a] = d;
  }

  const result = {
    pos    : pos.concat(newPos),
    isFixed: isFixed.concat(newPos.map(i => true)),
    faces  : modifiedFaces.concat(edgeFaces).concat(cycles(m))
  };

  return surfToJS(result);
};

if (require.main == module) {
  Array.prototype.toString = function() {
    return 'List [ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const cube = {
    pos: [[0,0,0], [0,0,1], [0,1,0], [0,1,1],
          [1,0,0], [1,0,1], [1,1,0], [1,1,1]],
    faces: [[0,1,3,2],[5,4,6,7],
            [1,0,4,5],[2,3,7,6],
            [0,2,6,4],[3,1,5,7]],
    isFixed: Array(8).fill(0).map((_, i) => i < 4)
  };

  const t = withFlattenedCenterFaces(cube);

  console.log(insetAt(t, 0.1, Array(8).fill(true)));
  console.log();
  console.log(beveledAt(cube, 0.1, Array(8).fill(true)));
  console.log();
  console.log(subD(cube));
}
<|endoftext|>


<commit_before>import Alt from 'alt';

const alt = new Alt();

// There's a chrome plugin for Alt that relies on this
//
import chromeDebug from 'alt/utils/chromeDebug';
chromeDebug(alt);

export default alt;<commit_msg>Update alt path<commit_after>import Alt from 'alt';

const alt = new Alt();

// There's a chrome plugin for Alt that relies on this
//
import chromeDebug from 'alt-utils/lib/chromeDebug';
chromeDebug(alt);

export default alt;<|endoftext|>


<commit_before>"use strict";

const constants = require("./constants");
const templates = require("../views");

module.exports = {
	updateText,
	getStoredTypes,
};

function getStoredTypes(condensed) {
	const obj = {};

	constants.condensedTypes.forEach((type) => {
		obj[type] = condensed.data(type) || 0;
	});

	return obj;
}

function updateText(condensed, addedTypes) {
	const obj = getStoredTypes(condensed);

	Object.keys(addedTypes).map((type) => {
		obj[type] += addedTypes[type];
		condensed.data(type, obj[type]);
	});

	const strings = [];
	constants.condensedTypes.forEach((type) => {
		if (obj[type]) {
			switch (type) {
			case "away":
				strings.push(obj[type] + (obj[type] > 1 ? " users have gone away" : " user has gone away"));
				break;
			case "back":
				strings.push(obj[type] + (obj[type] > 1 ? " users have come back" : " user has come back"));
				break;
			case "chghost":
				strings.push(obj[type] + (obj[type] > 1 ? " users have changed hostname" : " user has changed hostname"));
				break;
			case "join":
				strings.push(obj[type] + (obj[type] > 1 ? " users have joined the channel" : " user has joined the channel"));
				break;
			case "part":
				strings.push(obj[type] + (obj[type] > 1 ? " users have left the channel" : " user has left the channel"));
				break;
			case "quit":
				strings.push(obj[type] + (obj[type] > 1 ? " users have quit" : " user has quit"));
				break;
			case "nick":
				strings.push(obj[type] + (obj[type] > 1 ? " users have changed nick" : " user has changed nick"));
				break;
			case "kick":
				strings.push(obj[type] + (obj[type] > 1 ? " users were kicked" : " user was kicked"));
				break;
			case "mode":
				strings.push(obj[type] + (obj[type] > 1 ? " modes were set" : " mode was set"));
				break;
			}
		}
	});

	let text = strings.pop();

	if (strings.length) {
		text = strings.join(", ") + ", and " + text;
	}

	condensed.find(".condensed-summary .content")
		.html(text + templates.msg_condensed_toggle());
}
<commit_msg>Count quits as parts in condensed messages to reduce information density<commit_after>"use strict";

const constants = require("./constants");
const templates = require("../views");

module.exports = {
	updateText,
	getStoredTypes,
};

function getStoredTypes(condensed) {
	const obj = {};

	constants.condensedTypes.forEach((type) => {
		obj[type] = condensed.data(type) || 0;
	});

	return obj;
}

function updateText(condensed, addedTypes) {
	const obj = getStoredTypes(condensed);

	Object.keys(addedTypes).map((type) => {
		// Count quits as parts in condensed messages to reduce information density
		if (type === "quit") {
			type = "part";
		}

		obj[type] += addedTypes[type];
		condensed.data(type, obj[type]);
	});

	const strings = [];
	constants.condensedTypes.forEach((type) => {
		if (obj[type]) {
			switch (type) {
			case "away":
				strings.push(obj[type] + (obj[type] > 1 ? " users have gone away" : " user has gone away"));
				break;
			case "back":
				strings.push(obj[type] + (obj[type] > 1 ? " users have come back" : " user has come back"));
				break;
			case "chghost":
				strings.push(obj[type] + (obj[type] > 1 ? " users have changed hostname" : " user has changed hostname"));
				break;
			case "join":
				strings.push(obj[type] + (obj[type] > 1 ? " users have joined the channel" : " user has joined the channel"));
				break;
			case "part":
				strings.push(obj[type] + (obj[type] > 1 ? " users have left the channel" : " user has left the channel"));
				break;
			case "nick":
				strings.push(obj[type] + (obj[type] > 1 ? " users have changed nick" : " user has changed nick"));
				break;
			case "kick":
				strings.push(obj[type] + (obj[type] > 1 ? " users were kicked" : " user was kicked"));
				break;
			case "mode":
				strings.push(obj[type] + (obj[type] > 1 ? " modes were set" : " mode was set"));
				break;
			}
		}
	});

	let text = strings.pop();

	if (strings.length) {
		text = strings.join(", ") + ", and " + text;
	}

	condensed.find(".condensed-summary .content")
		.html(text + templates.msg_condensed_toggle());
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Navbar from './Navbar.js'

class Macweb extends Component {
  render() {

    //Image map created by jtwright
    let map = (
      <map name="macg">
        <area href="/entry-pages/a-entry.pdf" shape="poly" coords="285,68,329,92,417,42,417,93,329,144,285,119" alt="Link" data-maphilight='{"strokeColor":"ccf8ff","strokeWidth":4,"fillColor":"ccf8ff","fillOpacity":1}' title="A Entry" />
        <area href="/entry-pages/b-entry.pdf" shape="poly" coords="285,119,329,144,417,93,417,144,329,194,285,169" alt="Link" data-maphilight='{"strokeColor":"191970","strokeWidth":4,"fillColor":"191970","fillOpacity":1}' title="B Entry" />
        <area href="/entry-pages/c-entry.pdf" shape="poly" coords="285,169,329,194,417,144,417,195,329,245,285,220" alt="Link" data-maphilight='{"strokeColor":"32cd32","strokeWidth":4,"fillColor":"32cd32","fillOpacity":1}' title="C Entry" />
        <area href="/entry-pages/d-entry.pdf" shape="poly" coords="285,220,329,245,417,195,417,245,329,295,285,270" alt="Link" data-maphilight='{"strokeColor":"ff2400","strokeWidth":4,"fillColor":"ff2400","fillOpacity":1}' title="D Entry" />
        <area href="/entry-pages/e-entry.pdf" shape="poly" coords="285,270,329,295,402,254,418,244,402,254,402,304,329,346,285,321" alt="Link" data-maphilight='{"strokeColor":"100505","strokeWidth":4,"fillColor":"100505","fillOpacity":1}' title="E Entry" />
        <area href="/entry-pages/f-entry.pdf" shape="poly" coords="402,254,446,228,593,313,593,381,520,423,520,355,475,330,447,346,402,322" alt="Link" data-maphilight='{"strokeColor":"800080","strokeWidth":4,"fillColor":"800080","fillOpacity":1}' title="F Entry" />
        <area href="/entry/g" shape="poly" coords="475,330,520,355,520,423,329,533,329,466,285,440" alt="Link" data-maphilight='{"strokeColor":"e25822","strokeWidth":4,"fillColor":"e25822","fillOpacity":1}' title="G Entry" />
        <area href="/entry-pages/h-entry.pdf" shape="poly" coords="285,440,329,466,329,533,256,575,138,508,138,440,182,414,256,457" alt="Link" data-maphilight='{"strokeColor":"0aa200","strokeWidth":4,"fillColor":"0aa200","fillOpacity":1}' title="H Entry" />
        <area href="/entry/j" shape="poly" coords="182,414,138,440,138,508,65,465,65,398,152,347,196,372,196,422" alt="Link" data-maphilight='{"strokeColor":"8c41d8","strokeWidth":4,"fillColor":"8c41d8","fillOpacity":1}' title="J Entry" />
      </map>
    )
    //Made with David Lynch's Maphilight: http://davidlynch.org/blog/2008/03/maphilight-image-map-mouseover-highlighting/

    //reinsert the navbar before the container
    return (

Javascript is becoming core to the internet, this website requires it to function.

        <Navbar />

            {map}

Announcements

All bikes in the bike room must be cleared out and moved to another storage location by May 5th. Violators will forfeit their bikes.

    );
  }

  componentDidMount() {
    $('img[usemap]').maphilight();
  }
}

export default Macweb;
<commit_msg>reset announcements<commit_after>import React, { Component } from 'react';
import Navbar from './Navbar.js'

class Macweb extends Component {
  render() {

    //Image map created by jtwright
    let map = (
      <map name="macg">
        <area href="/entry-pages/a-entry.pdf" shape="poly" coords="285,68,329,92,417,42,417,93,329,144,285,119" alt="Link" data-maphilight='{"strokeColor":"ccf8ff","strokeWidth":4,"fillColor":"ccf8ff","fillOpacity":1}' title="A Entry" />
        <area href="/entry-pages/b-entry.pdf" shape="poly" coords="285,119,329,144,417,93,417,144,329,194,285,169" alt="Link" data-maphilight='{"strokeColor":"191970","strokeWidth":4,"fillColor":"191970","fillOpacity":1}' title="B Entry" />
        <area href="/entry-pages/c-entry.pdf" shape="poly" coords="285,169,329,194,417,144,417,195,329,245,285,220" alt="Link" data-maphilight='{"strokeColor":"32cd32","strokeWidth":4,"fillColor":"32cd32","fillOpacity":1}' title="C Entry" />
        <area href="/entry-pages/d-entry.pdf" shape="poly" coords="285,220,329,245,417,195,417,245,329,295,285,270" alt="Link" data-maphilight='{"strokeColor":"ff2400","strokeWidth":4,"fillColor":"ff2400","fillOpacity":1}' title="D Entry" />
        <area href="/entry-pages/e-entry.pdf" shape="poly" coords="285,270,329,295,402,254,418,244,402,254,402,304,329,346,285,321" alt="Link" data-maphilight='{"strokeColor":"100505","strokeWidth":4,"fillColor":"100505","fillOpacity":1}' title="E Entry" />
        <area href="/entry-pages/f-entry.pdf" shape="poly" coords="402,254,446,228,593,313,593,381,520,423,520,355,475,330,447,346,402,322" alt="Link" data-maphilight='{"strokeColor":"800080","strokeWidth":4,"fillColor":"800080","fillOpacity":1}' title="F Entry" />
        <area href="/entry/g" shape="poly" coords="475,330,520,355,520,423,329,533,329,466,285,440" alt="Link" data-maphilight='{"strokeColor":"e25822","strokeWidth":4,"fillColor":"e25822","fillOpacity":1}' title="G Entry" />
        <area href="/entry-pages/h-entry.pdf" shape="poly" coords="285,440,329,466,329,533,256,575,138,508,138,440,182,414,256,457" alt="Link" data-maphilight='{"strokeColor":"0aa200","strokeWidth":4,"fillColor":"0aa200","fillOpacity":1}' title="H Entry" />
        <area href="/entry/j" shape="poly" coords="182,414,138,440,138,508,65,465,65,398,152,347,196,372,196,422" alt="Link" data-maphilight='{"strokeColor":"8c41d8","strokeWidth":4,"fillColor":"8c41d8","fillOpacity":1}' title="J Entry" />
      </map>
    )
    //Made with David Lynch's Maphilight: http://davidlynch.org/blog/2008/03/maphilight-image-map-mouseover-highlighting/

    //reinsert the navbar before the container
    return (

Javascript is becoming core to the internet, this website requires it to function.

        <Navbar />

            {map}

    );
  }

  componentDidMount() {
    $('img[usemap]').maphilight();
  }
}

export default Macweb;
<|endoftext|>


<commit_before>goog.provide('rnoadm.state');

goog.require('goog.asserts');
goog.require('goog.debug.Logger');
goog.require('rnoadm.gfx');
goog.require('rnoadm.gfx.NetworkSprite');
goog.require('rnoadm.gfx.Sprite');
goog.require('rnoadm.net');
goog.require('rnoadm.state.NetworkObject');
goog.require('rnoadm.state.Object');

/**
 * Logger for rnoadm.state
 *
 * @type {goog.debug.Logger}
 * @private
 * @const
 */
rnoadm.state.logger_ = goog.debug.Logger.getLogger('rnoadm.state');

/**
 * @type {Array.<Object.<string, rnoadm.state.Object>>}
 * @private
 * @const
 */
rnoadm.state.objects_ = Array(256 * 256);

rnoadm.net.addHandler('Update', function(updates) {
  updates.forEach(function(update) {
    /**
     * @type {string}
     * @const
     */
    var id = update['I'];
    /**
     * @type {number}
     * @const
     */
    var x = update['X'];
    /**
     * @type {number}
     * @const
     */
    var y = update['Y'];
    /**
     * @type {number|undefined}
     * @const
     */
    var fromX = update['Fx'];
    /**
     * @type {number|undefined}
     * @const
     */
    var fromY = update['Fy'];
    /**
     * @type {boolean|undefined}
     * @const
     */
    var removed = update['R'];
    /**
     * @type {(!rnoadm.state.NetworkObject|undefined)}
     * @const
     */
    var object = update['O'];

    goog.asserts.assert(x >= 0 && x < 256, 'update: x out of range');
    goog.asserts.assert(y >= 0 && y < 256, 'update: y out of range');

    /**
     * @type {number}
     * @const
     */
    var index = x | y << 8;

    if (!rnoadm.state.objects_[index]) {
      rnoadm.state.objects_[index] = {};
    }

    if (removed) {
      goog.asserts.assert(!goog.isDef(fromX) && !goog.isDef(fromY) &&
                          !goog.isDef(object), 'update: contradictory update');
      goog.asserts.assert(id in rnoadm.state.objects_[index],
                          'update: removal of nonexistent object');
      delete rnoadm.state.objects_[index][id];
      rnoadm.gfx.repaint();
      return;
    }

    if (goog.isDef(object)) {
      goog.asserts.assert(!goog.isDef(fromX) && !goog.isDef(fromY),
                          'update: contradictory update');
      if (id in rnoadm.state.objects_[index]) {
        rnoadm.state.objects_[index][id].update(object);
      } else {
        rnoadm.state.objects_[index][id] = new rnoadm.state.Object(x, y, id,
                                                                   object);
      }
      return;
    }

    goog.asserts.assert(goog.isDef(fromX) && goog.isDef(fromY),
                        'update: blank update');
    var fromIndex = fromX | fromY << 8;
    goog.asserts.assert(id in rnoadm.state.objects_[fromIndex],
                        'update: move of nonexistent object');
    rnoadm.state.objects_[index][id] = rnoadm.state.objects_[fromIndex][id];
    delete rnoadm.state.objects_[fromIndex][id];
    rnoadm.state.objects_[index][id].move(x, y);
  });
  rnoadm.gfx.repaint();
});

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerLastMoveX_ = 0;

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerPrevX_ = 127;

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerX_ = 127;

rnoadm.net.addHandler('PlayerX', function(x) {
  if (rnoadm.state.playerX_ == x)
    return;
  rnoadm.state.playerPrevX_ = rnoadm.state.lerp(rnoadm.state.playerX_,
      rnoadm.state.playerPrevX_, rnoadm.state.playerLastMoveX_);
  rnoadm.state.playerX_ = x;
  rnoadm.state.playerLastMoveX_ = Date.now();
  rnoadm.gfx.repaint();
});

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerLastMoveY_ = 0;

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerPrevY_ = 127;

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerY_ = 127;

rnoadm.net.addHandler('PlayerY', function(y) {
  if (rnoadm.state.playerY_ == y)
    return;
  rnoadm.state.playerPrevY_ = rnoadm.state.lerp(rnoadm.state.playerY_,
      rnoadm.state.playerPrevY_, rnoadm.state.playerLastMoveY_);
  rnoadm.state.playerY_ = y;
  rnoadm.state.playerLastMoveY_ = Date.now();
  rnoadm.gfx.repaint();
});

rnoadm.net.onDisconnect.push(function() {
  for (var i = 0; i < rnoadm.state.objects_.length; i++) {
    delete rnoadm.state.objects_[i];
  }
});

/**
 * @type {rnoadm.gfx.Sprite}
 * @private
 * @const
 */
rnoadm.state.grass_ = new rnoadm.gfx.Sprite('grass', '#268f1e', '',
                                            0, 0, 512, 512);

rnoadm.gfx.paintObjects = function(w, h) {
  /** @type {number} */
  var playerX = rnoadm.state.lerp(rnoadm.state.playerX_,
      rnoadm.state.playerPrevX_, rnoadm.state.playerLastMoveX_);
  /** @type {number} */
  var playerY = rnoadm.state.lerp(rnoadm.state.playerY_,
      rnoadm.state.playerPrevY_, rnoadm.state.playerLastMoveY_);

  var xOffset = w / 2 / rnoadm.gfx.TILE_SIZE - playerX;
  var yOffset = h / 2 / rnoadm.gfx.TILE_SIZE - playerY;
  for (var x = 0; x < 256; x += 512 / rnoadm.gfx.TILE_SIZE) {
    for (var y = 0; y < 256; y += 512 / rnoadm.gfx.TILE_SIZE) {
      rnoadm.state.grass_.paint(xOffset + x + 512 / rnoadm.gfx.TILE_SIZE / 2,
                                yOffset + y + 512 / rnoadm.gfx.TILE_SIZE / 2);
    }
  }
  for (var y = Math.max(0, Math.floor(-h / 2 - yOffset));
       y < Math.min(256, Math.floor(h / 2 - yOffset));
       y++) {
    for (var x = Math.max(0, Math.floor((-w / 2 - xOffset) / 2));
         x < Math.min(128, Math.floor((w / 2 - xOffset) / 2));
         x++) {
      var objects = rnoadm.state.objects_[x << 1 | y << 8];
      if (objects) {
        for (var id in objects) {
          objects[id].paint(xOffset, yOffset);
        }
      }
    }
    for (var x = Math.max(0, Math.floor((-w / 2 - xOffset) / 2));
         x < Math.min(128, Math.floor((w / 2 - xOffset) / 2));
         x++) {
      var objects = rnoadm.state.objects_[x << 1 | y << 8 | 1];
      if (objects) {
        for (var id in objects) {
          objects[id].paint(xOffset, yOffset);
        }
      }
    }
  }
};

rnoadm.gfx.clickObject = function(x, y, w, h) {
  var xOffset = w / 2 / rnoadm.gfx.TILE_SIZE - rnoadm.state.playerX_;
  var yOffset = h / 2 / rnoadm.gfx.TILE_SIZE - rnoadm.state.playerY_;
  x = Math.floor(x / rnoadm.gfx.TILE_SIZE - xOffset);
  y = Math.ceil(y / rnoadm.gfx.TILE_SIZE - yOffset);
  if (x < 0 || x >= 256 || y < 0 || y >= 256)
    return false;
  rnoadm.net.send({'Walk': {'X': x, 'Y': y}});
  return true;
};

rnoadm.gfx.rightClickObject = function(x, y, w, h) {
  var xOffset = w / 2 / rnoadm.gfx.TILE_SIZE - rnoadm.state.playerX_;
  var yOffset = h / 2 / rnoadm.gfx.TILE_SIZE - rnoadm.state.playerY_;
  x = Math.floor(x / rnoadm.gfx.TILE_SIZE - xOffset);
  y = Math.ceil(y / rnoadm.gfx.TILE_SIZE - yOffset);
  if (x < 0 || x >= 256 || y < 0 || y >= 256)
    return false;
  var objects = [];
  for (var i in rnoadm.state.objects_[x | y << 8]) {
    objects.unshift(rnoadm.state.objects_[x | y << 8][i]);
  }
  if (objects.length == 1)
    rnoadm.hud.show('menu', objects[0]);
  else if (objects.length)
    rnoadm.hud.show('menu2', objects);
  return true;
};

// vim: set tabstop=2 shiftwidth=2 expandtab:
<commit_msg>fix grass alignment<commit_after>goog.provide('rnoadm.state');

goog.require('goog.asserts');
goog.require('goog.debug.Logger');
goog.require('rnoadm.gfx');
goog.require('rnoadm.gfx.NetworkSprite');
goog.require('rnoadm.gfx.Sprite');
goog.require('rnoadm.net');
goog.require('rnoadm.state.NetworkObject');
goog.require('rnoadm.state.Object');

/**
 * Logger for rnoadm.state
 *
 * @type {goog.debug.Logger}
 * @private
 * @const
 */
rnoadm.state.logger_ = goog.debug.Logger.getLogger('rnoadm.state');

/**
 * @type {Array.<Object.<string, rnoadm.state.Object>>}
 * @private
 * @const
 */
rnoadm.state.objects_ = Array(256 * 256);

rnoadm.net.addHandler('Update', function(updates) {
  updates.forEach(function(update) {
    /**
     * @type {string}
     * @const
     */
    var id = update['I'];
    /**
     * @type {number}
     * @const
     */
    var x = update['X'];
    /**
     * @type {number}
     * @const
     */
    var y = update['Y'];
    /**
     * @type {number|undefined}
     * @const
     */
    var fromX = update['Fx'];
    /**
     * @type {number|undefined}
     * @const
     */
    var fromY = update['Fy'];
    /**
     * @type {boolean|undefined}
     * @const
     */
    var removed = update['R'];
    /**
     * @type {(!rnoadm.state.NetworkObject|undefined)}
     * @const
     */
    var object = update['O'];

    goog.asserts.assert(x >= 0 && x < 256, 'update: x out of range');
    goog.asserts.assert(y >= 0 && y < 256, 'update: y out of range');

    /**
     * @type {number}
     * @const
     */
    var index = x | y << 8;

    if (!rnoadm.state.objects_[index]) {
      rnoadm.state.objects_[index] = {};
    }

    if (removed) {
      goog.asserts.assert(!goog.isDef(fromX) && !goog.isDef(fromY) &&
                          !goog.isDef(object), 'update: contradictory update');
      goog.asserts.assert(id in rnoadm.state.objects_[index],
                          'update: removal of nonexistent object');
      delete rnoadm.state.objects_[index][id];
      rnoadm.gfx.repaint();
      return;
    }

    if (goog.isDef(object)) {
      goog.asserts.assert(!goog.isDef(fromX) && !goog.isDef(fromY),
                          'update: contradictory update');
      if (id in rnoadm.state.objects_[index]) {
        rnoadm.state.objects_[index][id].update(object);
      } else {
        rnoadm.state.objects_[index][id] = new rnoadm.state.Object(x, y, id,
                                                                   object);
      }
      return;
    }

    goog.asserts.assert(goog.isDef(fromX) && goog.isDef(fromY),
                        'update: blank update');
    var fromIndex = fromX | fromY << 8;
    goog.asserts.assert(id in rnoadm.state.objects_[fromIndex],
                        'update: move of nonexistent object');
    rnoadm.state.objects_[index][id] = rnoadm.state.objects_[fromIndex][id];
    delete rnoadm.state.objects_[fromIndex][id];
    rnoadm.state.objects_[index][id].move(x, y);
  });
  rnoadm.gfx.repaint();
});

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerLastMoveX_ = 0;

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerPrevX_ = 127;

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerX_ = 127;

rnoadm.net.addHandler('PlayerX', function(x) {
  if (rnoadm.state.playerX_ == x)
    return;
  rnoadm.state.playerPrevX_ = rnoadm.state.lerp(rnoadm.state.playerX_,
      rnoadm.state.playerPrevX_, rnoadm.state.playerLastMoveX_);
  rnoadm.state.playerX_ = x;
  rnoadm.state.playerLastMoveX_ = Date.now();
  rnoadm.gfx.repaint();
});

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerLastMoveY_ = 0;

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerPrevY_ = 127;

/**
 * @type {number}
 * @private
 */
rnoadm.state.playerY_ = 127;

rnoadm.net.addHandler('PlayerY', function(y) {
  if (rnoadm.state.playerY_ == y)
    return;
  rnoadm.state.playerPrevY_ = rnoadm.state.lerp(rnoadm.state.playerY_,
      rnoadm.state.playerPrevY_, rnoadm.state.playerLastMoveY_);
  rnoadm.state.playerY_ = y;
  rnoadm.state.playerLastMoveY_ = Date.now();
  rnoadm.gfx.repaint();
});

rnoadm.net.onDisconnect.push(function() {
  for (var i = 0; i < rnoadm.state.objects_.length; i++) {
    delete rnoadm.state.objects_[i];
  }
});

/**
 * @type {rnoadm.gfx.Sprite}
 * @private
 * @const
 */
rnoadm.state.grass_ = new rnoadm.gfx.Sprite('grass', '#268f1e', '',
                                            0, 0, 512, 512);

rnoadm.gfx.paintObjects = function(w, h) {
  /** @type {number} */
  var playerX = rnoadm.state.lerp(rnoadm.state.playerX_,
      rnoadm.state.playerPrevX_, rnoadm.state.playerLastMoveX_);
  /** @type {number} */
  var playerY = rnoadm.state.lerp(rnoadm.state.playerY_,
      rnoadm.state.playerPrevY_, rnoadm.state.playerLastMoveY_);

  var xOffset = w / 2 / rnoadm.gfx.TILE_SIZE - playerX;
  var yOffset = h / 2 / rnoadm.gfx.TILE_SIZE - playerY;
  for (var x = 0; x < 256; x += 512 / rnoadm.gfx.TILE_SIZE) {
    for (var y = 0; y < 256; y += 512 / rnoadm.gfx.TILE_SIZE) {
      rnoadm.state.grass_.paint(xOffset + x + 512 / rnoadm.gfx.TILE_SIZE / 2 -
          0.5, yOffset + y + 512 / rnoadm.gfx.TILE_SIZE - 1);
    }
  }
  for (var y = Math.max(0, Math.floor(-h / 2 - yOffset));
       y < Math.min(256, Math.floor(h / 2 - yOffset));
       y++) {
    for (var x = Math.max(0, Math.floor((-w / 2 - xOffset) / 2));
         x < Math.min(128, Math.floor((w / 2 - xOffset) / 2));
         x++) {
      var objects = rnoadm.state.objects_[x << 1 | y << 8];
      if (objects) {
        for (var id in objects) {
          objects[id].paint(xOffset, yOffset);
        }
      }
    }
    for (var x = Math.max(0, Math.floor((-w / 2 - xOffset) / 2));
         x < Math.min(128, Math.floor((w / 2 - xOffset) / 2));
         x++) {
      var objects = rnoadm.state.objects_[x << 1 | y << 8 | 1];
      if (objects) {
        for (var id in objects) {
          objects[id].paint(xOffset, yOffset);
        }
      }
    }
  }
};

rnoadm.gfx.clickObject = function(x, y, w, h) {
  var xOffset = w / 2 / rnoadm.gfx.TILE_SIZE - rnoadm.state.playerX_;
  var yOffset = h / 2 / rnoadm.gfx.TILE_SIZE - rnoadm.state.playerY_;
  x = Math.floor(x / rnoadm.gfx.TILE_SIZE - xOffset);
  y = Math.ceil(y / rnoadm.gfx.TILE_SIZE - yOffset);
  if (x < 0 || x >= 256 || y < 0 || y >= 256)
    return false;
  rnoadm.net.send({'Walk': {'X': x, 'Y': y}});
  return true;
};

rnoadm.gfx.rightClickObject = function(x, y, w, h) {
  var xOffset = w / 2 / rnoadm.gfx.TILE_SIZE - rnoadm.state.playerX_;
  var yOffset = h / 2 / rnoadm.gfx.TILE_SIZE - rnoadm.state.playerY_;
  x = Math.floor(x / rnoadm.gfx.TILE_SIZE - xOffset);
  y = Math.ceil(y / rnoadm.gfx.TILE_SIZE - yOffset);
  if (x < 0 || x >= 256 || y < 0 || y >= 256)
    return false;
  var objects = [];
  for (var i in rnoadm.state.objects_[x | y << 8]) {
    objects.unshift(rnoadm.state.objects_[x | y << 8][i]);
  }
  if (objects.length == 1)
    rnoadm.hud.show('menu', objects[0]);
  else if (objects.length)
    rnoadm.hud.show('menu2', objects);
  return true;
};

// vim: set tabstop=2 shiftwidth=2 expandtab:
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);
});
<commit_msg>Edit test file<commit_after>import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);

  if(App.state) expect(App.state.length).toEqual(0);
});
<|endoftext|>