<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Lync.in &#187; Tech</title>
	<atom:link href="http://lync.in/category/tech/feed/" rel="self" type="application/rss+xml" />
	<link>http://lync.in</link>
	<description>Link the world.</description>
	<lastBuildDate>Thu, 19 Jan 2012 09:41:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>反SOPA黑屏行动：美国互联网大罢工</title>
		<link>http://lync.in/sopa-blackout-us-internet-strike/</link>
		<comments>http://lync.in/sopa-blackout-us-internet-strike/#comments</comments>
		<pubDate>Thu, 19 Jan 2012 08:54:40 +0000</pubDate>
		<dc:creator>Justice</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[blackout]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[PIPA]]></category>
		<category><![CDATA[reddit]]></category>
		<category><![CDATA[SOPA]]></category>
		<category><![CDATA[Wikipedia]]></category>

		<guid isPermaLink="false">http://lync.in/?p=1793</guid>
		<description><![CDATA[本文作者：Justice昨天，即 2012 年 1 月 18 日，美国互联网行业各大站点组织了大规模黑屏抗议活动，以对抗可能被国会通过的 SOPA 和 PIPA 两项法案。 先看一下维基百科对这两项法案的简介： SOPA和PIPA是美国众议院以及美国参议院所提出的两个重大议案，其中“SOPA”为《禁止网络盗版法案》（Stop Online Piracy Act）的英文简称，而“PIPA”则是《保护知识产权法案》（Preventing Real Online Threats to Economic Creativity and Theft of Intellectual Property Act of 2011）的英文简称。这两个法案在原本立意之下是要设法阻止有关外国网站对于版权的侵犯，但是在我们看来它实际上已经成为阻碍人民自由表达、破坏互联网价值观的工具。 简单来说，大致就是规定美国政府对封锁刊登没有专利权的内容的网站有了更大的权力，不管这些网站是否注册在美国。在该法案下，有了法庭的允许，美国司法部长能够要求搜索引擎，ISP 公司以及在线广告以及支付公司停止与那些国外的侵犯版权的网站合作，包括从网站链接以及该网站的 IP 地址来封锁该网站的名字。支持这项法案的主要是好莱坞和一些大唱片公司。而反对方主要是互联网公司。(via 百度百科) 这样做的后果，就是使得网站在提供内容时，特别是 UGC 时，不得不加大对内容的审查，严重影响互联网的知识共享和言论自由(参见我国的网络审查制度，当然审查的目的是不同的)。反对方认为，虽然知识产权理应得到保护，但是这样的法案只是把责任转嫁到了互联网企业头上，特别是那些需要发布大量 UGC 内容的站点，并不能够真正消灭盗版行为，却会让互联网的自由精神受到极大的伤害。 于是 Google 等美国互联网行业的各大站点选择在 1 月 18 日共同用行动来表达自己的愤慨。有些网站甚至是完全停止服务、首页黑屏发表申明来表达诉求。更有一些软硬件厂商、出版商为了自己产业上下游伙伴的利益，站出来进行声援。 下面就来看看，他们选择的抗议方式。 Google Wikipedia WordPress.org WordPress.com Mozilla reddit TechCrunch [...]]]></description>
			<content:encoded><![CDATA[<div class="feed-before" style="margin:15px 0; clear:both;">本文作者：<a href="http://lync.in/author/1/" title="Posts by Justice">Justice</a></div><p>昨天，即 2012 年 1 月 18 日，美国互联网行业各大站点组织了大规模黑屏抗议活动，以对抗可能被国会通过的 <acronym title="Stop Online Piracy Act">SOPA</acronym> 和 <acronym title="PROTECT IP Act">PIPA</acronym> 两项法案。</p>
<p>先看一下<a href="http://zh.wikipedia.org/wiki/Wikipedia:SOPA/Q">维基百科</a>对这两项法案的简介：</p>
<blockquote><p>SOPA和PIPA是美国众议院以及美国参议院所提出的两个重大议案，其中“SOPA”为《禁止网络盗版法案》（Stop Online Piracy Act）的英文简称，而“PIPA”则是《保护知识产权法案》（Preventing Real Online Threats to Economic Creativity and Theft of Intellectual Property Act of 2011）的英文简称。这两个法案在原本立意之下是要设法阻止有关外国网站对于版权的侵犯，但是在我们看来它实际上已经成为阻碍人民自由表达、破坏互联网价值观的工具。</p></blockquote>
<p>简单来说，大致就是规定美国政府对封锁刊登没有专利权的内容的网站有了更大的权力，不管这些网站是否注册在美国。在该法案下，有了法庭的允许，美国司法部长能够要求搜索引擎，ISP 公司以及在线广告以及支付公司停止与那些国外的侵犯版权的网站合作，包括从网站链接以及该网站的 IP 地址来封锁该网站的名字。支持这项法案的主要是好莱坞和一些大唱片公司。而反对方主要是互联网公司。(via <a href="http://baike.baidu.com/view/268902.htm#3" title="SOPA">百度百科</a>)</p>
<p><span id="more-1793"></span>这样做的后果，就是使得网站在提供内容时，特别是 <acronym title="User-generated content, 用户生成内容">UGC</acronym> 时，不得不加大对内容的审查，严重影响互联网的知识共享和言论自由(参见我国的网络审查制度，当然审查的目的是不同的)。反对方认为，虽然知识产权理应得到保护，但是这样的法案只是把责任转嫁到了互联网企业头上，特别是那些需要发布大量 UGC 内容的站点，并不能够真正消灭盗版行为，却会让互联网的自由精神受到极大的伤害。</p>
<p>于是 Google 等美国互联网行业的各大站点选择在 1 月 18 日共同用行动来表达自己的愤慨。有些网站甚至是完全停止服务、首页黑屏发表申明来表达诉求。更有一些软硬件厂商、出版商为了自己产业上下游伙伴的利益，站出来进行声援。</p>
<p>下面就来看看，他们选择的抗议方式。</p>
<h4>Google</h4>
<p><a href="http://lync.in/wp-content/uploads/2012/01/Google-Blackout.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2012/01/Google-Blackout-450x307.png" alt="Google" title="Google" width="450" height="307" class="aligncenter size-medium wp-image-1781" /></a></p>
<h4>Wikipedia</h4>
<p><a href="http://lync.in/wp-content/uploads/2012/01/Wikipedia-Blackout.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2012/01/Wikipedia-Blackout-450x255.png" alt="Wikipedia" title="Wikipedia" width="450" height="255" class="aligncenter size-medium wp-image-1776" /></a></p>
<h4>WordPress.org</h4>
<p><a href="http://lync.in/wp-content/uploads/2012/01/WordPress.org-Blackout.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2012/01/WordPress.org-Blackout-450x270.png" alt="WordPress.org" title="WordPress.org" width="450" height="270" class="aligncenter size-medium wp-image-1778" /></a></p>
<h4>WordPress.com</h4>
<p><a href="http://lync.in/wp-content/uploads/2012/01/WordPress.com-Blackout.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2012/01/WordPress.com-Blackout-450x230.png" alt="WordPress.com" title="WordPress.com" width="450" height="230" class="aligncenter size-medium wp-image-1777" /></a></p>
<h4>Mozilla</h4>
<p><a href="http://lync.in/wp-content/uploads/2012/01/Mozilla-Blackout.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2012/01/Mozilla-Blackout-450x265.png" alt="Mozilla" title="Mozilla" width="450" height="265" class="aligncenter size-medium wp-image-1799" /></a></p>
<h4>reddit</h4>
<p><a href="http://lync.in/wp-content/uploads/2012/01/reddit-Blackout.jpg" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2012/01/reddit-Blackout-450x235.jpg" alt="reddit" title="reddit" width="450" height="235" class="aligncenter size-medium wp-image-1809" /></a></p>
<h4>TechCrunch</h4>
<p><a href="http://lync.in/wp-content/uploads/2012/01/TechCrunch-Anti-SOPA-Labels.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2012/01/TechCrunch-Anti-SOPA-Labels-450x230.png" alt="TechCrunch" title="TechCrunch" width="450" height="230" class="aligncenter size-medium wp-image-1783" /></a></p>
<h4>Boing Boing</h4>
<p><a href="http://lync.in/wp-content/uploads/2012/01/BoingBoing-Blackout.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2012/01/BoingBoing-Blackout-450x239.png" alt="Boing Boing" title="Boing Boing" width="450" height="239" class="aligncenter size-medium wp-image-1780" /></a></p>
<h4>PHP</h4>
<p><a href="http://lync.in/wp-content/uploads/2012/01/PHP-Blackout.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2012/01/PHP-Blackout-450x166.png" alt="PHP" title="PHP" width="450" height="166" class="aligncenter size-medium wp-image-1802" /></a></p>
<h4>O'Reilly</h4>
<p><a href="http://lync.in/wp-content/uploads/2012/01/OReilly-Blackout.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2012/01/OReilly-Blackout-450x383.png" alt="O&#039;Reilly" title="O&#039;Reilly" width="450" height="383" class="aligncenter size-medium wp-image-1801" /></a></p>
<h4>Razer</h4>
<p><a href="http://lync.in/wp-content/uploads/2012/01/Razer-Blackout.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2012/01/Razer-Blackout-450x338.png" alt="Razer" title="Razer" width="450" height="338" class="aligncenter size-medium wp-image-1807" /></a></p>
<p>其他还有很多，就到这里吧。</p>
<p>关于互联网版权保护这个问题，的确是个两难的问题，尤其是对于 UGC 站点：网站是否需要对可能出现用户发表的侵权内容负法律责任？如果是，那网站不得不在用户内容发表前进行审核，这必然大大降低信息流动的效率，也极大地限制了互联网自由；如果不是，即按现行的“避风港”原则，只要 ISP 或网站在接到投诉后及时删除了侵权内容，就不视为盗版同案犯，但是这样的确对人们提供盗版利用盗版留下了可乘之机。不过，即使是实现审查，也难以保证所有内容都没有侵权可能，因为世界上并没有一个统一的机制来查询某些内容的版权是否属于当前发表人，真的要实施审查也会遗漏大量的侵权行为，如果这样都要怪罪于网站或 ISP，那的确会使得他们无法生存，所以 SOPA 会激起他们如此强烈的反抗。</p>
<div class="feed-after" style="margin:15px 0; clear:both;"><a href="http://lync.in">Lync.in</a> 是一个多人协作博客，欢迎您 <a href="http://lync.in/sopa-blackout-us-internet-strike/">查看原文</a> 以获得更好的阅读体验。</div>]]></content:encoded>
			<wfw:commentRss>http://lync.in/sopa-blackout-us-internet-strike/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>什么是Web 3.0？</title>
		<link>http://lync.in/what-is-web-3-0/</link>
		<comments>http://lync.in/what-is-web-3-0/#comments</comments>
		<pubDate>Fri, 16 Dec 2011 14:37:30 +0000</pubDate>
		<dc:creator>Justice</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[OWL]]></category>
		<category><![CDATA[RDF]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[Web 3.0]]></category>
		<category><![CDATA[语义网]]></category>

		<guid isPermaLink="false">http://lync.in/?p=1761</guid>
		<description><![CDATA[本文作者：Justice发一篇凑凑数，前几天在知乎看到一个问题：在你眼中 Web 3.0 是什么？恰好我对此稍微有些了解，于是组织了下语言尝试回答了一下。在这里也分享一下吧。 传统的 Web 以内容为核心，网站负责提供各种内容 (新闻、小说、视频等等) 吸引用户，用户主要是找到自己感兴趣的内容后较被动地接受信息。 Web 2.0 之所以加引号，因为毕竟没有所谓的 “Web 2.0 标准”。在广泛的共识中，Web 2.0 以用户为核心，用户自己共享内容 (User-generated content，比如发表微博、博客文章、podcast、上传视频、图片到 Youtube、Flickr 等等)，Web 服务商提供各类 Web 应用来承载用户的内容、组织他们之间的交互。相比传统 Web，Web 2.0 的各种服务更专注于用户，提供更好的交互体验，同时各种服务商提供不同的开放 API，使得一些个性化 Web 应用的实现成为可能。 Web 3.0 应该说 Web 3.0 这个说法究竟指什么还没能得到广泛的共识，大家对其定义有很多不同的理解。 其中，较多人把语义网 (Semantic Web) 看作 Web 3.0。这是 WWW 之父 Tim Berners-Lee 提出的，对当前的 Web 内容组织方式可以算是一种颠覆：目前互联网上的信息绝大多数都是以 HTML 格式承载的，是将各个网站背后数据库中隐藏着的结构化信息编码为浏览器能够渲染的 HTML 代码 (在这过程中浏览器并不知道网页内容的真正含义，只知道如何渲染)，最终渲染为人类可读的信息。这就有一个非常大的局限：真正在互联网上能够被共享的信息绝大多数都是半结构化的 HTML [...]]]></description>
			<content:encoded><![CDATA[<div class="feed-before" style="margin:15px 0; clear:both;">本文作者：<a href="http://lync.in/author/1/" title="Posts by Justice">Justice</a></div><p>发一篇凑凑数，前几天在<a href="http://www.zhihu.com/" title="知乎" target="_blank">知乎</a>看到一个问题：<a href="http://www.zhihu.com/question/19965749" title="在你眼中 Web 3.0 是什么？" target="_blank">在你眼中 Web 3.0 是什么</a>？恰好我对此稍微有些了解，于是组织了下语言尝试回答了一下。在这里也分享一下吧。</p>
<h3>传统的 Web</h3>
<p>以内容为核心，网站负责提供各种内容 (新闻、小说、视频等等) 吸引用户，用户主要是找到自己感兴趣的内容后较被动地接受信息。</p>
<h3>Web 2.0</h3>
<p>之所以加引号，因为毕竟没有所谓的 “Web 2.0 标准”。在广泛的共识中，Web 2.0 以用户为核心，用户自己共享内容 (User-generated content，比如发表微博、博客文章、podcast、上传视频、图片到 Youtube、Flickr 等等)，Web 服务商提供各类 Web 应用来承载用户的内容、组织他们之间的交互。相比传统 Web，Web 2.0 的各种服务更专注于用户，提供更好的交互体验，同时各种服务商提供不同的开放 API，使得一些个性化 Web 应用的实现成为可能。<br />
<span id="more-1761"></span></p>
<h3>Web 3.0</h3>
<p>应该说 Web 3.0 这个说法究竟指什么还没能得到广泛的共识，大家对其定义有很多不同的理解。</p>
<p>其中，较多人把<a href="http://www.w3.org/2001/sw/" title="Semantic Web" target="_blank">语义网 (Semantic Web)</a> 看作 Web 3.0。这是 WWW 之父 Tim Berners-Lee 提出的，对当前的 Web 内容组织方式可以算是一种颠覆：目前互联网上的信息绝大多数都是以 HTML 格式承载的，是将各个网站背后数据库中隐藏着的结构化信息编码为浏览器能够渲染的 HTML 代码 (在这过程中浏览器并不知道网页内容的真正含义，只知道如何渲染)，最终渲染为人类可读的信息。这就有一个非常大的局限：真正在互联网上能够被共享的信息绝大多数都是半结构化的 HTML 内容，因为这些信息是海量的，要把它们整合利用必须通过自动化的手段，但机器又难以理解 HTML 中实际给人类阅读的内容。所以现在各种自然语言处理、人工智能的方法被广泛应用于互联网内容的处理，力图让机器能够还原各种数据之间的关系，更智能地加以组织利用 (这里说的不是小说等自然语言文本，而主要是一些原本结构化的信息，在转换为 HTML 的过程中丢失了原来的语义。例如电子商务网站上某个照相机的各项参数，机器很难准确识别一个网页中哪些部分是用来描述这个相机的，而哪些又是广告或是描述其他产品的)。</p>
<p>而语义网的概念是从另一个方向来解决这个问题：不用机器来做基于人工智能的识别，而是在提供内容的时候，就用明确的、机器可读、有标准语义可依的方式来将所提供内容的语义描述清楚，建立完善的机制来描述互联网上的各种资源。在资源的标准化描述之上，建立标准的语义推理以及信任体系，为各种智能代理 (intelligent agent) 提供可靠的信息、标准化的交互模式，从而改变人们访问 Web 的方式 (比如比 Siri 更为强大且智能得多的应用，记得有个例子是当你牙疼要看牙医，智能代理会自动检查你的日程安排、寻找合适的有空的牙医、与诊所的代理进行自动登记，为你安排出行)。</p>
<p>目前已经有几项技术成为了语义网的<a href="http://www.w3.org/standards/semanticweb/" title="语义网标准" target="_blank">推荐标准</a>，其中在 Unicode、URI、XML 等基础设施层之上已经成为标准的有：</p>
<ul>
<li>RDF/RDFS，资源描述框架及基于此的一套词汇集</li>
<li>OWL，Web 本体语言，用来在 RDF 之上提供基于本体的词汇集</li>
<li>SPARQL，用于查询 RDF 文档</li>
</ul>
<p>在这些层次之上，还有推理、信任等层还没有确定的标准。</p>
<p>语义网能否最终发展成为其愿景所描述的状态，还很难说。因为从目前来看，各种开放 API 已经可以组织成甚为丰富的 Web 应用，而像语义网愿景中描述的可以自己搜寻识别可用服务进行组合完成用户需求的超级智能代理，显得有些遥遥无期。此外，语义网的标准的推动也有很大难度，率先“拥抱”这些标准短期内并不会给互联网公司带来多大的好处，且语义网的未来又被很多人看衰，最终结局会不会惨淡收场也尚未可知。批评语义网的观念太过理想化、认为其完全不可能成为现实的人大有人在。</p>
<p>回到 Web 3.0，语义网只是其中一种说法，也有人把语义网称为 “Web 4.0”。其它观点个人不太了解，也就留待其他人补充吧。</p>
<p>至于盈利模式，可以说目前互联网上的主要盈利模式还是广告。Web 不管是几点零，之前的模式也不是说就被取代了。比如说现在我们有那么多 Web 2.0 网站，但是提供内容服务的网站同样可以很红火，人们照样上新浪看新闻、上优酷看电视剧。只要用户能看得见的地方，都可以通过广告来获得利润。此外还有电子商务，不管 Web 几点零，通过交易总是可以获取利润的，只是交易的交互模式可能发生改变。另外一个途径是提供收费服务 (比如提供云计算服务、提供网游等娱乐服务)，这和电商类似，只是交易的内容不是实体。</p>
<p>欢迎探讨。 </p>
<hr />
PS. 有需要知乎邀请的可以找我。</p>
<div class="feed-after" style="margin:15px 0; clear:both;"><a href="http://lync.in">Lync.in</a> 是一个多人协作博客，欢迎您 <a href="http://lync.in/what-is-web-3-0/">查看原文</a> 以获得更好的阅读体验。</div>]]></content:encoded>
			<wfw:commentRss>http://lync.in/what-is-web-3-0/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>在 JavaScript 中判断“空值”</title>
		<link>http://lync.in/check-empty-value-in-javascript/</link>
		<comments>http://lync.in/check-empty-value-in-javascript/#comments</comments>
		<pubDate>Thu, 08 Dec 2011 15:49:23 +0000</pubDate>
		<dc:creator>Justice</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[data type]]></category>
		<category><![CDATA[empty value]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[toString]]></category>

		<guid isPermaLink="false">http://lync.in/?p=1718</guid>
		<description><![CDATA[本文作者：Justice有时候我们会遇到这样的情况：在一些前端控件要提交数据到服务器端的数据验证过程中，需要判断提交的数据是否为空。如果是普通表单的字符串数据，只需要在 trim 后判断 length 即可，而这里需要的数据可以是各种不同的类型(数字、字符串、数组、对象等等)，通过 JSON.stringify(data) 进行序列化后再传递。 在这里定义如下的数据值为“空值”： undefined null 空字符串及纯空白字符串：''、'&#160;&#160;&#160;&#160;' 等。 空数组：[] 空对象：{} 对于除此以外的数据值，均认为不为空。 其中 null 和 undefined 很容易识别，但对于其他类型，我们须要得到其数据类型才能用相应的方法去检测数据是否为空。最容易想到的方法就是利用 typeof 操作符： JavaScript1 2 3 if&#40;typeof data === 'number'&#41; &#123; //deal with numbers &#125; 但 typeof 返回的类型字符串只有 'object'、'function'、'number'、'boolean'、'string'、'undefined' 这六种，很多原生对象如 Date、RegExp 对象无法与用 {} 创建的对象进行区分。另外，typeof 对于一些基本数据类型如 (String、Number、Boolean) 与其对应的基本包装类型数据会分别返回不同值，如： JavaScript1 2 3 4 5 6 console.log&#40;typeof false&#41;; //'boolean' [...]]]></description>
			<content:encoded><![CDATA[<div class="feed-before" style="margin:15px 0; clear:both;">本文作者：<a href="http://lync.in/author/1/" title="Posts by Justice">Justice</a></div><p>有时候我们会遇到这样的情况：在一些前端控件要提交数据到服务器端的数据验证过程中，需要判断提交的数据是否为空。如果是普通表单的字符串数据，只需要在 <code>trim</code> 后判断 <code>length</code> 即可，而这里需要的数据可以是各种不同的类型(数字、字符串、数组、对象等等)，通过 <code>JSON.stringify(<var>data</var>)</code> 进行序列化后再传递。</p>
<p>在这里定义如下的数据值为“空值”：</p>
<ul>
<li>undefined</li>
<li>null</li>
<li>空字符串及纯空白字符串：''、'&nbsp;&nbsp;&nbsp;&nbsp;' 等。</li>
<li>空数组：[]</li>
<li>空对象：{}</li>
</ul>
<p>对于除此以外的数据值，均认为不为空。</p>
<p><span id="more-1718"></span>其中 <code>null</code> 和 <code>undefined</code> 很容易识别，但对于其他类型，我们须要得到其数据类型才能用相应的方法去检测数据是否为空。最容易想到的方法就是利用 <code>typeof</code> 操作符：</p>

<div class="wp_syntax_wrapper"><span class="wp_syntax_lang">JavaScript</span><div class="wp_syntax" style="background:#2c2c2c; overflow:auto"><table><tr><td class="line_numbers" style="color:#eee; background:#3c3c3c"><pre>1
2
3
</pre></td><td class="code"><pre class="javascript" style="color: #FFF; font-family:&quot;Consolas&quot;,monospace,&quot;Courier New&quot;"><span style="color: #3D9EDD;">if</span><span style="color: #CCC;">&#40;</span><span style="color: #3D9EDD;">typeof</span> data <span style="color: #CCC;">===</span> <span style="color: #7ACC00;">'number'</span><span style="color: #CCC;">&#41;</span> <span style="color: #CCC;">&#123;</span>
  <span style="color: #999;">//deal with numbers</span>
<span style="color: #CCC;">&#125;</span></pre></td></tr></table></div></div>

<p>但 <code>typeof</code> 返回的类型字符串只有 'object'、'function'、'number'、'boolean'、'string'、'undefined' 这六种，很多原生对象如 Date、RegExp 对象无法与用 {} 创建的对象进行区分。另外，<code>typeof</code> 对于一些基本数据类型如 (String、Number、Boolean) 与其对应的基本包装类型数据会分别返回不同值，如：</p>

<div class="wp_syntax_wrapper"><span class="wp_syntax_lang">JavaScript</span><div class="wp_syntax" style="background:#2c2c2c; overflow:auto"><table><tr><td class="line_numbers" style="color:#eee; background:#3c3c3c"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="javascript" style="color: #FFF; font-family:&quot;Consolas&quot;,monospace,&quot;Courier New&quot;">console.<span style="color: #FFF;">log</span><span style="color: #CCC;">&#40;</span><span style="color: #3D9EDD;">typeof</span> <span style="color: #F2B646;">false</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span> <span style="color: #999;">//'boolean'</span>
console.<span style="color: #FFF;">log</span><span style="color: #CCC;">&#40;</span><span style="color: #3D9EDD;">typeof</span> <span style="color: #F2B646;">new</span> Boolean<span style="color: #CCC;">&#40;</span><span style="color: #F2B646;">false</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span> <span style="color: #999;">//'object'</span>
console.<span style="color: #FFF;">log</span><span style="color: #CCC;">&#40;</span><span style="color: #3D9EDD;">typeof</span> <span style="color: #FFCC00;">1</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span> <span style="color: #999;">//'number'</span>
console.<span style="color: #FFF;">log</span><span style="color: #CCC;">&#40;</span><span style="color: #3D9EDD;">typeof</span> <span style="color: #F2B646;">new</span> Number<span style="color: #CCC;">&#40;</span><span style="color: #FFCC00;">1</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span> <span style="color: #999;">//'object'</span>
console.<span style="color: #FFF;">log</span><span style="color: #CCC;">&#40;</span><span style="color: #3D9EDD;">typeof</span> <span style="color: #7ACC00;">''</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span> <span style="color: #999;">//'string'</span>
console.<span style="color: #FFF;">log</span><span style="color: #CCC;">&#40;</span><span style="color: #3D9EDD;">typeof</span> <span style="color: #F2B646;">new</span> String<span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">''</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span> <span style="color: #999;">//'object'</span></pre></td></tr></table></div></div>

<p>这对我们的判断也有一定的影响。</p>
<p>用 <code>instanceof</code>？这只能判断对象，而且存在<a href="https://developer.mozilla.org/en/JavaScript/Reference/Operators/instanceof#section_5" title="instanceof and multiple context">多 frame 时多个同类对象不共享 prototype</a> 的问题，从其他 frame 中取得的对象无法正确判断。</p>
<p>还好，还有一个最简单也最可靠的方法：<code>Object.prototype.toString</code>。对于不同类型的数据，这个方法可以返回 '[object Object]'、'[object Array]'、'[object String]' 这样的字符串，非常方便判断。需要注意的是，在 IE8 及其以下浏览器中，这个方法对于 <code>null</code>、<code>undefined</code>、<code>window</code> 等都会返回 '[object Object]'，不过还好，这并不影响我们使用它判断空对象。</p>
<p>下面直接上代码，说明就看注释吧。</p>

<div class="wp_syntax_wrapper"><span class="wp_syntax_lang">JavaScript</span><div class="wp_syntax" style="background:#2c2c2c; overflow:auto"><table><tr><td class="line_numbers" style="color:#eee; background:#3c3c3c"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="code"><pre class="javascript" style="color: #FFF; font-family:&quot;Consolas&quot;,monospace,&quot;Courier New&quot;"><span style="color: #999;">// with jQuery</span>
<span style="color: #F2B646;">var</span> isEmptyValue <span style="color: #CCC;">=</span> <span style="color: #F2B646;">function</span><span style="color: #CCC;">&#40;</span>value<span style="color: #CCC;">&#41;</span> <span style="color: #CCC;">&#123;</span>
    <span style="color: #F2B646;">var</span> type<span style="color: #CCC;">;</span>
    <span style="color: #3D9EDD;">if</span><span style="color: #CCC;">&#40;</span>value <span style="color: #CCC;">==</span> <span style="color: #F2B646;">null</span><span style="color: #CCC;">&#41;</span> <span style="color: #CCC;">&#123;</span> <span style="color: #999;">// 等同于 value === undefined || value === null</span>
        <span style="color: #3D9EDD;">return</span> <span style="color: #F2B646;">true</span><span style="color: #CCC;">;</span>
    <span style="color: #CCC;">&#125;</span>
    type <span style="color: #CCC;">=</span> Object.<span style="color: #FFF;">prototype</span>.<span style="color: #FFF;">toString</span>.<span style="color: #FFF;">call</span><span style="color: #CCC;">&#40;</span>value<span style="color: #CCC;">&#41;</span>.<span style="color: #FFF;">slice</span><span style="color: #CCC;">&#40;</span><span style="color: #FFCC00;">8</span><span style="color: #CCC;">,</span> <span style="color: #CCC;">-</span><span style="color: #FFCC00;">1</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span>
    <span style="color: #3D9EDD;">switch</span><span style="color: #CCC;">&#40;</span>type<span style="color: #CCC;">&#41;</span> <span style="color: #CCC;">&#123;</span>
        <span style="color: #3D9EDD;">case</span> <span style="color: #7ACC00;">'String'</span><span style="color: #CCC;">:</span>
            <span style="color: #3D9EDD;">return</span> <span style="color: #CCC;">!!</span>$.<span style="color: #FFF;">trim</span><span style="color: #CCC;">&#40;</span>value<span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span>
        <span style="color: #3D9EDD;">case</span> <span style="color: #7ACC00;">'Array'</span><span style="color: #CCC;">:</span>
            <span style="color: #3D9EDD;">return</span> <span style="color: #CCC;">!</span>value.<span style="color: #FFF;">length</span><span style="color: #CCC;">;</span>
        <span style="color: #3D9EDD;">case</span> <span style="color: #7ACC00;">'Object'</span><span style="color: #CCC;">:</span>
            <span style="color: #3D9EDD;">return</span> $.<span style="color: #FFF;">isEmptyObject</span><span style="color: #CCC;">&#40;</span>value<span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span> <span style="color: #999;">// 普通对象使用 for...in 判断，有 key 即为 false</span>
        <span style="color: #F2B646;">default</span><span style="color: #CCC;">:</span>
            <span style="color: #3D9EDD;">return</span> <span style="color: #F2B646;">false</span><span style="color: #CCC;">;</span> <span style="color: #999;">// 其他对象均视作非空</span>
    <span style="color: #CCC;">&#125;</span>
<span style="color: #CCC;">&#125;</span><span style="color: #CCC;">;</span></pre></td></tr></table></div></div>

<p>对于不同 <code>value</code> 值，我做了个测试，看看对于不同值分别取 <code>Object.prototype.toString</code>、<code>typeof</code>、<code>isEmpty</code> 的结果。测试结果可以到下面这个地址围观：<br />
<a href="http://jsfiddle.net/Justineo/hFKRt/" title="isEmpty Test">http://jsfiddle.net/Justineo/hFKRt/</a></p>
<p>除了 IE8 及其以下浏览器外，其他浏览器结果基本一致，下面就贴下主要的 2 种结果吧。下图为 Firefox 下的结果，而在 Chrome 下，<code>window</code> 字符串化后的结果是 'object global'，和 Firefox 下稍有不同，但在判断空值时问题不大。<br />
<div id="attachment_1746" class="wp-caption aligncenter" style="width: 509px"><img src="http://lync.in/wp-content/uploads/2011/12/isempty-fx.png" alt="Firefox" title="isempty-fx" width="499" height="505" class="size-full wp-image-1746" /><p class="wp-caption-text">Firefox 下的结果</p></div></p>
<p>IE8 及以下唯一会误判的是 <code>alert</code>，不过这对于数据验证来说应该也可以接受。<br />
<div id="attachment_1752" class="wp-caption aligncenter" style="width: 509px"><img src="http://lync.in/wp-content/uploads/2011/12/isempty-ie.png" alt="IE8-" title="isempty-ie" width="499" height="505" class="size-full wp-image-1752" /><p class="wp-caption-text">IE8 及以下的结果</p></div></p>
<div class="feed-after" style="margin:15px 0; clear:both;"><a href="http://lync.in">Lync.in</a> 是一个多人协作博客，欢迎您 <a href="http://lync.in/check-empty-value-in-javascript/">查看原文</a> 以获得更好的阅读体验。</div>]]></content:encoded>
			<wfw:commentRss>http://lync.in/check-empty-value-in-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>命令行邮件工具SendEmail</title>
		<link>http://lync.in/tool-sendemail/</link>
		<comments>http://lync.in/tool-sendemail/#comments</comments>
		<pubDate>Thu, 24 Nov 2011 08:43:59 +0000</pubDate>
		<dc:creator>Sinnyn</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[139邮箱]]></category>
		<category><![CDATA[SendEmail]]></category>
		<category><![CDATA[免费短信]]></category>
		<category><![CDATA[命令行工具]]></category>

		<guid isPermaLink="false">http://lync.in/?p=1715</guid>
		<description><![CDATA[本文作者：Sinnyn简单介绍下这个命令行发送邮件的工具SendEmail，它的作用就是发送邮件。很多年前有一个流传很广的小工具bmail也是这个作用，只不过近些年来随着各个邮件ISP要求给发送服务器进行身份验证，bmail在停止开发后也就因此而失效。SendEmail包含了这个功能，通过一系列管道的方式，SendEmail也就能起到很多的功能。比如向139邮箱的发送邮件，利用139邮箱的免费短信提醒从而起到免费短信的作用。 这里是SendEmail的项目地址，有Linux版和Windows版，同样的项目目录之下还有很多别的小工具，也都是很好用的工具。 Lync.in 是一个多人协作博客，欢迎您 查看原文 以获得更好的阅读体验。]]></description>
			<content:encoded><![CDATA[<div class="feed-before" style="margin:15px 0; clear:both;">本文作者：<a href="http://lync.in/author/3/" title="Posts by Sinnyn">Sinnyn</a></div><p>简单介绍下这个命令行发送邮件的工具SendEmail，它的作用就是发送邮件。很多年前有一个流传很广的小工具bmail也是这个作用，只不过近些年来随着各个邮件ISP要求给发送服务器进行身份验证，bmail在停止开发后也就因此而失效。SendEmail包含了这个功能，通过一系列管道的方式，SendEmail也就能起到很多的功能。比如向139邮箱的发送邮件，利用139邮箱的免费短信提醒从而起到免费短信的作用。</p>
<p><a href="http://caspian.dotconf.net/menu/Software/SendEmail/" target="_blank">这里</a>是SendEmail的项目地址，有Linux版和Windows版，同样的项目目录之下还有很多别的小工具，也都是很好用的工具。</p>
<div class="feed-after" style="margin:15px 0; clear:both;"><a href="http://lync.in">Lync.in</a> 是一个多人协作博客，欢迎您 <a href="http://lync.in/tool-sendemail/">查看原文</a> 以获得更好的阅读体验。</div>]]></content:encoded>
			<wfw:commentRss>http://lync.in/tool-sendemail/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>利用Yahoo! Pipes制作全文RSS源</title>
		<link>http://lync.in/create-full-text-rss-feedsg-with-yahoo-pipes/</link>
		<comments>http://lync.in/create-full-text-rss-feedsg-with-yahoo-pipes/#comments</comments>
		<pubDate>Mon, 21 Nov 2011 11:15:59 +0000</pubDate>
		<dc:creator>Justice</dc:creator>
				<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[full text]]></category>
		<category><![CDATA[RSS]]></category>
		<category><![CDATA[Sina]]></category>
		<category><![CDATA[Yahoo! Pipes]]></category>

		<guid isPermaLink="false">http://lync.in/?p=1689</guid>
		<description><![CDATA[本文作者：Justice今天要介绍的东西已经不算什么新的玩意儿了，几年前就有人各种介绍了，最近已经冷下来了。不过在现在恶劣的网络环境当中，这样的工具变得越来越有实用价值了。 我们知道通过 Google Reader 订阅的 RSS 源只要有更新就会被 Google 记录，从而即使原文被删除，Reader 中还是有原文的记录，于是我们甚至还可以通过 Google Reader API 获取历史 RSS 条目。所以像韩寒那样的博客在 Reader 中总是能够读到原文。 但是最近我发现新浪博客的停止了全文 RSS 源输出，而仅仅输出摘要，导致一旦文章被删，Reader 里也只剩下摘要了，再要找原文看就比较麻烦了。这时候我想到了 Yahoo! Pipes 这个异常强大好用的数据整合工具，给博客来做个全文 RSS 源应该是轻而易举吧。结果在创建过程当中，发现新浪博客在博客文章源码中做了一些很恶心的处理(见下图)，会抽出文章中的几段文本，随即插入文章中，再设定样式将其隐藏，导致用普通的全文抓取的方法来制作 RSS 源会生成不少重复的文本块，插入文章中以后根本无法正常阅读。 幸好这“防御”措施还有规律可循，用正则替换就能轻松绕过，最后还是可以生成完美的全文 RSS 源的。下面就来分享一下这个过程吧，就以韩寒的博客为例： 首先要注册个 Yahoo! ID (废话)，在 Pipes 主页 上方可以找到 “Create a pipe” 按钮，点之，就进入了 Pipe 编辑界面。左侧是模块选择区域，右侧是编辑区域。将一个个模块拖动到右侧相连接，控制模块的逻辑组合与输入输出，我们就可以对原始的数据进行一系列复杂的操作。 从左边找到 Sources > Fetch Feed 拖动到右侧编辑区域，添加一个 Feed 模块。在 URL 输入框里填入博客的 [...]]]></description>
			<content:encoded><![CDATA[<div class="feed-before" style="margin:15px 0; clear:both;">本文作者：<a href="http://lync.in/author/1/" title="Posts by Justice">Justice</a></div><p>今天要介绍的东西已经不算什么新的玩意儿了，几年前就有人各种介绍了，最近已经冷下来了。不过在现在恶劣的网络环境当中，这样的工具变得越来越有实用价值了。</p>
<p>我们知道通过 <a href="http://www.google.com/reader/" title="Google Reader">Google Reader</a> 订阅的 <abbr title="Really Simple Syndication, aka RDF Site Summary">RSS</abbr> 源只要有更新就会被 Google 记录，从而即使原文被删除，Reader 中还是有原文的记录，于是我们甚至还可以<a href="http://lync.in/get-all-rss-items-by-google-reader/" title="通过 Google Reader API 获取历史 RSS 条目">通过 Google Reader API 获取历史 RSS 条目</a>。所以像<a href="http://blog.sina.com.cn/twocold" title="韩寒">韩寒</a>那样的博客在 Reader 中总是能够读到原文。</p>
<p>但是最近我发现新浪博客的停止了全文 RSS 源输出，而仅仅输出摘要，导致一旦文章被删，Reader 里也只剩下摘要了，再要找原文看就比较麻烦了。这时候我想到了 Yahoo! Pipes 这个异常强大好用的数据整合工具，给博客来做个全文 RSS 源应该是轻而易举吧。结果在创建过程当中，发现新浪博客在博客文章源码中做了一些很恶心的处理(见下图)，会抽出文章中的几段文本，随即插入文章中，再设定样式将其隐藏，导致用普通的全文抓取的方法来制作 RSS 源会生成不少重复的文本块，插入文章中以后根本无法正常阅读。<span id="more-1689"></span><br />
<a href="http://lync.in/wp-content/uploads/2011/11/sina-mess.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2011/11/sina-mess-450x327.png" alt="新浪博客文章源码" title="新浪博客文章源码" width="450" height="327" class="aligncenter size-medium wp-image-1693" /></a></p>
<p>幸好这“防御”措施还有规律可循，用正则替换就能轻松绕过，最后还是可以生成完美的全文 RSS 源的。下面就来分享一下这个过程吧，就以韩寒的博客为例：</p>
<ol>
<li>首先要注册个 Yahoo! ID (废话)，在 <a href="http://pipes.yahoo.com/pipes/" title="Yahoo! Pipes">Pipes 主页</a> 上方可以找到 “Create a pipe” 按钮，点之，就进入了 Pipe 编辑界面。左侧是模块选择区域，右侧是编辑区域。将一个个模块拖动到右侧相连接，控制模块的逻辑组合与输入输出，我们就可以对原始的数据进行一系列复杂的操作。</li>
<li>从左边找到 Sources > Fetch Feed 拖动到右侧编辑区域，添加一个 Feed 模块。在 URL 输入框里填入博客的 RSS 源地址：<br />
<code><a href="http://blog.sina.com.cn/rss/1191258123.xml" title="韩寒博客 RSS 源">http://blog.sina.com.cn/rss/1191258123.xml</a></code><br />
这样这个模块输出的就是这个 RSS 源的数据，一切顺利的话，底部的 Debugger 区域就能显示出文章标题列表了。<br />
<a href="http://lync.in/wp-content/uploads/2011/11/fetch_feed.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2011/11/fetch_feed-450x299.png" alt="Added Fetch Feed" title="Added Fetch Feed" width="450" height="299" class="aligncenter size-medium wp-image-1700" /></a></li>
<li>找到 Operators > Loop 模块拖到右边，并将 Fetch Feed 模块的输出拖放到此模块的输入位置，表示对输入的列表元素进行循环处理。编辑具体处理的逻辑需要从左侧再拖动 Sources > Fetch Page 到 Loop 模块内，URL 选 item.link。下面的 Cut content from ... to ... 是用来截取需要的文本的。也就是把从上面 Feed 模块中得到的每个 item 的 URL 输入 Fetch Page 模块中，可以输出这个 URL 的源码，我们需要找到标志文章开始和结束的代码标记，从而程序可以将这之间的代码取出来作为输出数据。分析一下新浪博客的文章页源码，我们可以分别将开始和结束标记填为 “<code>&lt;div id="sina_keyword_ad_area2" class="articalContent  "&gt;”</code> 及 “<code>&lt;INS&gt;来源：</code>”。Split using delimiter 这项只需留空即可，我们不需要再将内容进行分隔。最后，我们要将模块的输出重命名为 item.description。到这里我们已经用全文内容作为 description 将原来 feed 中的摘要覆盖了。接下来就要处理上面说到的新浪博客的重复文本问题了。<br />
<a href="http://lync.in/wp-content/uploads/2011/11/loop_page.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2011/11/loop_page-450x306.png" alt="Loop Page" title="Loop Page" width="450" height="306" class="aligncenter size-medium wp-image-1704" /></a></li>
<li>通过观察，新浪博客的隐藏文本都是放在一个 class 属性值为 MASS + 12 位字母数字的 span 元素中，所以只要将这个元素中的内容都过滤掉，就可以得到“干净”的文章内容了。从左边找 Operators > Loop 拖到右边，将上面模块的输出绑定。左边找到 String > String Regex 模块拖入 Loop 模块。For each 后面选择 item.description，来对前面的输出中的每篇文章内容做循环处理。replace 后面填入这个正则表达式：<code>&lt;span class='MASS[a-zA-Z0-9]+'&gt;[^&gt;]*&lt;/span&gt;</code>，<br />
然后将最下方的 assign results to 选为 item.description。最后，把整个模块的输出连到 Pipe output 模块，编辑过程就大功告成了。<br />
<a href="http://lync.in/wp-content/uploads/2011/11/filter_content.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2011/11/filter_content-450x268.png" alt="Filter Content" title="Filter Content" width="450" height="268" class="aligncenter size-medium wp-image-1709" /></a></li>
<li>点击右上角 Save 来保存配置，点击上方的 “Run Pipe...”，查看运行结果。在输出页面找到 Get as RSS 链接，这个链接的地址就是一个可用的 RSS 源了，在 Google Reader 中订阅吧！</li>
</ol>
<p>这仅仅是对 Yahoo! Pipes 的一个简单应用，其实还有更多强大的功能我都没研究过，但仅仅利用这么一些初级的功能，我们已经可以得到一些实用的输出了。下面是我已经创建好的韩寒博客的全文 RSS 源，请自取。<br />
Pipes 原始地址：<br />
<a href="http://pipes.yahoo.com/pipes/pipe.run?_id=86b82a340ea9fa635fb4732e0e2819f1&#038;_render=rss" title="韩寒博客全文 RSS 源">http://pipes.yahoo.com/pipes/pipe.run?_id=86b82a340ea9fa635fb4732e0e2819f1&#038;_render=rss</a><br />
Feedburner 烧制的源：<br />
<a href="http://feeds.feedburner.com/twocoldfulltext" title="韩寒博客全文 RSS 源">http://feeds.feedburner.com/twocoldfulltext</a></p>
<p>Enjoy~</p>
<div class="feed-after" style="margin:15px 0; clear:both;"><a href="http://lync.in">Lync.in</a> 是一个多人协作博客，欢迎您 <a href="http://lync.in/create-full-text-rss-feedsg-with-yahoo-pipes/">查看原文</a> 以获得更好的阅读体验。</div>]]></content:encoded>
			<wfw:commentRss>http://lync.in/create-full-text-rss-feedsg-with-yahoo-pipes/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Firefox下用Flash上传文件的session陷阱</title>
		<link>http://lync.in/session-trap-on-uploading-files-using-flash-in-firefox/</link>
		<comments>http://lync.in/session-trap-on-uploading-files-using-flash-in-firefox/#comments</comments>
		<pubDate>Sat, 29 Oct 2011 10:47:17 +0000</pubDate>
		<dc:creator>Justice</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[cookie]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[SWFUpload]]></category>

		<guid isPermaLink="false">http://lync.in/?p=1659</guid>
		<description><![CDATA[本文作者：Justice这几天在项目中遇到的问题，稍微整理一下。 HTML 表单是我们常用来进行浏览器与服务器数据交互的途径，除了传递普通的文本数据，通过 type="file" 的 input 控件，我们还可以向服务器发送文件。但在目前的实际使用中，基于 HTML 表单的文件选择框有着下面两个不尽人意之处： 一次只能选择一个文件 上传过程中，不能获取文件上传的进度 对于文件选择的数量，事实上在 HTML4.01 规范中对文件选择框的描述虽然没有明确提出可以同时选择多个文件，但从其用词可以看出，文件选择控件事实上是应该支持同时选择多个文件的： This control type allows the user to select files so that their contents may be submitted with a form. The INPUT element is used to create a file select control. 可以看见，“文件”一词使用的是复数形式。同时，RFC 1867: Form-based File Upload in HTML 的 3.3 节中，也有这样的描述： If [...]]]></description>
			<content:encoded><![CDATA[<div class="feed-before" style="margin:15px 0; clear:both;">本文作者：<a href="http://lync.in/author/1/" title="Posts by Justice">Justice</a></div><p>这几天在项目中遇到的问题，稍微整理一下。</p>
<p><abbr title="Hypertext Markup Language">HTML</abbr> 表单是我们常用来进行浏览器与服务器数据交互的途径，除了传递普通的文本数据，通过 <code>type="file"</code> 的 input 控件，我们还可以向服务器发送文件。但在目前的实际使用中，基于 HTML 表单的文件选择框有着下面两个不尽人意之处：</p>
<ol>
<li>一次只能选择一个文件</li>
<li>上传过程中，不能获取文件上传的进度</li>
</ol>
<p>对于文件选择的数量，事实上在 <a href="http://www.w3.org/TR/html401/interact/forms.html#file-select" title="File Select">HTML4.01 规范</a>中对文件选择框的描述虽然没有明确提出可以同时选择多个文件，但从其用词可以看出，文件选择控件事实上是<em>应该</em>支持同时选择多个文件的：<span id="more-1659"></span></p>
<blockquote><p>
This control type allows the user to select <em>files</em> so that their contents may be submitted with a form. The INPUT element is used to create a file select control.
</p></blockquote>
<p>可以看见，“文件”一词使用的是复数形式。同时，<a href="http://tools.ietf.org/html/rfc1867" title="Form-based File Upload in HTML">RFC 1867: Form-based File Upload in HTML</a> 的 3.3 节中，也有这样的描述：</p>
<blockquote><p>
If multiple files are selected, they should be transferred together using the multipart/mixed format.
</p></blockquote>
<p>说明文件选择框应当能够处理同时选择多个文件的情况。但需要注意的是，这个描述出现在这篇规范文档的 3. Suggested implementation 章节中，仅仅是建议的实现方式，所以除了以前有几个版本的 <a href="http://www.cs.tut.fi/~jkorpela/forms/file.html#opera" title="Opera Support for File Input">Opera</a> 曾经支持过多文件选择，其余浏览器都仅仅实现了单个文件的选择功能。</p>
<p>为了解决这个问题，HTML5 中进行了<a href="http://www.w3.org/TR/html5/common-input-element-attributes.html#attr-input-multiple" title="The multiple attribute">明确</a>，带有 <code>multiple</code> 属性的 input 控件将可以有多个取值，自然也包括文件选择框。但这不是今天说的重点，目前的困境还是摆在我们眼前的。</p>
<p>对于文件上传的进度，就更不好办了：一是文件上传本身就无法使用 AJAX 来进行页内无刷新上传，需要使用隐藏 iframe 来实现。而获取上传进度还得依靠 AJAX 向服务器端发送请求来获取，利用的手段可以是 AJAX 轮询、iframe + htmlfile 甚至是 WebSocket，但本质上做的事情都有违常理：浏览器自己发送了多少内容，却需要一遍遍地请求服务器来进行告知。其次，由于文件上传和进度查询采用的是不同的数据通道，还得使用额外数据来维护两者之间的联系，显得非常繁复。</p>
<p>好了，说了那么一大段就是为了引出目前使用 Flash 来进行文件上传的原因。目前我们项目采用的是 <a href="http://swfupload.org/" title="SWFUpload">SWFUpload</a> 这个 Flash 文件上传工具，支持文件批量选择，并且可以方便地进行上传进度的查询。但是在我们的项目的之前版本中，当页面需要使用 session 中的用户信息时，却无法在 Firefox 下使用它进行正常的文件上传。</p>
<p>在 IE 下，上传工具能够工作，而一到 Firefox 下，SWFUpload 就会提示错误 302。通过从 Fiddler 抓包，发现上传文件的请求被 302 重定向到了登陆页面，所以无法正常上传文件。也就是说，之前用户登陆后的 session 信息在 Flash 发送的上传请求中丢失了。再一看请求的 header，当中没有可以用来标识当前 session ID 的 cookie 值(对于 J2EE 为 JSESSIONID，PHP 为 PHPSESSID 等)。这是什么原因呢？经过一番搜索，找到了 Adobe 官方的<a href="http://cookbooks.adobe.com/post_Why_is_session_info__cookies__not_sent_when_upload-12029.html" title="Why is session info (cookies) not sent when uploading files in Firefox?">一篇文章</a>，说道：</p>
<blockquote><p>
A FileReference.upload() currently does not use the same cookies as your browser session if you are using Firefox (on Windows). In such cases, cookie information has to be inserted manually.
</p></blockquote>
<p>好吧，这个解释虽然很清楚地表明，Flash 在 Firefox 下进行文件上传不会使用浏览器 cookie 中的 session ID，但是貌似并没有回答文章标题当中的“为什么”，即为什么不使用和浏览器中的 session ID 来进行通信。</p>
<p>看来 cookie 是指望不上了，那只能像上面说的那样“手工”传递 session ID 了。于是我们可以在 URL 中拼接一个如 <code>";jsessionid=blahblah"</code> 的字符串(注意要拼在路径之后，参数之前)，像下面这样：</p>
<blockquote><p>http://example.com/app;jsessionid=blahblah?q=blah</p></blockquote>
<p>按<a href="http://download.oracle.com/otndocs/jcp/servlet-2.4-fr-spec-oth-JSpec/" title="Java™ Servlet Specification Version 2.4">Servlet 规范</a> 7.1.3 节中的规定，当客户端不接受 cookie 时，Servlet 容器应当能解析出这类 URL 中的 JSESSIONID，来维持当前 session 的状态。其他服务器端环境中也有类似的机制。于是我们对文件上传功能做了一定的修改：在用户进入上传页面时，就把当前的 session ID 写到页面中，作为 JavaScript 变量拼接到 SWFUpload 上传文件的目标 URL 中，Flash 就可以利用我们人工传入的 JSESSIONID 来维持登陆状态了。当开始上传时，Flash 通过自己的连接通道向服务器发送数据，但是因为请求的路径中包含了 session 信息所以可以和浏览器的 session 状态保持同步，服务器可以通过 URL 中的 session ID 来获取用户登录的信息。</p>
<p>于是，302 错误被解决了，看起来一切都 OK 了。可是，不久我们又发现了新的问题：当用户打开上传页面后较长时间没有操作，服务器端的 session 超时了，这个时候进行文件的上传用的还是超时的那个 session ID，所以会失败也是应该的。但是当这个时候用户重新进行了登录，再次进入上传页面上传，却仍然会失败，只有重新启动浏览器才能再次进行上传。这是为什么呢？从服务器端错误来看，还是用户的登陆信息丢失的问题，也就是说，Flash 在上传文件时，还是没能将用户进行过登陆的这个 session ID 成功传递给服务器端。通过在 Fiddler 中抓包分析，终于找到了原因。下面几张示意图大致描述了这个问题产生的过程：<br />
<img src="http://lync.in/wp-content/uploads/2011/10/first.png" alt="First Flash Request" title="First Flash Request" width="500" height="200" class="aligncenter size-full wp-image-1667" />登陆后 Flash 第一次进行上传时，自身没有 JSESSIONID 这个 Cookie，所以使用手工拼接到请求 URL 中的浏览器 JSESSIONID 即 XXX 进行提交，服务器解析出了 JSESSIONID 并且在内存中找到了其对应的 session 信息，成功进行了身份验证，最后返回上传成功的信息。</p>
<p><img src="http://lync.in/wp-content/uploads/2011/10/second.png" alt="Second Flash Request" title="Second Flash Request" width="500" height="200" class="aligncenter size-medium wp-image-1668" />然而当 XXX 这个 session ID 超时以后，Flash 仍然以刚才重写过的 URL 发送请求(图中[1])，服务器解析出 JSESSIONID 后发现已经没有 XXX 这个 session 了，于是重新创建了一个 ID 为 YYY 的 session(图中[2])，并且在返回的响应中除了登陆失败的信息外还加入了 SET-COOKIE:JSESSIONID=YYY 的请求头(图中[3])。于是这时在 Flash 的 <acronym title="Local Shared Objects">LSO</acronym> (“Flash cookies”) 中却把这个不带用户登录信息的 session ID 保存了下来。</p>
<p><img src="http://lync.in/wp-content/uploads/2011/10/third.png" alt="Third Flash Request" title="Third Flash Request" width="500" height="200" class="aligncenter size-full wp-image-1669" />发现登陆超时后，用户重新登陆，在新的 ID 为 ZZZ 的 session 中留下了有效的登录信息，进入上传页面后，生成了新的拼接 JSESSIONID 的 URL 传递给 Flash 控件，此时进行上传时， Flash 发送的请求既包含了 URL 中的 JSESSIONID，也带了 Cookie 请求头中的 JSESSIONID(图中[1])。按 Servlet 规范，当请求有 Cookie 头中的 JSESSIONID 时，URL 中的就被忽略了，于是服务端判断此请求应该在 YYY 这个 session 中进行处理(图中[2])，于是导致了用户看似已登录，Flash 请求却使用了另一未登录的 session 导致上传失败。</p>
<p>所以，要让 Flash 能够正确使用 URL 中指定的 session，就必须防止 flash cookies 中记录 JSESSIONID 值。如果 Set-cookie 头是 Web 服务器或 Servlet 容器自动加上的，那就需要我们在 Web 应用中将对应的 Cookie 值删除。下面以 Java 代码为例，我们可以通过添加一个名称同样为 JSESSIONID 的立即失效(通过设置 Max-Age=0，参见<a href="http://www.ietf.org/rfc/rfc2109.txt" title="HTTP State Management Mechanism">RFC 2109: HTTP State Management Mechanism</a> 的 4.3.3 节)的 Cookie 来覆盖之前的值：</p>

<div class="wp_syntax_wrapper"><span class="wp_syntax_lang">Java</span><div class="wp_syntax" style="background:#2c2c2c; overflow:auto"><table><tr><td class="line_numbers" style="color:#eee; background:#3c3c3c"><pre>1
2
3
</pre></td><td class="code"><pre class="java" style="color: #FFF; font-family:&quot;Consolas&quot;,monospace,&quot;Courier New&quot;">Cookie cookie <span style="color: #CCC;">=</span> <span style="color: #3D9EDD;">new</span> Cookie<span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">&quot;JSESSIONID&quot;</span>, <span style="color: #7ACC00;">&quot;&quot;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span>
cookie.<span style="color: #FFF;">setMaxAge</span><span style="color: #CCC;">&#40;</span><span style="color: #FFCC00;">0</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span>
response.<span style="color: #FFF;">addCookie</span><span style="color: #CCC;">&#40;</span>cookie<span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span></pre></td></tr></table></div></div>

<p>这样在 session 超时时，上传文件失败返回的请求中会带这样的头：</p>
<blockquote><p>
Set-cookie:JSESSIONID=YYY;<br />
Set-cookie:JSESSIONID="";Max-Age=0
</p></blockquote>
<p>于是在 flash cookie 中 YYY 这个 JSESSIONID 刚刚被设置就被清除了，在重新登陆后 Flash 发送的请求就不会带有 Cookie 头，不会覆盖 URL 中设置的值了。</p>
<p>总结一下，虽然这个过程说起来挺复杂，但解决方法还是比较简单明了的：</p>
<ol>
<li>在上传文件的目标 URL 中显式传递 session ID；</li>
<li>在处理上传的应用代码中，当发现无登录信息的 session ID 时，不发送 Set-cookie 头或在应用代码中将其用一个立即失效的同名 cookie 覆盖。</li>
</ol>
<div class="feed-after" style="margin:15px 0; clear:both;"><a href="http://lync.in">Lync.in</a> 是一个多人协作博客，欢迎您 <a href="http://lync.in/session-trap-on-uploading-files-using-flash-in-firefox/">查看原文</a> 以获得更好的阅读体验。</div>]]></content:encoded>
			<wfw:commentRss>http://lync.in/session-trap-on-uploading-files-using-flash-in-firefox/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>闲话多提交按钮的HTML表单</title>
		<link>http://lync.in/talking-about-html-forms-with-multiple-submit-buttons/</link>
		<comments>http://lync.in/talking-about-html-forms-with-multiple-submit-buttons/#comments</comments>
		<pubDate>Wed, 05 Oct 2011 12:57:16 +0000</pubDate>
		<dc:creator>Justice</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[form]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[submit button]]></category>

		<guid isPermaLink="false">http://lync.in/?p=1625</guid>
		<description><![CDATA[本文作者：Justice在 Web 应用中，最常用的服务器端与客户端的数据交互是通过提交 HTML 表单实现的。以 HTML 4.01 为例，规定当表单提交时，会将 &#60;form&#62; 元素中的 Successful controls 的当前取值提交到服务器。所谓成功控件需要有以下几个条件(但不仅限于此)： 必须有控件名字，即必须包含 name 属性 如果表单中有多个提交按钮，只有被点击的那个按钮可以是成功的 不能是禁用状态，即不含 disabled 属性 不是 Reset button 现在我们考虑一下这样的场景，在某个表单中我们需要用到多个提交按钮(Submit button)，就如同 WordPress 后台写文章那样的表单，我们在编辑完文章内容后，可以选择保存草稿或是直接发布。下面图中所示是一个简化的发布页面，可以填写文章标题、内容之后选某一种提交形式： 对应的 HTML 片段可以是这样： HTML1 2 3 4 5 6 &#60;form action=&#34;http://localhost/blog/new-post&#34; method=&#34;POST&#34;&#62; &#60;input type=&#34;text&#34; name=&#34;title&#34; id=&#34;title&#34; /&#62;&#60;br /&#62; &#60;textarea name=&#34;content&#34; id=&#34;content&#34; cols=&#34;30&#34; rows=&#34;10&#34;&#62;&#60;/textarea&#62;&#60;br /&#62; &#60;input type=&#34;submit&#34; id=&#34;save-post&#34; value=&#34;Save [...]]]></description>
			<content:encoded><![CDATA[<div class="feed-before" style="margin:15px 0; clear:both;">本文作者：<a href="http://lync.in/author/1/" title="Posts by Justice">Justice</a></div><p>在 Web 应用中，最常用的服务器端与客户端的数据交互是通过提交 <abbr title="Hypertext Markup Language">HTML</abbr> 表单实现的。以 HTML 4.01 为例，规定当表单提交时，会将 <code>&lt;form&gt;</code> 元素中的 <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2" title="Successful controls">Successful controls</a> 的当前取值提交到服务器。所谓成功控件需要有以下几个条件(但不仅限于此)：</p>
<ol>
<li>必须有控件名字，即必须包含 name 属性</li>
<li>如果表单中有多个提交按钮，只有被点击的那个按钮可以是成功的</li>
<li>不能是禁用状态，即不含 disabled 属性</li>
<li>不是 Reset button</li>
</ol>
<p>现在我们考虑一下这样的场景，在某个表单中我们需要用到多个提交按钮(Submit button)，就如同 WordPress 后台写文章那样的表单，我们在编辑完文章内容后，可以选择保存草稿或是直接发布。下面图中所示是一个简化的发布页面，可以填写文章标题、内容之后选某一种提交形式：<span id="more-1625"></span><br />
<img src="http://lync.in/wp-content/uploads/2011/10/scenario.png" alt="Scenario" title="Scenario" width="306" height="303" class="aligncenter size-full wp-image-1647" /></p>
<p>对应的 HTML 片段可以是这样：</p>

<div class="wp_syntax_wrapper"><span class="wp_syntax_lang">HTML</span><div class="wp_syntax" style="background:#2c2c2c; overflow:auto"><table><tr><td class="line_numbers" style="color:#eee; background:#3c3c3c"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="html4strict" style="color: #FFF; font-family:&quot;Consolas&quot;,monospace,&quot;Courier New&quot;"><span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">form</span> <span style="color: #996699;">action</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;http://localhost/blog/new-post&quot;</span> <span style="color: #996699;">method</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;POST&quot;</span>&gt;</span>
    <span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">input</span> <span style="color: #996699;">type</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;text&quot;</span> <span style="color: #996699;">name</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;title&quot;</span> <span style="color: #996699;">id</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;title&quot;</span> <span style="color: #CCC;">/</span>&gt;&lt;<span style="color: #F2B646;">br</span> <span style="color: #CCC;">/</span>&gt;</span>
    <span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">textarea</span> <span style="color: #996699;">name</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;content&quot;</span> <span style="color: #996699;">id</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;content&quot;</span> <span style="color: #996699;">cols</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;30&quot;</span> <span style="color: #996699;">rows</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;10&quot;</span>&gt;&lt;<span style="color: #CCC;">/</span><span style="color: #F2B646;">textarea</span>&gt;&lt;<span style="color: #F2B646;">br</span> <span style="color: #CCC;">/</span>&gt;</span>
    <span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">input</span> <span style="color: #996699;">type</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;submit&quot;</span> <span style="color: #996699;">id</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;save-post&quot;</span> <span style="color: #996699;">value</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;Save Draft&quot;</span> <span style="color: #CCC;">/</span>&gt;</span>
    <span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">input</span> <span style="color: #996699;">type</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;submit&quot;</span> <span style="color: #996699;">id</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;publish&quot;</span> <span style="color: #996699;">value</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;Publish&quot;</span> <span style="color: #CCC;">/</span>&gt;</span>
<span style="color: #CCFF33;">&lt;<span style="color: #CCC;">/</span><span style="color: #F2B646;">form</span>&gt;</span></pre></td></tr></table></div></div>

<p>为了在提交时能区分是保存还是发布，需要通过 JavaScript 来为不同的按钮绑定不同的操作：</p>

<div class="wp_syntax_wrapper"><span class="wp_syntax_lang">JavaScript</span><div class="wp_syntax" style="background:#2c2c2c; overflow:auto"><table><tr><td class="line_numbers" style="color:#eee; background:#3c3c3c"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="javascript" style="color: #FFF; font-family:&quot;Consolas&quot;,monospace,&quot;Courier New&quot;"><span style="color: #999;">//using jQuery</span>
$<span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">'#save-post'</span><span style="color: #CCC;">&#41;</span>.<span style="color: #FFF;">click</span><span style="color: #CCC;">&#40;</span><span style="color: #F2B646;">function</span><span style="color: #CCC;">&#40;</span><span style="color: #CCC;">&#41;</span> <span style="color: #CCC;">&#123;</span>
    <span style="color: #999;">//按保存草稿方式提交</span>
<span style="color: #CCC;">&#125;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span>
&nbsp;
$<span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">'#publish'</span><span style="color: #CCC;">&#41;</span>.<span style="color: #FFF;">click</span><span style="color: #CCC;">&#40;</span><span style="color: #F2B646;">function</span><span style="color: #CCC;">&#40;</span><span style="color: #CCC;">&#41;</span> <span style="color: #CCC;">&#123;</span>
    <span style="color: #999;">//按发布方式提交</span>
<span style="color: #CCC;">&#125;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span></pre></td></tr></table></div></div>

<p>那么提交的数据中如何来区分是保存草稿还是发布？通常我们会在 <code>&lt;form&gt;</code> 中添加一个隐藏的控件来保存这个状态，像这样：</p>

<div class="wp_syntax_wrapper"><span class="wp_syntax_lang">HTML</span><div class="wp_syntax" style="background:#2c2c2c; overflow:auto"><div class="code"><pre class="html4strict" style="color: #FFF; font-family:&quot;Consolas&quot;,monospace,&quot;Courier New&quot;"><span style="color: #808080;">&lt;!-- value 可以是 publish 或 draft，默认为 publish --&gt;</span>
<span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">input</span> <span style="color: #996699;">type</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;hidden&quot;</span> <span style="color: #996699;">name</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;post-status&quot;</span> <span style="color: #996699;">id</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;post-status&quot;</span> <span style="color: #996699;">value</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;publish&quot;</span> <span style="color: #CCC;">/</span>&gt;</span></pre></div></div></div>

<p>这样在 JS 中就只需要为按保存方式提交作相应处理：</p>

<div class="wp_syntax_wrapper"><span class="wp_syntax_lang">JavaScript</span><div class="wp_syntax" style="background:#2c2c2c; overflow:auto"><table><tr><td class="line_numbers" style="color:#eee; background:#3c3c3c"><pre>1
2
3
4
</pre></td><td class="code"><pre class="javascript" style="color: #FFF; font-family:&quot;Consolas&quot;,monospace,&quot;Courier New&quot;"><span style="color: #999;">//using jQuery</span>
$<span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">'#save-post'</span><span style="color: #CCC;">&#41;</span>.<span style="color: #FFF;">click</span><span style="color: #CCC;">&#40;</span><span style="color: #F2B646;">function</span><span style="color: #CCC;">&#40;</span><span style="color: #CCC;">&#41;</span> <span style="color: #CCC;">&#123;</span>
    $<span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">'#post-status'</span><span style="color: #CCC;">&#41;</span>.<span style="color: #FFF;">val</span><span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">'draft'</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span>
<span style="color: #CCC;">&#125;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span></pre></td></tr></table></div></div>

<p>这样如果按 Save Draft，那么 POST 出去的数据就应该是：</p>
<blockquote><p>
title=blah&#038;content=blahblah&#038;post-status=draft
</p></blockquote>
<p>OK，这样的确已经可以正常工作了，但是总觉得让人有些不爽：这么简单的工作为什么还要通过脚本来实现？有没有想过为什么要这样实现呢？事实上，因为在文章开始的地方提到的 HTML 规范中定义了一个表单中有多个提交按钮的情况下，只有一个可以是成功控件从而提交自己的数据值，也就是说，我们给提交按钮加上相同的 name 属性，只有一个按钮会提交相应的数据。那何必还需要隐藏控件？应该只需要这样：</p>

<div class="wp_syntax_wrapper"><span class="wp_syntax_lang">HTML</span><div class="wp_syntax" style="background:#2c2c2c; overflow:auto"><table><tr><td class="line_numbers" style="color:#eee; background:#3c3c3c"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="html4strict" style="color: #FFF; font-family:&quot;Consolas&quot;,monospace,&quot;Courier New&quot;"><span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">form</span> <span style="color: #996699;">action</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;http://localhost/blog/post&quot;</span> <span style="color: #996699;">method</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;POST&quot;</span>&gt;</span>
    <span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">input</span> <span style="color: #996699;">type</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;text&quot;</span> <span style="color: #996699;">name</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;title&quot;</span> <span style="color: #996699;">id</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;title&quot;</span> <span style="color: #CCC;">/</span>&gt;&lt;<span style="color: #F2B646;">br</span> <span style="color: #CCC;">/</span>&gt;</span>
    <span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">textarea</span> <span style="color: #996699;">name</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;content&quot;</span> <span style="color: #996699;">id</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;content&quot;</span> <span style="color: #996699;">cols</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;30&quot;</span> <span style="color: #996699;">rows</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;10&quot;</span>&gt;&lt;<span style="color: #CCC;">/</span><span style="color: #F2B646;">textarea</span>&gt;&lt;<span style="color: #F2B646;">br</span> <span style="color: #CCC;">/</span>&gt;</span>
    <span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">input</span> <span style="color: #996699;">type</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;submit&quot;</span> <span style="color: #996699;">name</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;post-status&quot;</span> <span style="color: #996699;">id</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;save-post&quot;</span> <span style="color: #996699;">value</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;Save Draft&quot;</span> <span style="color: #CCC;">/</span>&gt;</span>
    <span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">input</span> <span style="color: #996699;">type</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;submit&quot;</span> <span style="color: #996699;">name</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;post-status&quot;</span> <span style="color: #996699;">id</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;publish&quot;</span> <span style="color: #996699;">value</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;Publish&quot;</span> <span style="color: #CCC;">/</span>&gt;</span>
<span style="color: #CCFF33;">&lt;<span style="color: #CCC;">/</span><span style="color: #F2B646;">form</span>&gt;</span></pre></td></tr></table></div></div>

<p>像这样把两个提交按钮的 name 属性都设为 post-status，就可以像服务器发送唯一的 post-status 值了，但有一个问题：post-status 的值只能和按钮上的文字一样了，给 Web 应用的 <acronym title="internationalization">i18n</acronym> 带来了很大的不便。其实 HTML 规范也考虑到了这个问题，这就是为什么我们会有 <code>&lt;button&gt;</code> 元素。HTML 中的按钮共有三种类型：</p>
<ul>
<li>submit buttons</li>
<li>reset buttons</li>
<li>push buttons</li>
</ul>
<p>前两个不用说，第三种按钮没有默认的数据交互行为，需要依赖脚本才能起到作用。一般情况下我们使用的都是 type 属性分别为 submit / reset / button 的 <code>&lt;input&gt;</code> 元素，而 <code>&lt;button&gt;</code> 元素就是用来为这三类 <code>&lt;input&gt;</code> 提供更多渲染可能性的，<code>&lt;button&gt;</code> 中可以插入其他的 HTML 元素，比如可以加入 <code>&lt;img&gt;</code> 来给按钮增加一个图标，所以显然也可以使用任何你想使用的文本作为按钮上显示的文字。这个文字和按钮的取值 value 应当是分离的：</p>

<div class="wp_syntax_wrapper"><span class="wp_syntax_lang">HTML</span><div class="wp_syntax" style="background:#2c2c2c; overflow:auto"><div class="code"><pre class="html4strict" style="color: #FFF; font-family:&quot;Consolas&quot;,monospace,&quot;Courier New&quot;"><span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">button</span> <span style="color: #996699;">type</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;submit&quot;</span> <span style="color: #996699;">name</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;post-type&quot;</span> <span style="color: #996699;">id</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;save&quot;</span> <span style="color: #996699;">value</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;draft&quot;</span>&gt;</span>Save Draft<span style="color: #CCFF33;">&lt;<span style="color: #CCC;">/</span><span style="color: #F2B646;">button</span>&gt;</span>
<span style="color: #CCFF33;">&lt;<span style="color: #F2B646;">button</span> <span style="color: #996699;">type</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;submit&quot;</span> <span style="color: #996699;">name</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;post-type&quot;</span> <span style="color: #996699;">id</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;publish&quot;</span> <span style="color: #996699;">value</span><span style="color: #CCC;">=</span><span style="color: #7ACC00;">&quot;publish&quot;</span>&gt;</span>Publish<span style="color: #CCFF33;">&lt;<span style="color: #CCC;">/</span><span style="color: #F2B646;">button</span>&gt;</span></pre></div></div></div>

<p>看上去很不错，不是么？可是很不幸，我们又遇到麻烦了：在 IE6/7 下，这样的方法完全不能工作。在 IE6 下，用这样的方式按 Save Draft 按钮，POST 出的参数值为：</p>
<blockquote><p>
title=blah&#038;content=blahblah&#038;post-status=Save+Draft&#038;post-status=Publish
</p></blockquote>
<p>在 IE7 下，则是：</p>
<blockquote><p>
title=blah&#038;content=blahblah&#038;post-status=Save+Draft
</p></blockquote>
<p>可以看到，IE6/7 错误地把 <code>&lt;button&gt;</code> 的 innerHTML 当作了 value 发送出去了，而 IE6 甚至把没有点击的按钮也看作成功。WTF！本来很优雅的代码在现实中却是无法完美工作的。有人写了个针对 IE6/7 的 hack 来解决这个问题，即在 <code>&lt;button&gt;</code> 的点击事件中，disable 同一表单内的其他 <code>&lt;button&gt;</code> 并且用其 value 替换 innerHTML，但这样在网速比较慢的情况下(就是大多数情况下)会出现下面这样的画面：<br />
<img src="http://lync.in/wp-content/uploads/2011/10/ie.png" alt="Awkward hack" title="Awkward hack" width="306" height="283" class="aligncenter size-full wp-image-1639" /><br />
这显然也是不能让人接受的。就是没有一个完美的方案，就是这样，所以又回到我们开头的地方了。用隐藏控件来 hold 住提交方式参数，用脚本来给它赋值，又变成了看起来最通用最简单的方案了。</p>
<p>另外有一种观点认为，一个表单就不该有多个提交按钮，提交按钮不该携带任何参数值，完全可以换成单选框、下拉菜单等等等等方式来实现。但实际上多个提交按钮在很多时候可以减少用户的交互操作，并且更为直观，绝对是有其存在价值的。拿 WordPress 的文章发布方式来说，现在就是多提交按钮的：<br />
<img src="http://lync.in/wp-content/uploads/2011/10/original.png" alt="WP Submit" title="WP Submit" width="280" height="215" class="aligncenter size-full wp-image-1642" /><br />
如果改成下面这样，就感觉十分别扭了：<br />
<img src="http://lync.in/wp-content/uploads/2011/10/mod.png" alt="WP Submit Mod" title="WP Submit Mod" width="280" height="205" class="aligncenter size-full wp-image-1645" /></p>
<p>总结一下：如果你可以抛弃 IE6/7，那么恭喜你，用最简单优雅的方式去实现吧！如果不能，那么，还是老老实实地用隐藏控件吧，慢慢等着 IE6/7 入土为安吧。</p>
<p>很久没写东西了，有点虎头蛇尾，其实没多少内容，发发牢骚而已。</p>
<p><ins datetime="2011-10-09T02:22:26+00:00">Update: 欲了解更多关于按钮数据交互行为的浏览器差异可以阅读<a href="http://w3help.org/zh-cn/causes/CM2001">这篇文章</a>。</ins></p>
<div class="feed-after" style="margin:15px 0; clear:both;"><a href="http://lync.in">Lync.in</a> 是一个多人协作博客，欢迎您 <a href="http://lync.in/talking-about-html-forms-with-multiple-submit-buttons/">查看原文</a> 以获得更好的阅读体验。</div>]]></content:encoded>
			<wfw:commentRss>http://lync.in/talking-about-html-forms-with-multiple-submit-buttons/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>主题更新: SimpleDark 1.3</title>
		<link>http://lync.in/simpledark-1-3/</link>
		<comments>http://lync.in/simpledark-1-3/#comments</comments>
		<pubDate>Fri, 15 Jul 2011 18:27:38 +0000</pubDate>
		<dc:creator>Justice</dc:creator>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[comment edit]]></category>
		<category><![CDATA[Post Formats]]></category>
		<category><![CDATA[SimpleDark]]></category>
		<category><![CDATA[smileys]]></category>
		<category><![CDATA[theme]]></category>
		<category><![CDATA[update]]></category>

		<guid isPermaLink="false">http://lync.in/?p=1583</guid>
		<description><![CDATA[本文作者：Justice本想在 6 月份发布的，但是修修改改又拖了很久。 主要修改的部分如下： 表情选择菜单 增加了表情选择菜单，方便在评论中添加表情。 评论编辑 增加了评论编辑功能，使未注册访客可以在前台快速编辑自己在之前 30 分钟内提交的评论。目前很多博客使用的是 Willin Kan 的 Ajax comments 来提供有关评论的 AJAX 功能，也包括快速编辑功能。但是这段代码实际上是有漏洞的，只要懂点 Web 技术的人可以很轻松地修改使用这段代码的站点上其他任何人的评论。而且经过沟通看样子 Willin 暂时不准备对这个问题进行处理，虽然我觉得这个问题还是需要解决的。我在这里采取的是用评论者发表评论时使用的 Email 地址和 IP 地址进行验证。除此之外，在关闭 AJAX 时，评论快速编辑功能依然有效。 Post Format 功能支持 自 3.1 后，WordPress 增加了 Post Format 功能，以提供类似 Tumblr 的“轻博客”效果。如果主题支持 Post Format 功能，用户可以通过在发表文章时选择相应的文章格式，从而使不同类型文章在页面中的显示效果不同。这次主题更新中，加入了对目前所有已定义的文章形式的支持，包括 Standard, Aside, Gallery, Link, Image, Quote, Status, Video, Audio, Chat 共 10 [...]]]></description>
			<content:encoded><![CDATA[<div class="feed-before" style="margin:15px 0; clear:both;">本文作者：<a href="http://lync.in/author/1/" title="Posts by Justice">Justice</a></div><p>本想在 6 月份发布的，但是修修改改又拖了很久。</p>
<p>主要修改的部分如下：</p>
<ul>
<li>
<h5>表情选择菜单</h5>
</li>
<p>增加了表情选择菜单，方便在评论中添加表情。<br />
<a href="http://lync.in/wp-content/uploads/2011/07/smileys.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2011/07/smileys-450x148.png" alt="Smileys" title="Smileys" width="450" height="148" class="alignnone size-medium wp-image-1587" /></a></p>
<li>
<h5>评论编辑</h5>
<p>增加了评论编辑功能，使未注册访客可以在前台快速编辑自己在之前 30 分钟内提交的评论。目前很多博客使用的是 Willin Kan 的 Ajax comments 来提供有关评论的 AJAX 功能，也包括快速编辑功能。但是这段代码实际上是有漏洞的，只要懂点 Web 技术的人可以很轻松地修改使用这段代码的站点上其他任何人的评论。而且经过沟通看样子 Willin 暂时不准备对这个问题进行处理，虽然我觉得这个问题还是需要解决的。我在这里采取的是用评论者发表评论时使用的 Email 地址和 IP 地址进行验证。除此之外，在关闭 AJAX 时，评论快速编辑功能依然有效。<br />
<span id="more-1583"></span><a href="http://lync.in/wp-content/uploads/2011/07/comment-edit.png" class="highslide-image" onclick="return hs.expand(this);"><img src="http://lync.in/wp-content/uploads/2011/07/comment-edit-450x297.png" alt="Comment Edit" title="Comment Edit" width="450" height="297" class="alignnone size-medium wp-image-1585" /></a>
</li>
<li>
<h5>Post Format 功能支持</h5>
<p>自 3.1 后，WordPress 增加了 Post Format 功能，以提供类似 Tumblr 的“轻博客”效果。如果主题支持 Post Format 功能，用户可以通过在发表文章时选择相应的文章格式，从而使不同类型文章在页面中的显示效果不同。这次主题更新中，加入了对目前所有已定义的文章形式的支持，包括 Standard, Aside, Gallery, Link, Image, Quote, Status, Video, Audio, Chat 共 10 种。也许大家还不太明白这个功能有何具体作用，下面就来对各个类型文章的处理规则来作下简单介绍。</p>
<p>Post Format 这个功能主要是用来让用户可以通过简单的操作，无需进行排版，就可以很轻松地创作一篇带有预定义格式的文章，其格式可以按文章的类型从预定义好的这 10 中形式中进行选择。下面是 SimpleDark 中的处理规则：</p>
<h6>Standard</h6>
<p>标准形式，与之前的普通文章没有区别。</p>
<h6>Aside</h6>
<p>短篇日志，不显示标题，内容与标准形式相同。</p>
<h6>Gallery</h6>
<p>相册，通常内容中包含较多图片。在列表页面显示最多 3 张图片的缩略图，在文章页面显示所有图片的缩略图。</p>
<h6>Link</h6>
<p>链接，通常可以用来分享一个站外网页。不显示文章标题。如果文章内容是一个合法的 URL 那么链接就指向该 URL，否则，解析出文章内容中第一个链接指向的 URL 来使用。链接的锚文字使用文章的标题，如果没有标题则使用 URL 本身作为锚文字。除了链接本身外，列表页用文章摘要作为链接的描述信息显示，文章页按标准形式现实文章内容。</p>
<h6>Image</h6>
<p>图片。如果文章内容是一个合法的 URL 那么将这个 URL 作为图片的地址，否则，解析出文章内容中第一个图片来使用。文章标题作为图片标题显示在图片上方。图片带一个指向文章页的链接，可供访客评论。</p>
<h6>Quote</h6>
<p>引语，用来摘录他人的言论。不显示文章标题。最终的引语内容会自动加上双引号样式。自动解析文章内容中的第一个 <code>&lt;blockquote&gt;</code> 或 <code>&lt;q&gt;</code> 标签中的内容作为引用内容，并按相应格式进行显示。如果没有解析到，则将文章内容全部作为引用内容。自动解析文章内容中的第一个 <code>&lt;cite&gt;</code> 标签中的内容作为引用来源显示在引语内容下方，如果没有则将文章标题作为引用来源。</p>
<h6>Status</h6>
<p>状态，类似一条 tweet。不显示文章标题，使用文章内容作为状态内容，以类似 tweet 方式输出，显示作者的 Gravatar，不给出指向文章的链接。</p>
<h6>Video / Audio</h6>
<p>视频/音频，自动解析文章内容中的第一个 <code>&lt;video&gt;</code> / <code>&lt;audio&gt;</code> / <code>&lt;object&gt;</code> / <code>&lt;embed&gt;</code> / <code>&lt;iframe&gt;</code> 等常用来嵌入多媒体内容的标签来作为文章内容。文章标题作为视频/音频标题显示在视频/音频上方。</p>
<h6>Chat</h6>
<p>聊天，用来分享一段聊天内容。文章内容按换行隔开，显示在一个 <code>&lt;table&gt;</code> 中的不同行内。</p>
<p>下面是不同文章形式在列表页面中的表现形式：<br />

<a href='http://lync.in/simpledark-1-3/aside/' title='aside'><img width="150" height="126" src="http://lync.in/wp-content/uploads/2011/07/aside-150x126.png" class="attachment-thumbnail" alt="aside" title="aside" /></a>
<a href='http://lync.in/simpledark-1-3/gallery/' title='gallery'><img width="150" height="150" src="http://lync.in/wp-content/uploads/2011/07/gallery-150x150.png" class="attachment-thumbnail" alt="gallery" title="gallery" /></a>
<a href='http://lync.in/simpledark-1-3/link/' title='link'><img width="150" height="108" src="http://lync.in/wp-content/uploads/2011/07/link-150x108.png" class="attachment-thumbnail" alt="link" title="link" /></a>
<a href='http://lync.in/simpledark-1-3/image/' title='image'><img width="150" height="150" src="http://lync.in/wp-content/uploads/2011/07/image-150x150.png" class="attachment-thumbnail" alt="image" title="image" /></a>
<a href='http://lync.in/simpledark-1-3/quote/' title='quote'><img width="150" height="150" src="http://lync.in/wp-content/uploads/2011/07/quote-150x150.png" class="attachment-thumbnail" alt="quote" title="quote" /></a>
<a href='http://lync.in/simpledark-1-3/status/' title='status'><img width="150" height="92" src="http://lync.in/wp-content/uploads/2011/07/status-150x92.png" class="attachment-thumbnail" alt="status" title="status" /></a>
<a href='http://lync.in/simpledark-1-3/video/' title='video'><img width="150" height="150" src="http://lync.in/wp-content/uploads/2011/07/video-150x150.png" class="attachment-thumbnail" alt="video" title="video" /></a>
<a href='http://lync.in/simpledark-1-3/audio/' title='audio'><img width="150" height="144" src="http://lync.in/wp-content/uploads/2011/07/audio-150x144.png" class="attachment-thumbnail" alt="audio" title="audio" /></a>
<a href='http://lync.in/simpledark-1-3/chat/' title='chat'><img width="150" height="150" src="http://lync.in/wp-content/uploads/2011/07/chat-150x150.png" class="attachment-thumbnail" alt="chat" title="chat" /></a>

</li>
</ul>
<p>可以查看<a href="http://lync.in/demo/wp/">在线演示</a>同时也可以帮助我进行测试。</p>
<h3>下载</h3>
<p><a href="http://code.google.com/p/simpledark/"><strong>从 Google Code 下载最新版 SimpleDark</strong></a></p>
<blockquote><p><ins datetime="2011-08-12T10:46:25+08:00">2011-08-12: 本文评论已关闭，有相关问题请查看 <a title="SimpleDark" href="http://lync.in/projects/simpledark/">主题介绍</a> 并留言。</ins></p></blockquote>
<div class="feed-after" style="margin:15px 0; clear:both;"><a href="http://lync.in">Lync.in</a> 是一个多人协作博客，欢迎您 <a href="http://lync.in/simpledark-1-3/">查看原文</a> 以获得更好的阅读体验。</div>]]></content:encoded>
			<wfw:commentRss>http://lync.in/simpledark-1-3/feed/</wfw:commentRss>
		<slash:comments>60</slash:comments>
		</item>
		<item>
		<title>解决Office文档无法打开链接的问题</title>
		<link>http://lync.in/solve-office-cannot-open-link-problem/</link>
		<comments>http://lync.in/solve-office-cannot-open-link-problem/#comments</comments>
		<pubDate>Mon, 23 May 2011 10:24:25 +0000</pubDate>
		<dc:creator>Sinnyn</dc:creator>
				<category><![CDATA[Tips]]></category>
		<category><![CDATA[hyperlink]]></category>
		<category><![CDATA[Office 2010]]></category>

		<guid isPermaLink="false">http://lync.in/?p=1574</guid>
		<description><![CDATA[本文作者：Sinnyn近日碰到个状况，在Office中，不管是Word还是Outlook里面，想要通过Ctrl+左键打开一个链接的时候都会被Office拒绝，弹出一个对话框显示。 由于本机的限制，该操作已被取消。请与系统管理员联系。 解决方案其实在注册表里面。方案如下。 打开注册表，找到HKEY_CURRENT_USER\Software\Classes这个项。 然后在其下的.htm，.html，.mhtm，.mhtml，.shtm，.shtml里面，把(默认)这个键的值从ChromeHtmlDocument和FirefoxHtml改成htmlfile就好了。 Lync.in 是一个多人协作博客，欢迎您 查看原文 以获得更好的阅读体验。]]></description>
			<content:encoded><![CDATA[<div class="feed-before" style="margin:15px 0; clear:both;">本文作者：<a href="http://lync.in/author/3/" title="Posts by Sinnyn">Sinnyn</a></div><p>近日碰到个状况，在Office中，不管是Word还是Outlook里面，想要通过Ctrl+左键打开一个链接的时候都会被Office拒绝，弹出一个对话框显示。</p>
<blockquote><p>由于本机的限制，该操作已被取消。请与系统管理员联系。</p></blockquote>
<p>解决方案其实在注册表里面。方案如下。</p>
<p>打开注册表，找到HKEY_CURRENT_USER\Software\Classes这个项。</p>
<p>然后在其下的.htm，.html，.mhtm，.mhtml，.shtm，.shtml里面，把(默认)这个键的值从ChromeHtmlDocument和FirefoxHtml改成htmlfile就好了。</p>
<div class="feed-after" style="margin:15px 0; clear:both;"><a href="http://lync.in">Lync.in</a> 是一个多人协作博客，欢迎您 <a href="http://lync.in/solve-office-cannot-open-link-problem/">查看原文</a> 以获得更好的阅读体验。</div>]]></content:encoded>
			<wfw:commentRss>http://lync.in/solve-office-cannot-open-link-problem/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>在WordPress中获取指定评论所在分页页码</title>
		<link>http://lync.in/get-comment-page-number-in-wordpress/</link>
		<comments>http://lync.in/get-comment-page-number-in-wordpress/#comments</comments>
		<pubDate>Fri, 06 May 2011 19:18:40 +0000</pubDate>
		<dc:creator>Justice</dc:creator>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[comment]]></category>
		<category><![CDATA[pagination]]></category>

		<guid isPermaLink="false">http://lync.in/?p=1508</guid>
		<description><![CDATA[本文作者：JusticeWordPress 2.7 以后便自带了评论分页功能，那些有很多评论的热门日志页面在开启了评论分页后加载速度就会大大加快，同时也节省了站点的不少流量。但是评论分页以后，WordPress 没有提供一个方法让我们来查询某一条评论位于它所在那篇文章所有评论分页的第几页，这样就带来了一些不便。WordPress 提供了一个 get_page_of_comment 函数，用来获取评论所在的分页数。但这个函数有一个 bug：当嵌套评论选项被关闭，但是数据库中却存在之前未关闭选项时留下的回复的情况下，分页数会计算错误。比如说，本站使用了 mg12 的插件 WP-RecentComments 来显示站内的最新评论，它会在每条评论的作者名字上加上一个指向该评论位置的链接，比如： http://lync.in/projects/simpledark/#comment-1778 当评论没有分页或者这条评论位于最新的一页评论时，这样没有问题，因为在当前的页面就能找到这条评论。但是如果后面有了较多的新评论，使得这条评论在其他分页的时候，这个页内位置就失效了，上面的这个链接指向的页面也就无法。某篇文章的某个具体评论分页的地址一般还要加上 comment-page-n 或者加入参数 cpage=n，比如： http://lync.in/projects/simpledark/comment-page-9/ 所以如果可以得到评论所在的分页页码，就能通过 URL 直接找到那条评论的位置了，比如： http://lync.in/projects/simpledark/comment-page-9/#comment-1680 那剩下的问题就是给定一个评论 ID 如何找到它所在的评论分页的页码了。在 WordPress 中，评论在数据库中存在 wp_comments 表中，和今天这个问题相关的几个字段如下所示： wp_comments comment_ID comment_post_ID ... comment_approved ... comment_type comment_parent ... 1 1 ... 1 ... 0 ... 2 1 ... 1 ... 1 ... 3 2 ... [...]]]></description>
			<content:encoded><![CDATA[<div class="feed-before" style="margin:15px 0; clear:both;">本文作者：<a href="http://lync.in/author/1/" title="Posts by Justice">Justice</a></div><p>WordPress 2.7 以后便自带了评论分页功能，那些有很多评论的热门日志页面在开启了评论分页后加载速度就会大大加快，同时也节省了站点的不少流量。<del datetime="2011-06-27T09:09:10+00:00">但是评论分页以后，WordPress 没有提供一个方法让我们来查询某一条评论位于它所在那篇文章所有评论分页的第几页，这样就带来了一些不便。</del><ins datetime="2011-06-27T09:09:10+00:00">WordPress 提供了一个 <code>get_page_of_comment</code> 函数，用来获取评论所在的分页数。但这个函数有一个 bug：当嵌套评论选项被关闭，但是数据库中却存在之前未关闭选项时留下的回复的情况下，分页数会计算错误。</ins>比如说，本站使用了 <a href="http://www.neoease.com/">mg12</a> 的插件 <a href="http://wordpress.org/extend/plugins/wp-recentcomments/">WP-RecentComments</a> 来显示站内的最新评论，它会在每条评论的作者名字上加上一个指向该评论位置的链接，比如：</p>
<blockquote><p>http://lync.in/projects/simpledark/#comment-1778</p></blockquote>
<p>当评论没有分页或者这条评论位于最新的一页评论时，这样没有问题，因为在当前的页面就能找到这条评论。但是如果后面有了较多的新评论，使得这条评论在其他分页的时候，这个页内位置就失效了，上面的这个链接指向的页面也就无法。某篇文章的某个具体评论分页的地址一般还要加上 <code>comment-page-<var>n</var></code> 或者加入参数 <code>cpage=<var>n</var></code>，比如：</p>
<blockquote><p>http://lync.in/projects/simpledark/comment-page-9/</p></blockquote>
<p><span id="more-1508"></span>所以如果可以得到评论所在的分页页码，就能通过 URL 直接找到那条评论的位置了，比如：</p>
<blockquote><p>http://lync.in/projects/simpledark/comment-page-9/#comment-1680</p></blockquote>
<p>那剩下的问题就是给定一个评论 ID 如何找到它所在的评论分页的页码了。在 WordPress 中，评论在数据库中存在 wp_comments 表中，和今天这个问题相关的几个字段如下所示：</p>
<table>
<caption style="text-align:left">wp_comments</caption>
<thead>
<tr style="font-size:9px;">
<th>comment_ID</th>
<th>comment_post_ID</th>
<th>...</th>
<th>comment_approved</th>
<th>...</th>
<th>comment_type</th>
<th>comment_parent</th>
<th>...</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>1</td>
<td>...</td>
<td>1</td>
<td>...</td>
<td></td>
<td>0</td>
<td>...</td>
</tr>
<tr>
<td>2</td>
<td>1</td>
<td>...</td>
<td>1</td>
<td>...</td>
<td></td>
<td>1</td>
<td>...</td>
</tr>
<tr>
<td>3</td>
<td>2</td>
<td>...</td>
<td>1</td>
<td>...</td>
<td>pingback</td>
<td>0</td>
<td>...</td>
</tr>
<tr>
<td colspan="8" style="text-align:center">...</td>
</tr>
</tbody>
</table>
<ul>
<li>comment_ID - 评论 ID</li>
<li>comment_post_ID - 评论所在文章 ID</li>
<li>comment_approved - 评论是否已通过审核，1 为通过，0 为未通过</li>
<li>comment_type - 评论类型，可能取值为 trackback、pingback 或留空，若为空则是普通评论</li>
<li>comment_parent - 父评论的 ID，如果为 0 则没有父评论</li>
</ul>
<p>首先，我这里考虑范围内的仅有已通过审核的普通评论 (对于没有做评论/引用通告分离的朋友来说可以忽略 comment_type 字段)。计算评论所在分页页码可以分为是否开启嵌套评论两种情况。如果没有开启嵌套评论，那只需要知道在同一篇文章下指定评论之前发表的评论数，除以每页显示的评论数就可以获得页码；而开启嵌套评论时，评论的分页是按一级评论 (没有子评论的评论) 的数量来计算，所以只要计算指定评论所在的一级评论之前的一级评论数，除以每页现实的评论数就可获得页码。</p>
<p>WordPress 的数据库操作是基于 <a href="http://justinvincent.com/ezsql">ezSQL</a> 库的，API 用起来还是很方便的。下面给出具体代码：</p>

<div class="wp_syntax_wrapper"><span class="wp_syntax_lang">PHP</span><div class="wp_syntax" style="background:#2c2c2c; overflow:auto"><table><tr><td class="line_numbers" style="color:#eee; background:#3c3c3c"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre class="php" style="color: #FFF; font-family:&quot;Consolas&quot;,monospace,&quot;Courier New&quot;"><span style="color: #F2B646;">function</span> get_comment_page_number<span style="color: #CCC;">&#40;</span><span style="color: #7AB9BE;">$comment_id</span><span style="color: #CCC;">&#41;</span> <span style="color: #CCC;">&#123;</span>
    <span style="color: #F2B646;">global</span> <span style="color: #7AB9BE;">$wpdb</span><span style="color: #CCC;">;</span>
    <span style="color: #7AB9BE;">$comment</span> <span style="color: #CCC;">=</span> <span style="color: #7AB9BE;">$wpdb</span><span style="color: #CCC;">-&gt;</span><span style="color: #FFF;">get_row</span><span style="color: #CCC;">&#40;</span><span style="color: #7AB9BE;">$wpdb</span><span style="color: #CCC;">-&gt;</span><span style="color: #FFF;">prepare</span><span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">&quot;SELECT * FROM <span style="color: #99FF00;">$wpdb-&gt;comments</span> WHERE comment_ID = <span style="color: #009933; font-weight: bold;">%d</span>&quot;</span><span style="color: #CCC;">,</span> <span style="color: #7AB9BE;">$comment_id</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span> <span style="color: #999;">// 获取指定评论</span>
    <span style="color: #3D9EDD;">if</span><span style="color: #CCC;">&#40;</span><span style="color: #CCC;">!</span><span style="color: #7AB9BE;">$comment</span><span style="color: #CCC;">&#41;</span> <span style="color: #CCC;">&#123;</span> <span style="color: #999;">// 没有取到指定评论</span>
        <span style="color: #3D9EDD;">return</span> <span style="color: #CCC;">-</span><span style="color: #FFCC00;">1</span><span style="color: #CCC;">;</span>
    <span style="color: #CCC;">&#125;</span>
    <span style="color: #3D9EDD;">if</span><span style="color: #CCC;">&#40;</span><span style="color: #7AB9BE;">$comment</span><span style="color: #CCC;">-&gt;</span><span style="color: #FFF;">comment_parent</span> <span style="color: #CCC;">!=</span> <span style="color: #FFCC00;">0</span> <span style="color: #CCC;">&amp;&amp;</span> get_option<span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">'thread_comments'</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">&#41;</span> <span style="color: #CCC;">&#123;</span> <span style="color: #999;">// 开启了嵌套评论，且有父评论</span>
        <span style="color: #3D9EDD;">return</span> get_comment_page_number<span style="color: #CCC;">&#40;</span><span style="color: #7AB9BE;">$comment</span><span style="color: #CCC;">-&gt;</span><span style="color: #FFF;">comment_parent</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span> <span style="color: #999;">// 递归获取父评论页码</span>
    <span style="color: #CCC;">&#125;</span> <span style="color: #3D9EDD;">else</span> <span style="color: #CCC;">&#123;</span> <span style="color: #999;">// 没有父评论</span>
        <span style="color: #3D9EDD;">if</span><span style="color: #CCC;">&#40;</span>get_option<span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">'thread_comments'</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">&#41;</span> <span style="color: #CCC;">&#123;</span> <span style="color: #999;">// 开启了嵌套评论，则仅计算本文一级评论数</span>
            <span style="color: #7AB9BE;">$comment_root_position</span> <span style="color: #CCC;">=</span> <span style="color: #7AB9BE;">$wpdb</span><span style="color: #CCC;">-&gt;</span><span style="color: #FFF;">get_var</span><span style="color: #CCC;">&#40;</span><span style="color: #7AB9BE;">$wpdb</span><span style="color: #CCC;">-&gt;</span><span style="color: #FFF;">prepare</span><span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">&quot;SELECT count(*) FROM <span style="color: #99FF00;">$wpdb-&gt;comments</span> WHERE comment_post_ID = <span style="color: #009933; font-weight: bold;">%d</span> AND comment_parent = 0 AND comment_approved = 1 AND comment_ID &lt;= <span style="color: #009933; font-weight: bold;">%d</span>&quot;</span><span style="color: #CCC;">,</span> <span style="color: #7AB9BE;">$comment</span><span style="color: #CCC;">-&gt;</span><span style="color: #FFF;">comment_post_ID</span><span style="color: #CCC;">,</span> <span style="color: #7AB9BE;">$comment_id</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span>
            <span style="color: #7AB9BE;">$page_number</span> <span style="color: #CCC;">=</span> <span style="color: #996699;">ceil</span><span style="color: #CCC;">&#40;</span><span style="color: #7AB9BE;">$comment_root_position</span> <span style="color: #CCC;">/</span> get_option<span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">'comments_per_page'</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span>
        <span style="color: #CCC;">&#125;</span> <span style="color: #3D9EDD;">else</span> <span style="color: #CCC;">&#123;</span> <span style="color: #999;">// 未开启嵌套评论，计算本文之前所有评论数</span>
            <span style="color: #7AB9BE;">$comment_position</span> <span style="color: #CCC;">=</span> <span style="color: #7AB9BE;">$wpdb</span><span style="color: #CCC;">-&gt;</span><span style="color: #FFF;">get_var</span><span style="color: #CCC;">&#40;</span><span style="color: #7AB9BE;">$wpdb</span><span style="color: #CCC;">-&gt;</span><span style="color: #FFF;">prepare</span><span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">&quot;SELECT count(*) FROM <span style="color: #99FF00;">$wpdb-&gt;comments</span> WHERE comment_post_ID = <span style="color: #009933; font-weight: bold;">%d</span> AND comment_approved = 1 AND comment_ID &lt;= <span style="color: #009933; font-weight: bold;">%d</span>&quot;</span><span style="color: #CCC;">,</span> <span style="color: #7AB9BE;">$comment</span><span style="color: #CCC;">-&gt;</span><span style="color: #FFF;">comment_post_ID</span><span style="color: #CCC;">,</span> <span style="color: #7AB9BE;">$comment_id</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span>
            <span style="color: #7AB9BE;">$page_number</span> <span style="color: #CCC;">=</span> <span style="color: #996699;">ceil</span><span style="color: #CCC;">&#40;</span><span style="color: #7AB9BE;">$comment_position</span> <span style="color: #CCC;">/</span> get_option<span style="color: #CCC;">&#40;</span><span style="color: #7ACC00;">'comments_per_page'</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">&#41;</span><span style="color: #CCC;">;</span>
        <span style="color: #CCC;">&#125;</span>
        <span style="color: #3D9EDD;">return</span> <span style="color: #7AB9BE;">$page_number</span><span style="color: #CCC;">;</span>
    <span style="color: #CCC;">&#125;</span>
<span style="color: #CCC;">&#125;</span></pre></td></tr></table></div></div>

<p>不过需要注意的是，如果嵌套的层次比较深，可能会做比较多的数据库操作，比如如果 WP_RecentComments 列出的评论都要计算页码，10 个 3 级评论就要做 40 次数据库查询，效率比较低，不过可能是我写的 <acronym title="Structured Query Language">SQL</acronym> 代码不够高效吧，求优化。其实这样的需求其实也不是太多，仅属于锦上添花而已。另外一个可以应用的场合是，有很多主题都提供了 @ 回复的功能，在评论中自动插入原评论的链接，但当当前评论和原评论不在同一页内时，这个链接就失效了，但通过上面的方法利用原评论 ID 就可以将链接准确指向原始评论了。</p>
<div class="feed-after" style="margin:15px 0; clear:both;"><a href="http://lync.in">Lync.in</a> 是一个多人协作博客，欢迎您 <a href="http://lync.in/get-comment-page-number-in-wordpress/">查看原文</a> 以获得更好的阅读体验。</div>]]></content:encoded>
			<wfw:commentRss>http://lync.in/get-comment-page-number-in-wordpress/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
	</channel>
</rss>

