5.6. 案例:Frownies

转换图片表情为文字

Frownies 是对一个玩笑的回应。Greasemonkey 邮件列表有人宣称他们开发了一个用户脚本,可以把 ASCII “表情符号” 比如 :-) 转换为与它们相同含义的图片。其他人答复道,想知道要完成逆向转换要花多久时间:转换图片表情为文本。

为了创纪录,我花了大约20分钟。大部分时间是花在研究已发布的自动转换图片表情的软件上了,然后编写了详尽的转换表。

这个脚本倚赖于能自动生成表情图片的大多数软件有如下特征:这些软件会把相同含义的文字表情符号放在 <img> 元素的 alt 属性里。所以,这个脚本实际上所做的是:如果 ALT 文本匹配预定义的常量列表中的某一条,就用图片的 ALT 文本来替换图片。

例 5.6.  frownies.user.js

// ==UserScript==
// @name          Frownies
// @namespace     http://diveintogreasemonkey.org/download/
// @description   convert graphical smilies to their text equivalents
// @include       *
// ==/UserScript==

var smilies, images, img, replacement;
smilies = [":)", ":-)" ":-(", ":(", ";-)", ";)", ":-D", ":D", ":-/",
	":/", ":X", ":-X", ":\">", ":P", ":-P", ":O", ":-O", "X-(",
	"X(", ":->", ":>", "B-)", "B)", ">:)", ":((", ":(((", ":-((",
	":))", ":-))", ":-|", ":|", "O:-)", "O:)", ":-B", ":B", "=;",
	"I)", "I-)", "|-)", "|)", ":-&", ":&", ":-$", ":$", "[-(", ":O)",
	":@)", "3:-O", ":(|)", "@};-", "**==", "(~~)", "*-:)", "8-X",
	"8X", "=:)", "<):)", ";;)", ":*", ":-*", ":S", ":-S", "/:)",
	"/:-)", "8-|", "8|", "8-}", "8}", "(:|", "=P~", ":-?", ":?",
	"#-O", "#O", "=D>", "~:>", "%%-", "~O)", ":-L", ":L", "[-O<",
	"[O<", "@-)", "@)", "$-)", "$)", ">-)", ":-\"", ":^O", "B-(",
	"B(", ":)>-", "[-X", "[X", "\\:D/", ">:D<", "(%)", "=((", "#:-S",
	"#:S", "=))", "L-)", "L)", "<:-P", "<:P", ":-SS", ":SS", ":-W",
	":W", ":-<", ":<", ">:P", ">:-P", ">:/", ";))", ":-@", "^:)^",
	":-J", "(*)", ":GRIN:", ":-)", ":SMILE:", ":SAD:", ":EEK:",
	":SHOCK:", ":???:", "8)", "8-)", ":COOL:", ":LOL:", ":MAD:",
	":RAZZ:", ":OOPS:", ":CRY:", ":EVIL:", ":TWISTED:", ":ROLL:",
	":WINK:", ":!:", ":?:", ":IDEA:", ":ARROW:", ":NEUTRAL:",
	":MRGREEN:"];
images = document.evaluate(
	'//img[@alt]',
	document,
	null,
	XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
	null);
for (var i = 0; i < images.snapshotLength; i++) {
	img = images.snapshotItem(i);
	alt = img.alt.toUpperCase();
	for (var j in smilies) {
		if (alt == smilies[j]) {
			replacement = document.createTextNode(alt);
			img.parentNode.replaceChild(replacement, img);
		}
	}
}

这段代码分为四个步骤:

  1. 定义表情符号的列表(文本格式)。
  2. 找出页面中所有含有 alt 属性的图片。
  3. 对每一幅图像,检测它的 ALT 文本是否与列表中的某个 ASCII 表情符号匹配。
  4. 如果匹配,就替换 <img> 元素为包含 ASCII 表情的文本节点。

第一步简单的定义了一个列表,用 Javascript 的 [ ] 语法。

smilies = [":)", ":-)" ":-(", ":(", ";-)", ";)", ":-D", ":D", ":-/",
	":/", ":X", ":-X", ":\">", ":P", ":-P", ":O", ":-O", "X-(",
	"X(", ":->", ":>", "B-)", "B)", ">:)", ":((", ":(((", ":-((",
	":))", ":-))", ":-|", ":|", "O:-)", "O:)", ":-B", ":B", "=;",
	"I)", "I-)", "|-)", "|)", ":-&", ":&", ":-$", ":$", "[-(", ":O)",
	":@)", "3:-O", ":(|)", "@};-", "**==", "(~~)", "*-:)", "8-X",
	"8X", "=:)", "<):)", ";;)", ":*", ":-*", ":S", ":-S", "/:)",
	"/:-)", "8-|", "8|", "8-}", "8}", "(:|", "=P~", ":-?", ":?",
	"#-O", "#O", "=D>", "~:>", "%%-", "~O)", ":-L", ":L", "[-O<",
	"[O<", "@-)", "@)", "$-)", "$)", ">-)", ":-\"", ":^O", "B-(",
	"B(", ":)>-", "[-X", "[X", "\\:D/", ">:D<", "(%)", "=((", "#:-S",
	"#:S", "=))", "L-)", "L)", "<:-P", "<:P", ":-SS", ":SS", ":-W",
	":W", ":-<", ":<", ">:P", ">:-P", ">:/", ";))", ":-@", "^:)^",
	":-J", "(*)", ":GRIN:", ":-)", ":SMILE:", ":SAD:", ":EEK:",
	":SHOCK:", ":???:", "8)", "8-)", ":COOL:", ":LOL:", ":MAD:",
	":RAZZ:", ":OOPS:", ":CRY:", ":EVIL:", ":TWISTED:", ":ROLL:",
	":WINK:", ":!:", ":?:", ":IDEA:", ":ARROW:", ":NEUTRAL:",
	":MRGREEN:"];

接下来,我在页面中使用 XPath 查询语句搜索所有的含有 alt 属性的 <img> 元素。有关 XPath 查询语句的更多信息,请阅读操作所有有特定属性的元素

images = document.evaluate(
'//img[@alt]',
document,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null);

第三步,遍历所有的 <img>元素,检验 alt 属性是否与我们定义的表情符号匹配。由于一些表情符号含有字母,我用toUpperCase()方法在比较前把 alt 属性转换为大写。

for (var i = 0; i < images.snapshotLength; i++) {
	img = images.snapshotItem(i);
	alt = img.alt.toUpperCase())
	for (var j in smilies) {
		if (alt == smilies[j]) {
			// ...
		}
	}
}

最后,我创建了一个包含文字表情符号的新文本节点,然后用它替换掉已有的 <img> 元素。更多的信息,请阅读替换元素为新内容

replacement = document.createTextNode(alt);
img.parentNode.replaceChild(replacement, img);
← 案例:Dumb Quotes
案例:Zoom Textarea →