<?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>PlanABC - 怿飞’s Blog &#187; JS/Ajax/AS/Flex</title>
	<atom:link href="http://www.planabc.net/category/javascript-ajax/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.planabc.net</link>
	<description>落草为根—专注前端技术&#38;&#38;关注用户体验</description>
	<lastBuildDate>Tue, 24 Aug 2010 06:33:13 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>浅谈 Mousewheel 事件</title>
		<link>http://www.planabc.net/2010/08/12/mousewheel_event_in_javascript/</link>
		<comments>http://www.planabc.net/2010/08/12/mousewheel_event_in_javascript/#comments</comments>
		<pubDate>Thu, 12 Aug 2010 11:48:19 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[mouse]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=500</guid>
		<description><![CDATA[当需要制作转动鼠标滚轮放大页面字体这样的交互效果时，会用到 Mousewheel 事件。其实在大多数浏览器（IE6, IE7, IE8, Opera 10+, Safari 5+）中，都提供了 &#8220;mousewheel&#8221; 事件。但杯具的是 Firefox 3.5+ 却不支持此事件，不过庆幸 Firefox 3.5+ 中提供了另外一个等同的事件：&#8221;DOMMouseScroll&#8221; （事件和事件属性的测试案例）。 OK，我们现在已经知道了不同浏览器之间实现的差别，兼容代码如下： var addEvent = (function(){ if (window.addEventListener) { return function(el, sType, fn, capture) { el.addEventListener(sType, fn, (capture)); }; } else if (window.attachEvent) { return function(el, sType, fn, capture) { el.attachEvent("on" + sType, fn); }; } else { [...... ]]></description>
			<content:encoded><![CDATA[<p>当需要制作转动鼠标滚轮放大页面字体这样的交互效果时，会用到 Mousewheel 事件。其实在大多数浏览器（IE6, IE7, IE8, Opera 10+, Safari 5+）中，都提供了 <a href="http://msdn.microsoft.com/en-us/library/ms536951%28VS.85%29.aspx" target="_blank" title="onmousewheel Event">&#8220;mousewheel&#8221;</a> 事件。但杯具的是 Firefox 3.5+ 却不支持此事件，不过庆幸 Firefox 3.5+ 中提供了另外一个等同的事件：&#8221;DOMMouseScroll&#8221; （<a href="http://www.planabc.net/demo/event/mousewheel.html" target="_blank" title="Mousewheel Event in JavaScript">事件和事件属性的测试案例</a>）。</p>
<p>OK，我们现在已经知道了不同浏览器之间实现的差别，兼容代码如下：</p>
<pre><code>var addEvent = (function(){
        if (window.addEventListener) {
            return function(el, sType, fn, capture) {
                el.addEventListener(sType, fn, (capture));
            };
        } else if (window.attachEvent) {
            return function(el, sType, fn, capture) {
                el.attachEvent("on" + sType, fn);
            };
        } else {
            return function(){};
        }
    })(),
    // isFirefox 是伪代码，大家可以自行实现
    mousewheel = isFirefox ? "DOMMouseScroll" : "mousewheel";

// object 也是伪代码，你需要注册 Mousewheel 事件的元素
addEvent(object, mousewheel, function(event){
    event = window.event || event;
    // todo something
}, false);</code></pre>
<p>我们再回到要实现的交互效果上，现在还有其他一些问题需要来解决：</p>
<ol>
<li>页面字体到底是放大还是缩小呢？ ==> 鼠标滚轮是向上滚动还是向下滚动呢？</li>
<li>页面字体缩放的倍数到底是多少呢？ ==> 鼠标滚轮滚动的幅度大小是多少呢？</li>
</ol>
<p>还好，我们可以通过 event 的某些属性值得到这些信息：</p>
<ol>
<li>&#8220;mousewheel&#8221; 事件中的 &#8220;event.wheelDelta&#8221; 属性值：返回的值，如果是正值说明滚轮是向上滚动，如果是负值说明滚轮是向下滚动；返回的值，均为 120 的倍数，即：幅度大小 = 返回的值 / 120。 </li>
<li>&#8220;DOMMouseScroll&#8221; 事件中的 &#8220;event.detail&#8221; 属性值：返回的值，如果是负值说明滚轮是向上滚动（与 &#8220;event.wheelDelta&#8221; 正好相反），如果是正值说明滚轮是向下滚动；返回的值，均为 3 的倍数，即：幅度大小 = 返回的值 / 3。 </li>
<li>&#8220;mousewheel&#8221; 事件在 Opera 10+ 中却是个特例，既有 &#8220;event.wheelDelta&#8221; 属性，也有 &#8220;event.detail&#8221; 属性。</li>
</ol>
<p>注：上面第三点，在<a href="http://www.javascriptkit.com/javatutors/onmousewheel.shtml" target="_blank" title="The onmousewheel event of JavaScript">《The onmousewheel event of JavaScript》</a>一文中有这样一段批注：</p>
<blockquote><p>In Opera, &#8220;detail&#8221; returns the same value as it does in FF, so for the big O you should rely on &#8220;detail&#8221; instead of &#8220;wheelDelta&#8221;, which depending on the Opera version may return a different value than in IE&#8217;s.</p></blockquote>
<p>但经测试， Opera 9+ 和 Opera 10+ 中的 event.wheelDelta 属性与其他浏览器中的表现完全一致，未发现异常与错误，从接口角度来说，代码中应优先使用 &#8220;event.wheelDelta&#8221; 属性。</p>
<p>此时代码如下：</p>
<pre><code>var addEvent = (function(){
        if (window.addEventListener) {
            return function(el, sType, fn, capture) {
                el.addEventListener(sType, fn, (capture));
            };
        } else if (window.attachEvent) {
            return function(el, sType, fn, capture) {
                el.attachEvent("on" + sType, fn);
            };
        } else {
            return function(){};
        }
    })(),
    stopEvent: function(event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }

        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },
    zoomIn = function(){},
    zoomOut = function(){},
    // isFirefox 是伪代码，大家可以自行实现
    mousewheel = isFirefox ? "DOMMouseScroll" : "mousewheel";

// object 是伪代码，你需要注册 Mousewheel 事件的元素
addEvent(object, mousewheel, function(event){
    var delta = 0;
    event = window.event || event;
    stopEvent(event);

    delta = event.wheelDelta ? (event.wheelDelta / 120) : (- event.detail / 3);
    // zoomIn, zoomOut 是伪代码，需要实现的缩放事件
    delta > 0 ? zoomIn(delta): zoomOut(Math.abs(delta));
} , false);</code></pre>
<p>事件和事件属性的测试案例：<a href="http://www.planabc.net/demo/event/mousewheel.html" target="_blank" title="Mousewheel Event in JavaScript">http://www.planabc.net/demo/event/mousewheel.html</a></p>
<p>扩展阅读：</p>
<ul>
<li><a href="http://adomas.org/javascript-mouse-wheel/" target="_blank" title="Mouse wheel programming in JavaScript">《Mouse wheel programming in JavaScript》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=500&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/08/12/mousewheel_event_in_javascript/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>在线 Base64 编/解码小工具</title>
		<link>http://www.planabc.net/2010/07/22/online_base64_encoding_or_decoding_tools/</link>
		<comments>http://www.planabc.net/2010/07/22/online_base64_encoding_or_decoding_tools/#comments</comments>
		<pubDate>Thu, 22 Jul 2010 09:03:42 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[base64]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=484</guid>
		<description><![CDATA[抽时间做了一个简单的基于 JavaScript 的在线 Base64 编/解码小工具： http://www.planabc.net/lab/tools/base64.html 原理详见上篇文章：《JavaScript 中的 Base64 编码（一）：Encoding 》 注：对于下面的代码： TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4= 各浏览器原生的解码方法处理不太一致，Opera 比较智能能够处理此情形，但 Firefox、Safari 和 Chrome 却无法处理。 出现上面代码的原因为：规则约定 Base64 编码过程每76个字符加一个换行符。 Firefox、Safari 和 Chrome 下的修复非常简单（对输入的字符去除换行符等）： text = text.replace(/\s*/g, ''); //Fix... ]]></description>
			<content:encoded><![CDATA[<p>抽时间做了一个简单的基于 JavaScript 的在线 Base64 编/解码小工具：</p>
<p><a href="http://www.planabc.net/lab/tools/base64.html" title="Base64 Encoding or Decoding in Javascript" target="_blank">http://www.planabc.net/lab/tools/base64.html</a></p>
<p>原理详见上篇文章：<a href="http://www.planabc.net/2010/07/21/base64_encoding_in_javascript_part_i/" target="_blank" title="JavaScript 中的 Base64 编码（一）：Encoding ">《JavaScript 中的 Base64 编码（一）：Encoding 》</a></p>
<p><strong>注：</strong>对于下面的代码：</p>
<pre><code>TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=</code></pre>
<p>各浏览器原生的解码方法处理不太一致，Opera 比较智能能够处理此情形，但 Firefox、Safari 和 Chrome 却无法处理。</p>
<p>出现上面代码的原因为：<strong>规则约定  Base64 编码过程每76个字符加一个换行符</strong>。</p>
<p> Firefox、Safari 和 Chrome 下的修复非常简单（对输入的字符去除换行符等）：</p>
<pre><code>text = text.replace(/\s*/g, ''); //Fix Firefox/Safari/Chrom bug: MultiLine</code></pre>
<p>如有 BUG 欢迎随时联系：blankzheng#gmail.com</p>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=484&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/07/22/online_base64_encoding_or_decoding_tools/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>JavaScript 中的 Base64 编码（一）：Encoding</title>
		<link>http://www.planabc.net/2010/07/21/base64_encoding_in_javascript_part_i/</link>
		<comments>http://www.planabc.net/2010/07/21/base64_encoding_in_javascript_part_i/#comments</comments>
		<pubDate>Wed, 21 Jul 2010 15:05:03 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[base64]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=447</guid>
		<description><![CDATA[除了IE浏览器，其他所有主流的浏览器均支持原生的 Base64 编码： btoa(text) &#8211; base64 encodes text. atob(text) &#8211; base64 decodes text. 而对于 IE 我们可以根据已知的 Base64 编码原理进行编写： Base64 编码将每三个 8Bit 的字节（注：由于要求输入的字符为 8Bit 字节，故范围应该在 ASCII 字符范围内，即：\u0000-\u00ff）转换为四个 6Bit 的字节（3*8 = 4*6 = 24），然后在每个 6Bit 字节前添两位高位 0，组成四个 8Bit 的字节，最后再将每个 8Bit 字节转换成十进制的数字，对应 Base64 编码表（为了保证所输出的编码为可读字符，Base64制定了一个编码表，以便进行统一转换，编码表的大小为 2^6=64，即 Base64 名称的由来）输出编码后的字符。 如果原字节不足 3 的倍数，则用 0 填充，输出字符使用“=”，因此编码后输出的文本末尾可能会出现 1 或 2 个“=”（余数 = 原文字节数 [..... ]]></description>
			<content:encoded><![CDATA[<p>除了IE浏览器，其他所有主流的浏览器均支持原生的 Base64 编码：</p>
<ul>
<li><strong>btoa(text)</strong> &#8211; base64 encodes text.</li>
<li><strong>atob(text)</strong> &#8211; base64 decodes text.</li>
</ul>
<p>而对于 IE 我们可以根据已知的 Base64 编码原理进行编写：</p>
<p>Base64 编码将每三个 8Bit 的字节（<strong>注：</strong>由于要求输入的字符为 8Bit 字节，故范围应该在 ASCII 字符范围内，即：\u0000-\u00ff）转换为四个 6Bit 的字节（3*8 = 4*6 = 24），然后在每个 6Bit 字节前添两位高位 0，组成四个 8Bit 的字节，最后再将每个 8Bit 字节转换成十进制的数字，对应 Base64 编码表（为了保证所输出的编码为可读字符，Base64制定了一个编码表，以便进行统一转换，编码表的大小为 2^6=64，即 Base64 名称的由来）输出编码后的字符。</p>
<p>如果原字节不足 3 的倍数，则用 0 填充，输出字符使用“=”，因此编码后输出的文本末尾可能会出现 1 或 2 个“=”（余数 = 原文字节数 MOD 3 ，如果余数为 1，则要补 2 个“=”，为 2，则补 1 个“=”）。</p>
<style type="text/css">
/* base64table class for skinning normal tables */
table.base64table {
    margin: 1em 1em 1em 30px;
    background: #f9f9f9;
    border: 1px #aaa solid;
}
.base64table th, .base64table td {
    border: 1px #aaa solid;
    padding: 0.2em;
}
.base64table th {
    background: #f2f2f2;
    text-align: center;
}
.base64table caption {
    font-weight: bold;
}
</style>
<table style="text-align: center;" class="base64table">
<thead>
<tr>
<th colspan="11"> <strong>Base64 编码表</strong></th>
</tr>
</thead>
<tbody>
<tr>
<th scope="col">Value</th>
<th scope="col">Char</th>
<td rowspan="18">&nbsp;</td>
<th scope="col">Value</th>
<th scope="col">Char</th>
<td rowspan="18">&nbsp;</td>
<th scope="col">Value</th>
<th scope="col">Char</th>
<td rowspan="18">&nbsp;</td>
<th scope="col">Value</th>
<th scope="col">Char</th>
</tr>
<tr>
<td>0</td>
<td>A</td>
<td>16</td>
<td>Q</td>
<td>32</td>
<td>g</td>
<td>48</td>
<td>w</td>
</tr>
<tr>
<td>1</td>
<td>B</td>
<td>17</td>
<td>R</td>
<td>33</td>
<td>h</td>
<td>49</td>
<td>x</td>
</tr>
<tr>
<td>2</td>
<td>C</td>
<td>18</td>
<td>S</td>
<td>34</td>
<td>i</td>
<td>50</td>
<td>y</td>
</tr>
<tr>
<td>3</td>
<td>D</td>
<td>19</td>
<td>T</td>
<td>35</td>
<td>j</td>
<td>51</td>
<td>z</td>
</tr>
<tr>
<td>4</td>
<td>E</td>
<td>20</td>
<td>U</td>
<td>36</td>
<td>k</td>
<td>52</td>
<td>0</td>
</tr>
<tr>
<td>5</td>
<td>F</td>
<td>21</td>
<td>V</td>
<td>37</td>
<td>l</td>
<td>53</td>
<td>1</td>
</tr>
<tr>
<td>6</td>
<td>G</td>
<td>22</td>
<td>W</td>
<td>38</td>
<td>m</td>
<td>54</td>
<td>2</td>
</tr>
<tr>
<td>7</td>
<td>H</td>
<td>23</td>
<td>X</td>
<td>39</td>
<td>n</td>
<td>55</td>
<td>3</td>
</tr>
<tr>
<td>8</td>
<td>I</td>
<td>24</td>
<td>Y</td>
<td>40</td>
<td>o</td>
<td>56</td>
<td>4</td>
</tr>
<tr>
<td>9</td>
<td>J</td>
<td>25</td>
<td>Z</td>
<td>41</td>
<td>p</td>
<td>57</td>
<td>5</td>
</tr>
<tr>
<td>10</td>
<td>K</td>
<td>26</td>
<td>a</td>
<td>42</td>
<td>q</td>
<td>58</td>
<td>6</td>
</tr>
<tr>
<td>11</td>
<td>L</td>
<td>27</td>
<td>b</td>
<td>43</td>
<td>r</td>
<td>59</td>
<td>7</td>
</tr>
<tr>
<td>12</td>
<td>M</td>
<td>28</td>
<td>c</td>
<td>44</td>
<td>s</td>
<td>60</td>
<td>8</td>
</tr>
<tr>
<td>13</td>
<td>N</td>
<td>29</td>
<td>d</td>
<td>45</td>
<td>t</td>
<td>61</td>
<td>9</td>
</tr>
<tr>
<td>14</td>
<td>O</td>
<td>30</td>
<td>e</td>
<td>46</td>
<td>u</td>
<td>62</td>
<td>+</td>
</tr>
<tr>
<td>15</td>
<td>P</td>
<td>31</td>
<td>f</td>
<td>47</td>
<td>v</td>
<td>63</td>
<td>/</td>
</tr>
</tbody>
</table>
<p>比如：</p>
<pre><code>字符：               f        2         e
ASCII：              102      50        101
3个8Bit字节：        01100110 00110010  01100101
4个6Bit字节：          011001   100011    001001    100101
高位补0：            00011001 00100011  00001001  00100101
十进制：             25       35        9         37
对应码表值：         Z        j         J         l

最终： btoa('f2e') = ZjJl</code></pre>
<pre><code>字符：              b        a        s        e
ASCII：             98       97       115      101
3个8Bit字节：       01100010 01100001 01110011 01100101 00000000 00000000
4个6Bit字节：         011000   100110   000101   110011   011001   010000    000000   000000
高位补0：           00011000 00100110 00000101 00110011 00011001 00010000 00000000 00000000
十进制：            24       38       5        51       25       16
对应码表值：        Y        m        F        z        Z        Q        =        =

最终： btoa('base') = YmFzZQ==</code></pre>
<p>如果将上面的 Base64 编码原理换成接近于编程的思维，过程大致如下（以f2e为例）：</p>
<p><strong>注：</strong>Base64 编码表我们可以简化为字符串，并通过其进行位置索引：</p>
<pre><code>table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"</code></pre>
<p><strong>第一步：</strong></p>
<ol>
<li>第一个字符 f（102 -> 01100110）通过右移 2 位（first >> 2）获得第一个目标字符（00011001 -> 25），索引编码表中对应的目标字符 Z（table.charAt(first >> 2)）。</li>
</ol>
<p><strong>第二步：</strong></p>
<ol>
<li>将第一个字符 f（102 -> 01100110）与 3（00000011）进行 AND 运算（first &#038; 3)）:
<pre><code>    01100110
AND 00000011
------------
    00000010</code></pre>
</li>
<li>第一个字符运算后（00000010）再左移 4 位（(first &#038; 3) << 4），得：00100000。</li>
<li>将第二个字符 2 （50 -> 00110010）右移 4 位（second >> 4），得：00000011。</li>
<li>最后将运算后的两个字符进行 OR 运算（(first &#038; 3) << 4 | second >> 4）：
<pre><code>    00100000
OR  00000011
------------
    00100011</code></pre>
<p>获得第二个目标字符（00100011 -> 35），索引编码表中对应的目标字符 j（table.charAt((first &#038; 3) << 4 | second >> 4)）。</li>
</ol>
<p><strong>第三步：</strong></p>
<ol>
<li>将第二个字符 2 （50 -> 00110010）与 15（00001111，十六进制：0x0f）进行 AND 运算（second &#038; 0x0f）:
<pre><code>    00110010
AND 00001111
------------
    00000010
</code></pre>
</li>
<li>第二个字符运算后（00000010）再左移 2 位（(second &#038; 0x0f) << 2），得：00001000。</li>
<li>第三个字符 e（101 -> 01100101）右移 6 位（third >> 6），得：00000001。</li>
<li>最后将运算后的两个字符进行 OR 运算（(second &#038; 0x0f) << 2 | third >> 6）：
<pre><code>    00001000
OR  00000001
------------
    00001001</code></pre>
<p>即获得第三个目标字符（00001001 -> 9），索引编码表中对应的目标字符 J（table.charAt((second &#038; 0x0f) << 2 | third >> 6)）。</li>
</ol>
<p><strong>第四步：</strong></p>
<ol>
<li>取第三个字符 e（101 -> 01100101）的右 6 位，即与 63 （00111111，16进制：0x3f）进行 AND 运算（third &#038; 0x3f）：
<pre><code>    01100101
AND 00111111
------------
    00100101</code></pre>
<p>获得第四个目标字符（00100101 -> 37），索引编码表中对应的目标字符 l（table.charAt(third &#038; 0x3f)）。</li>
</ol>
<p><strong>异常情况：</strong></p>
<ol>
<li>当第二个字符不存在时（即：余数 = 原文字节数 MOD 3 ，余数为 1），截止至第二步的第 2 小步，然后在最终输出的目标字符后添加两个“=”。</li>
<li>当第三个字符不存在时（即：余数 = 原文字节数 MOD 3 ，余数为 2），截止至第三步的第 2 小步，然后在最终输出的目标字符后添加两个“=”。</li>
</ol>
<p>代码实现如下：</p>
<pre><code>if(!window.btoa) {
    window.btoa  = function(text) {
        if (/([^\u0000-\u00ff])/.test(text)) return;
        var table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
            i = 0,
            cur, prev, byteNum,
            result=[];    

        while(i < text.length){
            cur = text.charCodeAt(i);
            byteNum = (i+1) % 3;

            switch(byteNum){
                case 1: //first byte
                    result.push(table.charAt(cur >> 2));
                    break;

                case 2: //second byte
                    result.push(table.charAt((prev &#038; 3) << 4 | (cur >> 4)));
                    break;

                case 0: //third byte
                    result.push(table.charAt((prev &#038; 0x0f) << 2 | (cur >> 6)));
                    result.push(table.charAt(cur &#038; 0x3f));
                    break;
            }

            prev = cur;
            i++;
        }

        if (byteNum == 1){
            result.push(table.charAt((prev &#038; 3) << 4));
            result.push("==");
        } else if (byteNum == 2){
            result.push(table.charAt((prev &#038; 0x0f) << 2));
            result.push("=");
        }

        return result.join("");
    }
}</code> </pre>
<p>-----------------------------------------------------------------------------</p>
<p><strong>下篇：《JavaScript 中的 Base64 编码（二）：Decoding》</strong></p>
<p>-----------------------------------------------------------------------------</p>
<p><strong>扩展阅读：</strong></p>
<ul>
<li><a href="http://www.nczonline.net/blog/2009/12/08/computer-science-in-javascript-base64-encoding/" target="_blank" title="Computer science in JavaScript: Base64 encoding">《Computer science in JavaScript: Base64 encoding》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=447&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/07/21/base64_encoding_in_javascript_part_i/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>用 JavaScript 解数学题</title>
		<link>http://www.planabc.net/2010/05/26/solving_the_mathematical_problem_using_javascript/</link>
		<comments>http://www.planabc.net/2010/05/26/solving_the_mathematical_problem_using_javascript/#comments</comments>
		<pubDate>Wed, 26 May 2010 10:42:37 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=431</guid>
		<description><![CDATA[题目：一个六位数，分别用2，3，4，5，6乘它，得到的五个新数仍是由原数中的六个数字组成，只是位置不同，则此六位数是多少？ function evaluate() { var multiplier = [2,3,4,5,6], sortNumber = function (number) { return parseInt((number + '').split('').sort().join(''), 10); }, gameOver = false, // 判断是否已经找到，如果找到，结束循环 determine = function(origin) { var result, i, len = multiplier.length; for( i = 0; i < len; i++ ) { result = origin * multiplier[i]; if (result < 100000 &#124;&#124; result [...... ]]></description>
			<content:encoded><![CDATA[<p><strong>题目：</strong>一个六位数，分别用2，3，4，5，6乘它，得到的五个新数仍是由原数中的六个数字组成，只是位置不同，则此六位数是多少？</p>
<pre><code>function evaluate() {
    var multiplier = [2,3,4,5,6],
        sortNumber = function (number) {
            return parseInt((number + '').split('').sort().join(''), 10);
        },
        gameOver = false, // 判断是否已经找到，如果找到，结束循环
        determine = function(origin) {
            var result, i, len = multiplier.length;
            for( i = 0; i < len; i++ ) {
                result = origin * multiplier[i];
                if (result < 100000 || result > 999999 || sortNumber(result) !== sortNumber(origin)) {
                    return;
                }
                if(i === (len-1)){
                    gameOver = true;
                    alert('此六位数是 ' + origin);
                }
            }
        };

    // Math.floor(1000000/6) 缩小范围
    for(var j = Math.floor(1000000/6); j >= 100000  &#038;&#038; !gameOver; j--) {
        determine(j);
    }
}</code></pre>
<p>扩展阅读：</p>
<ul>
<li><a href="http://scriptfans.javaeye.com/blog/100114" target="_blank" title="用python解一道奥数题（小学三年级）">《用python解一道奥数题（小学三年级）》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=431&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/05/26/solving_the_mathematical_problem_using_javascript/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>与 Function 和  Object 相关的有趣代码</title>
		<link>http://www.planabc.net/2010/05/06/interesting_code_associated_with_function_and_object/</link>
		<comments>http://www.planabc.net/2010/05/06/interesting_code_associated_with_function_and_object/#comments</comments>
		<pubDate>Thu, 06 May 2010 03:33:05 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[function]]></category>
		<category><![CDATA[object]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=411</guid>
		<description><![CDATA[代码片段一： alert(Function instanceof Object); // true alert(Object instanceof Function); // true Function 是 Object 的实例，Object 也是 Function 的实例，好“纠缠”的关系。 代码片段一： alert(Object.forEach); // undefined Function.prototype.forEach = function(object, block, context) { for (var key in object) { if (typeof this.prototype[key] == "undefined") { block.call(context, object[key], key, object); } } }; alert(Object.forEach); alert(Function.forEach); alert(Object.forEach === Function.forEach); // true 给 [...... ]]></description>
			<content:encoded><![CDATA[<p>代码片段一：</p>
<pre><code>alert(Function instanceof Object); // true
alert(Object instanceof Function); // true</code></pre>
<p>Function 是 Object 的实例，Object 也是 Function 的实例，好“纠缠”的关系。</p>
<p>代码片段一：</p>
<pre><code>alert(Object.forEach); // undefined

Function.prototype.forEach = function(object, block, context) {
    for (var key in object) {
        if (typeof this.prototype[key] == "undefined") {
            block.call(context, object[key], key, object);
        }
    }
};

alert(Object.forEach);
alert(Function.forEach);
alert(Object.forEach === Function.forEach); // true</code></pre>
<p>给 Function 设置的原型方法 forEach，“有趣”的是 Object 也能够获取。</p>
<p>至于为什么可以从 <a href="http://www.mollypages.org/misc/js.mp" target="_blank" title="JavaScript Object layout">JavaScript Object layout</a> 图中获取部分解答：</p>
<p><img src="http://www.planabc.net/wp-content/uploads/2010/05/javascript_object_layout.jpg" alt="JavaScript Object layout" / ></p>
<p>看懂上图了没？如果看懂了，留道思考题给大家：</p>
<pre><code>function Foo() {};
var foo = new Foo();

alert(foo instanceof Foo); // ?
alert(foo instanceof Object); // ?
alert(foo instanceof Function); // ?
alert(Foo instanceof Function); // ?
alert(Foo instanceof Object); // ?</code></pre>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=411&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/05/06/interesting_code_associated_with_function_and_object/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>学习 YUI3 中的沙箱机制</title>
		<link>http://www.planabc.net/2010/04/08/study_sandbox_pattern_in_yui3/</link>
		<comments>http://www.planabc.net/2010/04/08/study_sandbox_pattern_in_yui3/#comments</comments>
		<pubDate>Thu, 08 Apr 2010 12:40:07 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[pattern]]></category>
		<category><![CDATA[YUI]]></category>
		<category><![CDATA[YUI3]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=403</guid>
		<description><![CDATA[简化了一下 YUI3 中的沙箱实现 方式： if (typeof Sandbox === 'undefined' &#124;&#124; !Sandbox) { Sandbox = function(o) { var self = this; if (!(self instanceof Sandbox)) { // 允许没有 new 操作符的实例化 return new Sandbox(o); } else { self._init(); self._config(o); // 预加载某些指定模块 /* self._setup(); */ // 返回实例本身，支持链式模式 return self; } }; } // Sandbox的（类）静态属性 Sandbox.Env = { /* [...... ]]></description>
			<content:encoded><![CDATA[<p>简化了一下 <a href="http://developer.yahoo.com/yui/3/api/yui.js.html" target="_blank" title="API: yui yui.js (YUI Library)">YUI3 中的沙箱实现</a> 方式：</p>
<pre><code>if (typeof Sandbox === 'undefined' || !Sandbox) {
    Sandbox = function(o) {
        var self = this;

        if (!(self instanceof Sandbox)) { // 允许没有 new 操作符的实例化
            return new Sandbox(o);
        } else {
            self._init();
            self._config(o);

            // 预加载某些指定模块
            /* self._setup(); */

            // 返回实例本身，支持链式模式
            return self;
        }
    };
}

// Sandbox的（类）静态属性
Sandbox.Env = {
    /* sidx: 0 , */
    mods: {}
};

(function() {
    var p, i,
        SLICE         = Array.prototype.slice,
        /* instances     = {}, */
        time          = new Date().getTime(),
        win           = window,
        doc           = document;

    Sandbox.prototype = {

        // 格式化配置参数
        // NOTE：本着简单适用的原则，取消了原先 YUI 中对 loader 的支持
        _config: function(o) {

            var c = this.config, i, j, m, mods;

            o = o || {};

            // mods = c.modules;

            for (i in o) {
                if (i == 'win') {
                    c[i]  = o[i].contentWindow || o[i];
                    c.doc = c[i].document;
                } else {
                    c[i]  = o[i];
                }
            }
        },

        /**
         * 初始化沙箱实例
         * @private
         */
        _init: function() {

            var self  = this,
                G_Env = Sandbox.Env,
                Env   = self.Env;

            if(!Env) {
                self.Env = {
                    mods: {},
                    _used: {},
                    _attached: {},
                    _loaded: {}
                };

                Env = self.Env;

                /* if (G_Env &#038;&#038; self !== Sandbox ) {
                    Env._sidx  = ++ G_Env.sidx;
                    Env._guid = ('sandbox_' + Env._sidx + '_' + time).replace(/\./g, '_');
                }

                self.id = Env._guid;
                instances[self.id] = self; */
            }

            self.constructor = Sandbox;

            self.config = {
                win: win || {},
                doc: doc || {}
            };

        },

        // 预留预加载某些指定模块，接口可根据实际需要扩展
        /* _setup: function(o) {}, */

        /**
         * 添加模块
         * @method add
         * @param name {string} 模块名
         * @param fn {Function} 模块对应的函数
         * @param version {string}
         * @param details 可选配置：
         *     requires   -  {array}  在本模块执行之前附加的必须的模块数组
         *     use  - {array} 在本模块执行之后附加的模块数组
         *
         */
        add: function(name, fn, details) {
            Sandbox.Env.mods[name] = {
                name: name,
                fn: fn,
                details: details || {}
            };

            return this; // chain support
        },

        /**
         * 执行与 Sandbox 实例相关联的模块：details.requires--》fn--》details.use
         * @method _attach
         * @param r {array} 模块列表数组
         * @private
         */

        _attach: function(r) {

            var mods = Sandbox.Env.mods,
                self = this,
                attached = self.Env._attached,
                i, l = r.length, name, m, fn, d, req, use;

            for (i = 0; i < l; i = i+1) {

                name = r[i];
                m    = mods[name];

                if (!attached[name] &#038;&#038; m) {

                    attached[name] = true;

                    fn  = m.fn;
                    d   = m.details;
                    req = d.requires;
                    use = d.use;

                    if (req) {
                        self._attach(req);
                    }

                    if (fn) {
                        fn(self);
                    }

                    if (use) {
                        self._attach(use);
                    }
                }
            }

        },

        /**
         * 绑定模块至 Sandbox 实例
         * @param modules* {string} 1-n 个模块 (uses arguments array)
         * @param *callback {function} callback function  如果包括，必须是最后一个参数。
         *
         * Sandbox().use('planabc.net')
         * Sandbox().use('planabc.net',function(){})
         * Sandbox().use('planabc.net','planabc.com')
         * Sandbox().use('planabc.net','planabc.com',function(){})
         * Sandbox().use('*'); // use all available modules
         *
         */
        use: function() {

            var self = this,
                a = SLICE.call(arguments, 0),
                mods = Sandbox.Env.mods,
                used = self.Env._used,
                loader,
                firstArg = a[0],
                callback = a[a.length-1],
                k, i, l,
                r = [],
                process = function(name) {

                    // 添加模块至附加的模块列表
                    r.push(name);

                    // 一个模块仅附加一次
                    if (used[name]) {
                        return;
                    }

                    var m = mods[name], req, use, j, jl, t, tl,
                        d = m.details;

                    if (m) {
                        used[name] = true;

                        req = d.requires;
                        use = d.use;
                    }

                    // 附加上 requires 模块
                    if (req) {
                        for (j = 0,jl = req.length ; j < jl; j = j + 1) {
                            process(req[j]);
                        }
                    }

                    // 附加上 use 模块
                    if (use) {
                        for (t = 0, tl = use.length; t < tl; t = t + 1) {
                            process(use[t]);
                        }
                    }

                },

                onComplete;

            if (typeof callback === 'function') {
                a.pop();
            } else {
                callback = null;
            }

            onComplete = function() {
                if (callback) {
                    callback(self);
                }
            };

            // Sandbox().use('*');
            if (firstArg === "*") {
                a = [];
                for (k in mods) {
                    if (mods.hasOwnProperty(k)) {
                        a.push(k);
                    }
                }

                if (callback) {
                    a.push(callback);
                }

                return self.use.apply(self, a);
            }

            l = a.length;

            // 处理所有必须和附加的模块
            for (i = 0; i < l; i = i + 1) {
                process(a[i]);
            }

            self._attach(r);
            onComplete();

            return self; // chain support
        }
    };

})();</code></pre>
<p>测试页面：<a href="http://www.planabc.net/lab/yui/sandbox.html" target="_blank" title="YUI3 中的沙箱机制">http://www.planabc.net/lab/yui/sandbox.html</a></p>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=403&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/04/08/study_sandbox_pattern_in_yui3/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>前端 JavaScript 相关的小Tips</title>
		<link>http://www.planabc.net/2010/04/01/some_tips_for_using_javascript/</link>
		<comments>http://www.planabc.net/2010/04/01/some_tips_for_using_javascript/#comments</comments>
		<pubDate>Thu, 01 Apr 2010 07:27:07 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[json]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=398</guid>
		<description><![CDATA[受 SNS 后台开发同学的邀请，做的一次小分享，希望够简单、明了、有效。其实在给别人分享的同时，自己也能收获很多... ]]></description>
			<content:encoded><![CDATA[<p>受 SNS 后台开发同学的邀请，做的一次小分享，希望够简单、明了、有效。其实在给别人分享的同时，自己也能收获很多！</p>
<p>    <object width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=jstips-100401005855-phpapp01&#038;stripped_title=javascript-tips" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=jstips-100401005855-phpapp01&#038;stripped_title=javascript-tips" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object></p>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=398&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/04/01/some_tips_for_using_javascript/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>如何获取当前 select 元素的值</title>
		<link>http://www.planabc.net/2010/03/27/how_to_get_select_element_value/</link>
		<comments>http://www.planabc.net/2010/03/27/how_to_get_select_element_value/#comments</comments>
		<pubDate>Sat, 27 Mar 2010 06:05:20 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[form]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[select]]></category>

		<guid isPermaLink="false">http://www.planabc.net/2010/03/27/%e5%a6%82%e4%bd%95%e8%8e%b7%e5%8f%96%e5%bd%93%e5%89%8d-select-%e5%85%83%e7%b4%a0%e7%9a%84%e5%80%bc/</guid>
		<description><![CDATA[如果 select 元素下的所有 option 元素均没有指定 selected 属性，会默认选中第一个。 可以通过 select.selectedIndex 获取到选中的 option 元素的索引。 可以通过 select.options[select.selectedIndex] 获取到选中的 option 元素。 option 元素 &#60;option selected="selected" value="value3"&#62;text3&#60;/option&#62;，可以通过 option.value 获得 option 元素的 value 属性值，即 value3；可以通过 option.text 获得 option 元素内的文本，即 text3。 如果 option 元素没有定义 value 属性，则 IE 中 option.value 无法获得，但 Safari、Opera、FireFox 依旧可以通过 option.value 获得，值同于 option.text 。 可以通过 option.attributes.value &#038;&#038; option.attributes.value.specified 来判断 option [...... ]]></description>
			<content:encoded><![CDATA[<ol>
<li>如果 select 元素下的所有 option 元素均没有指定 selected 属性，会默认选中第一个。</li>
<li>可以通过 <code>select.selectedIndex</code> 获取到选中的 option 元素的索引。</li>
<li>可以通过 <code>select.options[select.selectedIndex]</code> 获取到选中的  option 元素。</li>
<li>option 元素 <code>&lt;option selected="selected" value="value3"&gt;text3&lt;/option&gt;</code>，可以通过 option.value 获得 option 元素的 value 属性值，即 value3；可以通过 option.text 获得 option 元素内的文本，即 text3。</li>
<li>如果 option 元素没有定义 value 属性，则 IE 中 option.value 无法获得，但 <strong>Safari、Opera、FireFox 依旧可以通过 option.value 获得，值同于 option.text </strong>。</li>
<li>可以通过 <code>option.attributes.value &#038;&#038; option.attributes.value.specified</code> 来判断 option 元素是否定义了 value 属性。</li>
</ol>
<p>故，获得当前 select 元素值的脚本如下：</p>
<pre><code>var getSelectValue = function(select) {
    var idx = select.selectedIndex,
        option,
        value;
    if (idx > -1) {
        option = select.options[idx];
        value = option.attributes.value;
        return (value &#038;&#038; value.specified) ? option.value : option.text;
    }
    return null;
}</code></pre>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=390&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/03/27/how_to_get_select_element_value/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>如何在事件代理中正确使用 focus 和 blur 事件</title>
		<link>http://www.planabc.net/2010/01/30/how_to_use_focus_and_blur_event_in_event_delegation/</link>
		<comments>http://www.planabc.net/2010/01/30/how_to_use_focus_and_blur_event_in_event_delegation/#comments</comments>
		<pubDate>Fri, 29 Jan 2010 17:36:59 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[event]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=382</guid>
		<description><![CDATA[什么是事件代理（Event Delegation）？ 如果不太了解的朋友，可详细阅读：《Event delegation in JavaScript》，这里不再累述。 首先让我们一起来回顾一些常识： 通常支持事件冒泡（Event Bubbling）的事件类型为鼠标事件和键盘事件，例如：mouseover, mouseout, click, keydown, keypress。 接口事件则通常不支持事件冒泡（Event Bubbling），例如：load, change, submit, focus, blur。 很明显：focus 和 blur 都属于不支持冒泡的接口事件。既然都不支持冒泡，那又如何实现事件代理呢？ 可以换个角度，逆向思维，尝试事件捕获（Event Capturing，除了IE，现在流行的标准浏览器均支持）。 测试后会发现，如果你捕获 focus 或 blur 事件，目标元素的祖先元素均执行事件函数。至于为什么？或许是实现的一个 BUG。 el.addEventListener('focus', focusHandler, true); el.addEventListener('blur', blurHandler, true); 那对于 I... ]]></description>
			<content:encoded><![CDATA[<p>什么是事件代理（Event Delegation）？</p>
<p>如果不太了解的朋友，可详细阅读：<a href="http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/" target="_blank" title="Event delegation in JavaScript">《Event delegation in JavaScript》</a>，这里不再累述。</p>
<p>首先让我们一起来回顾一些常识：</p>
<ol>
<li>通常支持事件冒泡（Event Bubbling）的事件类型为鼠标事件和键盘事件，例如：<strong>mouseover, mouseout, click, keydown, keypress</strong>。</li>
<li>接口事件则通常不支持事件冒泡（Event Bubbling），例如：<strong>load, change, submit, focus, blur</strong>。</li>
</ol>
<p>很明显：focus 和 blur 都属于不支持冒泡的接口事件。既然都不支持冒泡，那又如何实现事件代理呢？</p>
<p>可以换个角度，逆向思维，尝试事件捕获（Event Capturing，除了IE，现在流行的标准浏览器均支持）。</p>
<p>测试后会发现，如果你捕获 focus 或 blur 事件，目标元素的祖先元素均执行事件函数。至于为什么？或许是实现的一个 BUG。</p>
<pre><code>el.addEventListener('focus', focusHandler, true);
el.addEventListener('blur', blurHandler, true);</code></pre>
<p>那对于 IE ，我们如何实现呢？</p>
<p>非常幸运，IE 下支持 focusin 和 focusout 事件，非常类似于 focus 和 blur 事件，唯一不同的是，这两种事件支持事件冒泡（Event Bubbling）。</p>
<pre><code>el.onfocusin = focusHandler;
el.onfocusout = blurHandler;</code></pre>
<p>很完美的解决方案：</p>
<pre><code>if (document.addEventListener) {
    el.addEventListener('focus', focusHandler, true);
    el.addEventListener('blur', blurHandler, true);
} else {
    el.onfocusin = focusHandler;
    el.onfocusout = blurHandler;
}</code></pre>
<p>当你下次看到 <a href="http://developer.yahoo.com/yui/2/" title="YUI 2.8" target="_blank">YUI 2.8</a> 的 event/event-debug.js 源码中下面几段代码时，一定会清晰很多：</p>
<pre><code>576    // String constants used by the addFocusListener and removeFocusListener methods
577
578     FOCUSIN = "focusin",
579     FOCUSOUT = "focusout";

        ....

750     _specialTypes: {
751         focusin: (isIE ? "focusin" : "focus"),
752         focusout: (isIE ? "focusout" : "blur")
753      },

        ....

1053    addListener: function (el, sType, fn, obj, overrideContext) {
1054
1055	    var capture = ((sType == FOCUSIN || sType == FOCUSOUT) &#038;&#038; !YAHOO.env.ua.ie) ? true : false;
1056
1057        return this._addListener(el, this._getType(sType), fn, obj, overrideContext, capture);
1058
1059    },</code></pre>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=382&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/01/30/how_to_use_focus_and_blur_event_in_event_delegation/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>修复 jQuery 中 isFunction 方法的 BUG</title>
		<link>http://www.planabc.net/2010/01/23/repair_the_bug_of_isfunction_method_in_jquery/</link>
		<comments>http://www.planabc.net/2010/01/23/repair_the_bug_of_isfunction_method_in_jquery/#comments</comments>
		<pubDate>Sat, 23 Jan 2010 09:34:09 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=376</guid>
		<description><![CDATA[jQuery 1.4 源码 449 行（core.js 431 行），判断是否为函数的方法如下（思路来源于 Douglas Crockford 的《The Miller Device》）： isFunction: function( obj ) { return toString.call(obj) === "[object Function]"; }, 同时 jQuery 的作者也作了部分注释： See test/unit/core.js for details concerning isFunction. Since version 1.3, DOM methods and functions like alert aren&#8217;t supported. They return false on IE (#2968). 即：此方法在 IE 下无法正确识别 DOM 方法和一些函数（例如 alert [...... ]]></description>
			<content:encoded><![CDATA[<p><a href="http://github.com/jquery/jquery" title="JQuery 1.4" target="_blank">jQuery 1.4</a> 源码 449 行（core.js 431 行），判断是否为函数的方法如下（思路来源于 <a href="http://www.crockford.com/" title="Douglas Crockford" target="_blank">Douglas Crockford</a> 的《The Miller Device》）：</p>
<pre><code>isFunction: function( obj ) {
    return toString.call(obj) === "[object Function]";
},</code></pre>
<p>同时 jQuery 的作者也作了部分注释：</p>
<blockquote><p>See test/unit/core.js for details concerning isFunction. Since version 1.3, DOM methods and functions like alert aren&#8217;t supported. They return false on IE (#2968).</p></blockquote>
<p>即：此方法在 IE 下无法正确识别 DOM 方法和一些函数（例如 alert 方法等）。</p>
<p>为什么会这样呢？</p>
<p>详细看测试页面：<a href="http://www.planabc.net/demo/isfunction/">http://www.planabc.net/demo/isfunction/</a></p>
<p>会发现在 IE 下用 typeof 检测 alert、confirm 方法以及 DOM 的方法显示 object，而其他浏览器下显示 function。</p>
<p>那如何完善这个问题呢？</p>
<ol>
<li>typeof 检测某个方法（例如：document.getElementById） 是否是 object，如何是，则重写 isFunction 函数；</li>
<li>怎样重写呢？正则判断传入的对象字符串后（&#8221;" + fn），是否起始位置含有 function，即：/^\s*\bfunction\b/.test(&#8221; + fn)。</li>
</ol>
<p>OK，看下根据以上思路修改后的 isFunction 函数：</p>
<pre><code>var isFunction = (function() {
    // Performance optimization: Lazy Function Definition
    return "object"  === typeof document.getElementById ?
           isFunction = function(fn){
                try {
                    return /^\s*\bfunction\b/.test("" + fn);
                } catch (x) {
                    return false
                }
           }:
           isFunction = function(fn){
               return "[object Function]" === Object.prototype.toString.call(fn);
           };
})()</code></pre>
<p>参考阅读：</p>
<ul>
<li><a href="http://webreflection.blogspot.com/2009/08/isfunction-hacked-iscallable-solution.html" title="isFunction hacked, isCallable solution" target="_blank">《isFunction hacked, isCallable solution》</a></li>
<li><a href="http://dbj.org/dbj/?p=286" title="isFunction() or isObject(), that is the question ?" target="_blank">《isFunction() or isObject(), that is the question ? 》</a></li>
<li><a href="http://peter.michaux.ca/articles/lazy-function-definition-pattern" title="Lazy Function Definition Pattern" target="_blank">《Lazy Function Definition Pattern》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=376&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/01/23/repair_the_bug_of_isfunction_method_in_jquery/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>如何将函数的实际参数转换成数组</title>
		<link>http://www.planabc.net/2010/01/06/arguments_to_array/</link>
		<comments>http://www.planabc.net/2010/01/06/arguments_to_array/#comments</comments>
		<pubDate>Wed, 06 Jan 2010 10:21:18 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[arguments]]></category>
		<category><![CDATA[Array]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=354</guid>
		<description><![CDATA[实际参数在函数中我们可以使用 arguments 对象获得 （注：形参可通过 arguments.callee 获得），虽然 arguments 对象与数组形似，但仍不是真正意义上的数组。 值得庆幸的是，我们可以通过数组的 slice 方法将 arguments 对象转换成真正的数组： var args = Array.prototype.slice.call(arguments, 0); 对于slice 方法，ECMAScript 262 中 15.4.4.10 Array.prototype.slice (start, end) 章节有备注： The slice function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects [...... ]]></description>
			<content:encoded><![CDATA[<p>实际参数在函数中我们可以使用 <code>arguments</code> 对象获得 （注：形参可通过 <code>arguments.callee</code> 获得），虽然 <code>arguments</code> 对象与数组形似，但仍不是真正意义上的数组。</p>
<p>值得庆幸的是，我们可以通过数组的 <code>slice</code> 方法将 <code>arguments</code> 对象转换成真正的数组：</p>
<pre><code>var args = Array.prototype.slice.call(arguments, 0);</code></pre>
<p>对于<code>slice</code> 方法，<a href="http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf" title="ECMAScript" target="_blank">ECMAScript 262</a> 中 15.4.4.10 Array.prototype.slice (start, end) 章节有备注：</p>
<blockquote><p>The slice function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the slice function can be applied successfully to a host object is implementation-dependent.</p></blockquote>
<p><a href="http://jsdesignpatterns.com/" target="_blank" title="Pro JavaScript Design Patterns">《Pro JavaScript Design Patterns》</a>（《JavaScript 设计模式》）的作者 <a href="http://www.dustindiaz.com/" title="Dustin Diaz: ./with Imagination" target="_blank">Dustin Diaz</a> 曾指出：</p>
<blockquote><p>
instead of…<br />
var args = Array.prototype.slice.call(arguments, 0); // 怿飞注：下称方法一<br />
do this…<br />
var args = [].slice.call(arguments, 0); // 怿飞注：下称方法二</p></blockquote>
<p>但二者的性能差异真的存在吗？经过个人简单测试发现：</p>
<p><del datetime="2010-01-30T07:44:07+00:00">在 <code>arguments.length</code> 较小的时候，方法二性能上稍有一点点优势，而在<code>arguments.length</code> 较大的时候，方法一却又稍有优势。</del></p>
<p>2010年1月30日更新（<a href="http://www.planabc.net/demo/toarray/arguments.html" target="_blank">测试地址</a>）：几经验证，性能差异不大，反而第一张方法性能稍优势一点，或许由于第二种方法创建新数组产生开销。</p>
<p>最后附上方法三，最老土的方式：</p>
<pre><code>var args = [];
for (var i = 1; i < arguments.length; i++) {
    args.push(arguments[i]);
}</code></pre>
<p>不过对于平常来说，个人建议使用第一种方法，但任何解决方案，没有最好的，只有最合适：</p>
<pre><code>var args = Array.prototype.slice.call(arguments, 0); </code></pre>
<p>------------------------------------------------------------------</p>
<p><strong>如何将 <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-536297177" target="_blank" title="NodeList">NodeList</a> （比如：document.getElementsByTagName('div')）转换成数组呢？</strong></p>
<p>解决方案简单如下：</p>
<pre><code>function nodeListToArray(nodes){
    var arr, length;

    try {
        // works in every browser except IE
        arr = [].slice.call(nodes);
        return arr;
    } catch(err){
        // slower, but works in IE
        arr = [];
        length = nodes.length;

        for(var i = 0; i < length; i++){
             arr.push(nodes[i]);
         }  

        return arr;
    }
} </code></pre>
<p>为什么 IE 中 NodeList 不可以使用 [].slice.call(nodes) 方法转换呢？</p>
<blockquote><p>In Internet Explorer it throws an error that it can't run Array.prototype.slice.call(nodes) because a DOM NodeList is not a JavaScript object.</p></blockquote>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=354&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/01/06/arguments_to_array/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>删除数组中重复项（uniq）</title>
		<link>http://www.planabc.net/2009/12/26/array_uniq/</link>
		<comments>http://www.planabc.net/2009/12/26/array_uniq/#comments</comments>
		<pubDate>Sat, 26 Dec 2009 11:08:03 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[Array]]></category>
		<category><![CDATA[uniq]]></category>
		<category><![CDATA[YUI]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=338</guid>
		<description><![CDATA[首先让我们看下 YUI 是如何处理的： var toObject = function(a) { var o = {}; for (var i = 0; i < a.length; i = i+1) { o[a[i]] = true; } return o; }; var keys = function(o) { var a=[], i; for (i in o) { if (lang.hasOwnProperty(o, i)) { // YUI的方法 a.push(i); } } return a; [...... ]]></description>
			<content:encoded><![CDATA[<p>首先让我们看下 <a href="http://developer.yahoo.com/yui/" target="_blank" title="YUI">YUI</a> 是如何处理的：</p>
<pre><code>var toObject = function(a) {
    var o = {};
    for (var i = 0; i < a.length; i = i+1) {
     o[a[i]] = true;
    }
    return o;
};

var keys = function(o) {
    var a=[], i;
    for (i in o) {
        if (lang.hasOwnProperty(o, i)) { // YUI的方法
            a.push(i);
        }
    }
    return a;
};

var uniq = function(a) {
    return keys(toObject(a));
};</code></pre>
<p>详细分析，见同事 <a href="http://www.mytcer.com/" title="长天">长天</a> 的分享 <a href="http://www.mytcer.com/357" target="_blank" title="巧妙去除数组中的重复项">《巧妙去除数组中的重复项》</a>。</p>
<p>自己使用的方式与 <a href="http://developer.yahoo.com/yui/" target="_blank" title="YUI">YUI</a> 的方式十分相似，不过仅使用了一次循环便完成了删除数组中重复项，如下：</p>
<pre><code>var uniq = function (arr) {
    var a = [],
        o = {},
        i,
        v,
        len = arr.length;

    if (len < 2) {
        return arr;
    }

    for (i = 0; i < len; i++) {
        v = arr[i];
        if (o[v] !== 1) {
            a.push(v);
            o[v] = 1;
        }
    }

    return a;
}</code></pre>
<p>经过了简单的测试：自己使用的方式性能远远高于 <a href="http://developer.yahoo.com/yui/" target="_blank" title="YUI">YUI</a> 的方式。</p>
<p>抛砖引玉，欢迎大家提供更好的处理方法。</p>
<p><strong>2009年12月28日更新：</strong></p>
<p>以上两种函数方法暂时都不能处理复杂的含有混合类型的数组（感谢 小猫 提出的疑问），如：[0,"0",1,"1",0]、["null",null]。</p>
<p>对于能够约定类型为数字（注：要求非0开头的数字，小数除外）或字符串的数组，我们可以用改进后的函数方法（感谢 <a href="http://cfanseal.cnblogs.com/" target="_blank" title="closurecache">closurecache</a> 提供的思路）：</p>
<pre><code>var uniq = function (arr) {
    var a = [],
        o = {},
        i,
        v,
        cv, // corrected value
        len = arr.length;

    if (len < 2) {
        return arr;
    }

    for (i = 0; i < len; i++) {
        v = arr[i];

        /* closurecache 提供的函数中使用的是  cv = v + 0;，
         * 这样就无法辨别类似[1, 10, "1", "10"]的数组，
         * 因为运算后 => 1, 10, 10, 100，很明显，出现了重复的标示符。
         * 加前面就难道没问题吗？
         * 有的：数组中不能出现类似01 、001，以 0 开头的数字，
         * 但适用性比原先更广。
         */
        cv = 0 + v;

        if (!o[cv]) {
            a.push(v);
            o[cv] = true;
        }
    }

    return a;
}</code></pre>
<p>如果大家想在此解题思路的基础上，更完美一点，推荐 <a href="http://www.limboy.com/" title="Dexter.Yy" target="_blank">Dexter.Yy</a> 的方法，进行类型判断，给予唯一标示符，详见 <a href="http://www.planabc.net/2009/12/26/array_uniq/#comment-5779" target="_blank">评论 20 楼</a>。</p>
<p><strong>没有最好，只有最合适的方式</strong>，其实使用 Array.indexOf() 的思路也是不错的选择，对于已经支持的浏览器直接用原生的 Array.indexOf() 方法，对于未支持的，我们增加 Array.indexOf() 方法，如下：</p>
<pre><code>if(!Array.prototype.indexOf) {
    Array.prototype.indexOf = function (obj, fromIndex) {
        if (fromIndex == null) {
            fromIndex = 0;
        } else if (fromIndex < 0) {
            fromIndex = Math.max(0, this.length + fromIndex);
        }

        for (var i = fromIndex; i < this.length; i++) {
            if (this[i] === obj)
                return i;
            }
        return -1;
    };
}</code></pre>
<p>接下来，实现的过程就非常简单了。</p>
<p>对于使用 Array.indexOf() 方法实现方案的优化提示：找到相同值时，从数组中移除，以减少下次遍历的量。</p>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=338&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2009/12/26/array_uniq/feed/</wfw:commentRss>
		<slash:comments>33</slash:comments>
		</item>
		<item>
		<title>Firefox 的 Jetpack 扩展案例分析：Gmail 邮件提醒</title>
		<link>http://www.planabc.net/2009/10/14/jetpack_gmail_checker/</link>
		<comments>http://www.planabc.net/2009/10/14/jetpack_gmail_checker/#comments</comments>
		<pubDate>Wed, 14 Oct 2009 10:11:27 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[Firfox]]></category>
		<category><![CDATA[gmail]]></category>
		<category><![CDATA[Jetpack]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=317</guid>
		<description><![CDATA[Gtalk 软件的最下方有个很好又很实用的功能，就是 Gmail 邮件提醒功能。会定时更新你 Gmail 中未读新邮件的数量。 试想如果我们将此功能移植到 Firefox 上一定有趣！ 第一步，在状态栏中显示图标和数据。 通过 《如何创建 Firefox 的 Jetpack 扩展》 这篇文章，我们可以轻易的创建： jetpack.statusBar.append({ html: '&#60;img src="http://mail.google.com/mail/images/favicon.ico"/&#62;&#60;span id="count"&#62;&#60;/span&#62;', //Gmail邮件图标和未读新邮件数 width: 55, //状态栏上的宽度为55 onReady: function(widget) { $("#count", widget).css({ //给未读新邮件数添加样式 cursor: "pointer", paddingLeft:"4px", fontFamily: "Tahoma, Arial, sans-serif", verticalAlign: "top", fontSize: "10px", lineHeight:"18px", }); } }); 第二步，获取 Gmail 的数据，更新未读新邮件数。 可以通过 Gmail 邮件的 Feed 获得（需登录）：https://mail.google.com/mail/feed/atom [...... ]]></description>
			<content:encoded><![CDATA[<p>Gtalk 软件的最下方有个很好又很实用的功能，就是 Gmail 邮件提醒功能。会定时更新你 Gmail 中未读新邮件的数量。</p>
<p><img src="http://www.planabc.net/wp-content/uploads/2009/10/gmail01.png" alt="Gtalk" /></p>
<p>试想如果我们将此功能移植到 Firefox 上一定有趣！</p>
<p><strong>第一步，在状态栏中显示图标和数据。</strong></p>
<p>通过 <a href="http://www.planabc.net/2009/10/13/build_firefox_extensions_with_jetpack/" title="如何创建 Firefox 的 Jetpack 扩展" target="_blank">《如何创建 Firefox 的 Jetpack 扩展》</a> 这篇文章，我们可以轻易的创建：</p>
<pre><code>jetpack.statusBar.append({
    html: '&lt;img src="http://mail.google.com/mail/images/favicon.ico"/&gt;&lt;span id="count"&gt;&lt;/span&gt;', //Gmail邮件图标和未读新邮件数
    width: 55, //状态栏上的宽度为55
    onReady: function(widget) {
        $("#count", widget).css({ //给未读新邮件数添加样式
            cursor: "pointer",
            paddingLeft:"4px",
            fontFamily: "Tahoma, Arial, sans-serif",
            verticalAlign: "top",
            fontSize: "10px",
            lineHeight:"18px",
        });
    }
});</code></pre>
<p><strong>第二步，获取 Gmail 的数据，更新未读新邮件数。</strong></p>
<p>可以通过 Gmail 邮件的 Feed 获得（需登录）：<a href="https://mail.google.com/mail/feed/atom" target="_blank">https://mail.google.com/mail/feed/atom</a></p>
<p>Feed 源码中的 fullcount 标签是用来记录当前的未读新邮件数。</p>
<p>OK，首先数据源有了。接着，我们使用再熟悉不过的 Ajax 技术，获取到数据并赋给指定的元素。</p>
<pre><code>function update(widget) {
    var widget = $(widget)；

    $.get("https://mail.google.com/mail/feed/atom", function(xml) {
        var el = $(xml).find("fullcount"); // 记录未读新邮件数的节点
        if(el){
            var newcount = parseInt(el.get(0).textContent);
            widget.find("#count").text(newcount); //赋给指定的元素
        } else { //如果未登录，显示“Login”
            widget.find("#count").text( "Login" );
	}
    });
}</code></pre>
<p>我们还可以通过进行一些优化：比如当未读新邮件数大于原来的邮件数时，增加提示信息等。<br />
提示信息这里使用 <code>jetpack.notifications.show(options)</code> 方法，options 参数有三个属性：title (String)：通知的标题；icon (URL)：通知 icon 的 URL；body (String)：通知的主题内容。</p>
<p>优化后的代码如下：</p>
<pre><code>var count = 0;

function update(widget) {
    var widget = $(widget),
        notify = function(msg) { // 定义通知的公用方法
            jetpack.notifications.show({
                title: "Gmail",
                body: msg,
                icon: "http://mail.google.com/mail/images/favicon.ico"
            });
        };

    $.get("https://mail.google.com/mail/feed/atom", function(xml) {
        var el = $(xml).find("fullcount"); // 记录未读新邮件数的节点
        if(el){
            var newcount = parseInt(el.get(0).textContent);
            if(newcount > count) { // 如果未读新邮件数大于原来的邮件数，则提示来自哪里
                var sender = $(xml).find("name").get(0).textContent;
                notify("New message from "+sender);
            }
            count = newcount;
            widget.find("#count").text(count); //赋给指定的元素

        } else { //如果未登录，提示登录
            widget.find("#count").text( "Login" );
            notify("Please login to Gmail");
	}
    });
}</code></pre>
<p><strong>第三步：设置定时更新数据。</strong></p>
<p>我们设置每 1 分钟更新一次数据：</p>
<pre><code>setInterval( function() { update(widget) }, 60*1000 );</code></pre>
<p><strong>第四步：设置点击扩展后的链接窗口。</strong></p>
<pre><code>$(widget).click(function() { //设置点击扩展后的链接窗口
    jetpack.tabs.open("http://mail.google.com");
    jetpack.tabs[ jetpack.tabs.length-1 ].focus();
});</code></pre>
<p><code>jetpack.tabs</code> 为浏览器窗口的标签对象，<code>.open(url)</code> 为新打开浏览器窗口标签的方法，<code>.focus()</code>为选中此标签为当前标签的方法。</p>
<p>OK，Firefox 的 Jetpack 扩展——Gmail 邮件提醒，经过简单的四步轻松完成。</p>
<p>全部代码如下：</p>
<pre><code>var count = 0;

function update(widget) {
    var widget = $(widget),
        notify = function(msg) { // 定义通知的公用方法
            jetpack.notifications.show({
                title: "Gmail",
                body: msg,
                icon: "http://mail.google.com/mail/images/favicon.ico"
            });
        };

    $.get("https://mail.google.com/mail/feed/atom", function(xml) {
        var el = $(xml).find("fullcount"); // 记录未读新邮件数的节点
        if(el){
            var newcount = parseInt(el.get(0).textContent);
            if(newcount > count) { // 如果未读新邮件数大于原来的邮件数，则提示来自哪里
                var sender = $(xml).find("name").get(0).textContent;
                notify("New message from "+sender);
            }
            count = newcount;
            widget.find("#count").text(count); //赋给指定的元素

        } else { //如果未登录，提示登录
            widget.find("#count").text( "Login" );
            notify("Please login to Gmail");
	}
    });
}

jetpack.statusBar.append({
    html: '&lt;img src="http://mail.google.com/mail/images/favicon.ico"/&gt;&lt;span id="count"&gt;&lt;/span&gt;', //Gmail邮件图标和未读新邮件数
    width: 40, //状态栏上的宽度为40，预留3位数的宽度
    onReady: function(widget) {
        $("#count", widget).css({ //给未读新邮件数添加样式
            cursor: "pointer",
            paddingLeft:"4px",
            fontFamily: "Tahoma, Arial, sans-serif",
            verticalAlign: "top",
            fontSize: "10px",
            lineHeight:"18px",
        });

        $(widget).click(function() { //设置点击扩展后的链接窗口
            jetpack.tabs.open("http://mail.google.com");
            jetpack.tabs[ jetpack.tabs.length-1 ].focus();
        });

        update(widget);
	setInterval( function() {update(widget) }, 60*1000 );
    }
});</code></pre>
<p>测试Demo：<a href="http://www.planabc.net/lab/jetpack/gmail/" target="blank" title="Gmail 邮件提醒">http://www.planabc.net/lab/jetpack/gmail/</a></p>
<p>对于 Jetpack 详细的 API，可以阅读 about:jetpack 页面的 API Reference 标签部分。</p>
<p>案例源码来自：<a href="https://jetpack.mozillalabs.com/demos/gmail-checker.js" target="_blank">https://jetpack.mozillalabs.com/demos/gmail-checker.js</a></p>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=317&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2009/10/14/jetpack_gmail_checker/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>如何创建 Firefox 的 Jetpack 扩展</title>
		<link>http://www.planabc.net/2009/10/13/build_firefox_extensions_with_jetpack/</link>
		<comments>http://www.planabc.net/2009/10/13/build_firefox_extensions_with_jetpack/#comments</comments>
		<pubDate>Tue, 13 Oct 2009 09:31:41 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[Firfox]]></category>
		<category><![CDATA[Jetpack]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=304</guid>
		<description><![CDATA[Firefox 的 Jetpack 可以让我们很轻松地创建 Firefox 插件，仅通过已掌握的前端技能（HTML/CSS/JS），估计让人更兴奋的一点—— Jetpack 还集成了 jQuery 框架。 Adobe Air 也好，Web OS 也好， Jetpack 也好，至少让我们肯定一点，前端的价值将会在机遇中越来越大的被展现。 下面让我们一起来看看如何一步步创建你的第一个 Firefox 的 Jetpack 扩展： 第一步：安装 Jetpack 插件 Jetpack 插件地址：https://jetpack.mozillalabs.com/install.html 安装完成 Jetpack 插件后，你就可以通过在地址栏中输入 about:jetpack 来访问 Jetpack 本地界面了。 第二步：创建 planabc.js 文件。 planabc.js 的详细代码为： jetpack.statusBar.append({ html: '&#60;img src="http://www.planabc.net/favicon.ico"/&#62;', width: 16, onReady: function(widget){ $(widget).click(function(){ jetpack.tabs.focused.contentWindow.location = "http://www.planabc.net/"; }); } }); jetpack.statusBar.a... ]]></description>
			<content:encoded><![CDATA[<p>Firefox 的 <a href="https://jetpack.mozillalabs.com/index.html" title="Mozilla Labs Jetpack | Exploring new ways to extend and personalize the Web" target="_blank">Jetpack</a> 可以让我们很轻松地创建 Firefox 插件，仅通过已掌握的前端技能（HTML/CSS/JS），估计让人更兴奋的一点—— Jetpack 还集成了 jQuery 框架。</p>
<p>Adobe Air 也好，Web OS 也好， Jetpack 也好，至少让我们肯定一点，前端的价值将会在机遇中越来越大的被展现。</p>
<p>下面让我们一起来看看如何一步步创建你的<a href="http://www.planabc.net/lab/jetpack/planabc/" target="_blank" title=" Firefox 的 Jetpack 扩展">第一个 Firefox 的 Jetpack 扩展</a>：</p>
<p><strong>第一步：安装 Jetpack 插件</strong></p>
<p>Jetpack 插件地址：<a href="https://jetpack.mozillalabs.com/install.html" target="_blank" title="Jetpack ">https://jetpack.mozillalabs.com/install.html</a></p>
<p>安装完成 Jetpack 插件后，你就可以通过在地址栏中输入 <code>about:jetpack</code> 来访问 Jetpack 本地界面了。</p>
<p><img src="http://www.planabc.net/wp-content/uploads/2009/10/jetpack01.png" alt="about:jetpack" title="about:jetpack" /></p>
<p><strong>第二步：创建 planabc.js 文件。</strong></p>
<p><a href="http://www.planabc.net/lab/jetpack/planabc/planabc.js" target="_blank" title="planabc.js">planabc.js</a> 的详细代码为：</p>
<pre><code>jetpack.statusBar.append({
    html: '&lt;img src="http://www.planabc.net/favicon.ico"/&gt;',
    width: 16,
    onReady: function(widget){
        $(widget).click(function(){
            jetpack.tabs.focused.contentWindow.location = "http://www.planabc.net/";
        });
    }
});</code></pre>
<p><code>jetpack.statusBar.append</code> 将执行 JavaScript 对象（该JavaScript 对象有四个属性：html、url、width 和 onReady）。</p>
<ul>
<li>html 属性：定义初始的 HTML，将显示在状态栏。此样例中，将显示一个简单的 IMG 元素。</li>
<li>url 属性：定义将在状态栏上显示的外部 HTML 内容的 URL。此样例中，未使用该属性。</li>
<li>width 属性：定义内容在状态栏上的宽度（单位：像素）。此样例中，定义为 16 像素，也就是  IMG 元素本身的宽度。</li>
<li>onReady 属性：定义被调用的函数（一旦状态栏被创建，此函数将会被调用）。由于 Jetpack 集成了 jQuery 框架，所以可以直接使用 jQuery 的属性和方法。在此样例中定义了这样一个函数，当点击该 Jetpack 扩展时，我们将修改 <code>jetpack.tabs.focused.contentWindow.location</code> 属性为 <code>http://www.planabc.net/</code>。<code>jetpack.tabs.focused.contentWindow</code> 对象相当于 window 对象，你可以访问通过 JavaScript 访问网页。</li>
</ul>
<p><strong>第三步：测试 planabc.js 代码。</strong></p>
<p>通过在地址栏中输入 <code>about:jetpack</code> 访问到 Jetpack 本地界面，点击 Develop 标签，然后将 planabc.js 文件中的代码拷贝到页面上的输入框中。点击 “try out this code” 按钮，你将会看到一个新的 icon 出现在 Firefox 窗口的右下角。</p>
<p>Develop 非常方便在代码部署前，在 Firefox 中测试你的代码。</p>
<p><img src="http://www.planabc.net/wp-content/uploads/2009/10/jetpack02.png" alt="Develop" title="Develop" /></p>
<p><strong>第四步：部署 Jetpack 扩展。</strong></p>
<p>创建一个简单的页面，在页面的 HEAD 元素内添加如下的 LINK 元素：</p>
<pre><code>&lt;link rel="jetpack" href="planabc.js" name="怿飞的博客"/&gt;</code></pre>
<p><a href="http://www.planabc.net/lab/jetpack/planabc/index.html" target="_blank" >HTML 文件</a>源码如下：</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&gt;
        &lt;title&gt;Mozilla Jetpack 案例&lt;/title&gt;
        &lt;link rel="jetpack" href="planabc.js" name="怿飞的博客"/&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;a href="http://www.planabc.net/2009/10/13/build_firefox_extensions_with_jetpack/"&gt;《如何创建 Firefox 的 Jetpack 扩展》 &lt;/a&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>最后，将 HTML 文件和 planabc.js 文件都上传到服务器。</p>
<p><strong>第五步：安装 Jetpack 扩展。</strong></p>
<p>在 Firefox 中浏览该 HTML 页面时，Jetpack 插件将会在屏幕的最上方显示一个安装条，点击 “install” 按钮。将会提示安装不受信任的特性，点击 “I know what I&#8217;m doing. Install It!” 按钮。</p>
<p>安装完成和第三步测试一样，将会看到一个新的 icon 出现在 Firefox 窗口的右下角。如果此时你发现状态栏有2个相同的新 icon，那意味着原先的测试代码依然还在 about:jetpack 页面的 Develop 标签页中，清空输入框的代码即可消除。</p>
<p>返回到 about:jetpack 页面，点击 Installed Features 标签，将在列表中显示有新安装的 Jetpack 扩展。</p>
<p><strong>测试Demo：</strong><a href="http://www.planabc.net/lab/jetpack/planabc/" target="_blank" title=" Firefox 的 Jetpack 扩展">http://www.planabc.net/lab/jetpack/planabc/</a></p>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=304&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2009/10/13/build_firefox_extensions_with_jetpack/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>判断 iframe 是否加载完成的完美方法</title>
		<link>http://www.planabc.net/2009/09/22/iframe_onload/</link>
		<comments>http://www.planabc.net/2009/09/22/iframe_onload/#comments</comments>
		<pubDate>Tue, 22 Sep 2009 03:06:38 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[iframe]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[load]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=297</guid>
		<description><![CDATA[一般来说，我们判断 iframe 是否加载完成其实与 判断 JavaScript 文件是否加载完成 采用的方法很类似： var iframe = document.createElement("iframe"); iframe.src = "http://www.planabc.net"; if (!/*@cc_on!@*/0) { //if not IE iframe.onload = function(){ alert("Local iframe is now loaded."); }; } else { iframe.onreadystatechange = function(){ if (iframe.readyState == "complete"){ alert("Local iframe is now loaded."); } }; } document.body.appendChild(iframe); 最近， Nicholas C. Zakas 文章《Iframes, onload, and [...... ]]></description>
			<content:encoded><![CDATA[<p>一般来说，我们判断 iframe 是否加载完成其实与 <a href="http://www.planabc.net/2008/10/31/javascript_ready_onload/" title="如何判断脚本加载完成 " target="_blank">判断 JavaScript 文件是否加载完成</a> 采用的方法很类似：</p>
<pre><code>var iframe = document.createElement("iframe");
iframe.src = "http://www.planabc.net";

if (!/*@cc_on!@*/0) { //if not IE
    iframe.onload = function(){
        alert("Local iframe is now loaded.");
    };
} else {
    iframe.onreadystatechange = function(){
        if (iframe.readyState == "complete"){
            alert("Local iframe is now loaded.");
        }
    };
}

document.body.appendChild(iframe);</code></pre>
<p>最近， <a href="http://www.nczonline.net/" title="NCZOnline" target="_blank">Nicholas C. Zakas</a> 文章<a href="http://www.nczonline.net/blog/2009/09/15/iframes-onload-and-documentdomain/" title="Iframes, onload, and document.domain" target="_blank">《Iframes, onload, and document.domain》</a>的评论中 Christopher 提供了一个新的判断方法（很完美）：</p>
<pre><code>var iframe = document.createElement("iframe");
iframe.src = "http://www.planabc.net";

if (iframe.attachEvent){
    iframe.attachEvent("onload", function(){
        alert("Local iframe is now loaded.");
    });
} else {
    iframe.onload = function(){
        alert("Local iframe is now loaded.");
    };
}

document.body.appendChild(iframe);</code></pre>
<p>几点补充说明：</p>
<ol>
<li><a href="http://msdn.microsoft.com/en-us/library/cc197055(VS.85).aspx" target="_blank" title="onload Event (BODY, FRAME, FRAMESET, ...)<br />
">IE 支持 iframe 的 onload 事件</a>，不过是隐形的，需要通过 attachEvent 来注册。</li>
<li>第二种方法比第一种方法更完美，因为 readystatechange 事件相对于 load 事件有一些潜在的问题。</li>
</ol>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=297&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2009/09/22/iframe_onload/feed/</wfw:commentRss>
		<slash:comments>29</slash:comments>
		</item>
		<item>
		<title>YUI Compressor 组件压缩 JavaScript 的一些原理</title>
		<link>http://www.planabc.net/2009/08/02/javascript-compression_with_yui_compressor/</link>
		<comments>http://www.planabc.net/2009/08/02/javascript-compression_with_yui_compressor/#comments</comments>
		<pubDate>Sun, 02 Aug 2009 08:40:39 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[compressor]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[YUI]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=287</guid>
		<description><![CDATA[YUI Compressor 压缩 JavaScript 的内容包括： 移除注释 移除额外的空格 细微优化 标识符替换（Identifier Replacement） YUI Compressor 包括哪些细微优化呢？ object["property"] ，如果属性名是合法的 JavaScript 标识符（注：合法的 JavaScript 标识符——由一个字母开头，其后选择性地加上一个或者多个字母、数字或下划线）且不是保留字，将优化为： object.property {"property":123} ，如果属性名是合法的 JavaScript 标识符且不是保留字，将优化为 {property:123} （注：在对象字面量中，如果属性名是一个合法的 JavaScript 标识符且不是保留字，并不强制要求用引号引住属性名）。 'abcd\'efgh'，将优化为 "abcd'efgh"。 "abcd" + "efgh"，如果是字符串相连接，将优化成 "abcdefgh"（注：所有在使用 YUI Compressor 的前提下，对于脚本中的字符串连接，使用连接符 &#8220;+&#8221; 的效率和可维护性最高）... ]]></description>
			<content:encoded><![CDATA[<p><a href="http://developer.yahoo.com/yui/compressor/" target="_blank" title="YUI Compressor">YUI Compressor</a> 压缩 JavaScript 的内容包括：</p>
<ol>
<li>移除注释</li>
<li>移除额外的空格</li>
<li>细微优化</li>
<li>标识符替换（Identifier Replacement）</li>
</ol>
<p><a href="http://developer.yahoo.com/yui/compressor/" target="_blank" title="YUI Compressor">YUI Compressor</a> 包括哪些细微优化呢？</p>
<ul>
<li><code>object["property"]</code> ，如果属性名是合法的 JavaScript 标识符（注：合法的 JavaScript 标识符——由一个字母开头，其后选择性地加上一个或者多个字母、数字或下划线）且不是保留字，将优化为： <code>object.property</code></li>
<li><code>{"property":123} </code>，如果属性名是合法的 JavaScript 标识符且不是保留字，将优化为 <code>{property:123}</code> （注：在对象字面量中，如果属性名是一个合法的 JavaScript 标识符且不是保留字，并不强制要求用引号引住属性名）。</li>
<li><code>'abcd\'efgh'</code>，将优化为 <code>"abcd'efgh"</code>。</li>
<li><code>"abcd" + "efgh"</code>，如果是字符串相连接，将优化成 <code>"abcdefgh"</code>（注：所有在使用 YUI Compressor 的前提下，对于脚本中的字符串连接，使用连接符 &#8220;+&#8221; 的效率和可维护性最高）。</li>
</ul>
<p>对于 JavaScript 最有效的压缩优化，当属<strong>标识符替换</strong>。</p>
<p>比如：</p>
<pre><code>(function(){
    function add(num1, num2) {
        return num1 + num2;
    }
})();</code></pre>
<p>进行属标识符替换后：</p>
<pre><code>(function(){
    function A(C, B) {
        return C+ B;
    }
})();</code></pre>
<p>再移除额外的空格，最终成了：</p>
<pre><code>(function(){function A(C,B){return C+B;}})();</code></pre>
<p><a href="http://developer.yahoo.com/yui/compressor/" target="_blank" title="YUI Compressor">YUI Compressor</a> 标识符替换仅替换<strong>函数名和变量名</strong>，那哪些不能被替代呢？</p>
<ol>
<li>原始值：字符串、布尔值、数字、null 和 undefined。一般来说字符串占的空间最多，而非数字字面量其次（true、false，null，underfinded）。</li>
<li>全局变量：window、document、XMLHttpRequest等等。使用最多的就是 document、window。</li>
<li>属性名，比如：foo.bar。占据的空间仅次于字符串，&#8221;.&#8221; 操作符无法被代替，且 a.b.c 更加费空间。</li>
<li>关键字。经常被过度使用的关键字有：var、return。最好的优化方法：一个函数仅出现一次 var 和 return 关键字。</li>
</ol>
<p>对于原始值、全局变量、属性名的优化处理方式大致相同：<strong>任何字面量值、全局变量或者属性名被使用超过 2 次（包括2次），都应该用局部变量存储代替。</strong></p>
<p>但有部分情况下是禁止使用标识符替换的：</p>
<ol>
<li>使用 eval() 函数。解决方法：不使用或者创建一个全局函数封装 eval()。</li>
<li>使用 with 语句。解决方法：方法同上。</li>
<li>JScript 的条件注释。唯一解决的方法：不使用。</li>
</ol>
<p>由于 <a href="http://developer.yahoo.com/yui/compressor/" target="_blank" title="YUI Compressor">YUI Compressor</a> 是建立在 <a href="http://www.mozilla.org/rhino/" target="_blank" title="Rhino: JavaScript for Java">rhino interpreter</a> 基础上的，所以上述所有的优化都是安全的。</p>
<p>扩展阅读：</p>
<ul>
<li><a href="http://www.slideshare.net/nzakas/extreme-javascript-compression-with-yui-compressor" target="_blank" title="Extreme JavaScript Compression With YUI Compressor">《Extreme JavaScript Compression With YUI Compressor》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=287&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2009/08/02/javascript-compression_with_yui_compressor/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>模拟兼容性的 addDOMLoadEvent 事件</title>
		<link>http://www.planabc.net/2009/07/30/adddomloadevent/</link>
		<comments>http://www.planabc.net/2009/07/30/adddomloadevent/#comments</comments>
		<pubDate>Thu, 30 Jul 2009 06:46:49 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[event]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=268</guid>
		<description><![CDATA[由于 window.onload 事件需要在页面所有内容（包括图片等）加载完后，才执行，但往往我们更希望在 DOM 一加载完就执行脚本。其实在现在大部分主流浏览器上（Firefox 3+，Opera 9+，Safari 3+，Chrome 2+）都提供了这一事件方法：addDOMLoadEvent。 document.addEventListener("DOMContentLoaded", init, false); 那对于 IE 我们如何模拟 addDOMLoadEvent 事件呢？ Matthias Miller 最早提供了如下的解决方案： // for Internet Explorer (using conditional comments) /*@cc_on @*/ /*@if (@_win32) document.write(&#34;&#60;script id=__ie_onload defer src=javascript:void(0)&#62;&#60;\/script&#62;&#34;); var script = document.getElementById("__ie_onload"); script.onreadystatechange = function() { if (this.readyState == "complete") { init(); // call the onload handler } [...... ]]></description>
			<content:encoded><![CDATA[<p>由于 <code>window.onload</code> 事件需要在页面所有内容（包括图片等）加载完后，才执行，但往往我们更希望在 DOM 一加载完就执行脚本。其实在现在大部分主流浏览器上（Firefox 3+，Opera 9+，Safari 3+，Chrome 2+）都提供了这一事件方法：<code>addDOMLoadEvent</code>。</p>
<pre><code>document.addEventListener("DOMContentLoaded", init, false);</code></pre>
<p>那对于 IE 我们如何模拟 addDOMLoadEvent 事件呢？</p>
<p>Matthias Miller 最早提供了如下的解决方案：</p>
<pre><code>// for Internet Explorer (using conditional comments)
/*@cc_on @*/
/*@if (@_win32)
document.write(&quot;&lt;script id=__ie_onload defer src=javascript:void(0)&gt;&lt;\/script&gt;&quot;);
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
    if (this.readyState == "complete") {
        init(); // call the onload handler
    }
};
/*@end @*/</code></pre>
<p>而 <a href="http://javascript.nwbox.com/" title="Javascript @ NWBOX" target="_blank">Diego Perini</a> 在其后提供了一种利用 <code>doScroll() </code>方法来模拟 addDOMLoadEvent 事件的方案，且现在主流的 JavaScript 框架（JQuery、YUI等）基本都采用的这一解决方案。</p>
<p>原理基本如下：</p>
<blockquote><p>当 ondocumentready 事件触发，文档（ document ）已经完全解析和建立。如果组件需要操作最初的文档结构，初始化代码需被安置在这之后。ondocumentready 事件告知组件，整个页面已被加载，且在 初始文档的 onload 事件触发之前立即触发。</p>
<p>一些方法，例如 doScroll，要求最初的文档被完全加载。如果这些方法是初始化函数的一部分，当ondocumentready 事件触发，他们将被执行。</p></blockquote>
<pre><code>/*
 *
 * IEContentLoaded.js
 *
 * Author: Diego Perini (diego.perini at gmail.com) NWBOX S.r.l.
 * Summary: DOMContentLoaded emulation for IE browsers
 * Updated: 05/10/2007
 * License: GPL/CC
 * Version: TBD
 *
 */

// @w    window reference
// @fn    function reference
function IEContentLoaded (w, fn) {
    var d = w.document, done = false,
    // only fire once
    init = function () {
        if (!done) {
            done = true;
            fn();
        }
    };
    // polling for no errors
    (function () {
        try {
            // throws errors until after ondocumentready
            d.documentElement.doScroll('left');
        } catch (e) {
            setTimeout(arguments.callee, 50);
            return;
        }
        // no errors, fire
        init();
    })();
    // trying to always fire before onload
    d.onreadystatechange = function() {
        if (d.readyState == 'complete') {
            d.onreadystatechange = null;
            init();
        }
    };
}</code></pre>
<p>JQuery 1.3.2 中源码实现如下：</p>
<pre><code>// If IE and not an iframe
// continually check to see if the document is ready
if ( document.documentElement.doScroll &#038;&#038; window == window.top ) (function(){
    if ( jQuery.isReady ) return;

    try {
        // If IE is used, use the trick by Diego Perini
        // http://javascript.nwbox.com/IEContentLoaded/
        document.documentElement.doScroll("left");
    } catch( error ) {
        setTimeout( arguments.callee, 0 );
        return;
    }

    // and execute any waiting functions
    jQuery.ready();
})();</code></pre>
<p>YUI 2.7.0 中源码实现如下：</p>
<pre><code>if (EU.isIE) {

    // Process onAvailable/onContentReady items when the
    // DOM is ready.
    YAHOO.util.Event.onDOMReady(
            YAHOO.util.Event._tryPreloadAttach,
            YAHOO.util.Event, true);

    var n = document.createElement('p');  

    EU._dri = setInterval(function() {
        try {
            // throws an error if doc is not ready
            n.doScroll('left');
            clearInterval(EU._dri);
            EU._dri = null;
            EU._ready();
            n = null;
        } catch (ex) {
        }
    }, EU.POLL_INTERVAL); 

}</code></pre>
<p>另外对于版本小于 Safari 3+ 的 Safari 浏览器，<a href="http://ejohn.org/" title="John Resig" target="_blank">John Resig</a> 也提供了一个解决方案：</p>
<pre><code>if (/WebKit/i.test(navigator.userAgent)) { // sniff
    var _timer = setInterval(function() {
        if (/loaded|complete/.test(document.readyState)) {
            clearInterval(_timer);
            init(); // call the onload handler
        }
    }, 10);
}</code></pre>
<p>怿飞提示：</p>
<ol>
<li>如果脚本是动态注入到页面上，则原生的 DOMContentReady 事件是不会被触发（即：IE 除外）。</li>
<li>IE 下对于在 iframe 里的使用 addDOMLoadEvent 事件，需做处理和慎用（这一点 YUI 做得不如 JQuery 细致）。
<pre><code>// form JQuery 1.3.2
// ensure firing before onload, maybe late but safe also for iframes
document.attachEvent("onreadystatechange", function(){
    if ( document.readyState === "complete" ) {
        document.detachEvent( "onreadystatechange", arguments.callee );
        jQuery.ready();
    }
});</code></pre>
</li>
</ol>
<p>扩展阅读：</p>
<ul>
<li><a href="http://www.thefutureoftheweb.com/blog/adddomloadevent" title="addDOMLoadEvent" target="_blank">《addDOMLoadEvent》</a></li>
<li><a href="http://dean.edwards.name/weblog/2006/06/again/" title="window.onload (again)" target="_blank">《window.onload (again)》</a></li>
<li><a href="http://javascript.nwbox.com/IEContentLoaded/" title="IEContentLoaded" target="_blank">《IEContentLoaded》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=268&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2009/07/30/adddomloadevent/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>JavaScript 获取事件对象的一个注意点</title>
		<link>http://www.planabc.net/2009/07/24/tips_for_getting_event_in_javascript/</link>
		<comments>http://www.planabc.net/2009/07/24/tips_for_getting_event_in_javascript/#comments</comments>
		<pubDate>Fri, 24 Jul 2009 01:59:17 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=258</guid>
		<description><![CDATA[平时我们获取事件对象一般写法如下： function getEvent(event) { return event &#124;&#124; window.event // IE:window.event } 如果没有参数，也可写成（非IE ：事件对象会自动传递给对应的事件处理函数，且为第一个参数）： function getEvent() { return arguments[0] &#124;&#124; window.event // IE:window.event } 这样的写法在除 Firefox（测试版本：3.0.12，下同） 外的浏览器上运行都不会有问题，但 Firefox 为什么例外呢？让我们这样一种情形： &#60;button id="btn" onclick="foo()"&#62;按钮&#60;/button&#62; &#60;script&#62; function foo(){ var e = getEvent(); alert(e); } &#60;/script&#62; 运行结果在 Firefox 中是 undefined，为什么呢？ 在 Firefox 中调用其实是这样的，先调用执行的是： function onclick(event) { foo(); } 然后调用执行的是： function [...... ]]></description>
			<content:encoded><![CDATA[<p>平时我们获取事件对象一般写法如下：</p>
<pre><code>function getEvent(event) {
    return event || window.event  // IE:window.event
}</code></pre>
<p>如果没有参数，也可写成（非IE ：事件对象会自动传递给对应的事件处理函数，且为第一个参数）：</p>
<pre><code>function getEvent() {
    return arguments[0] || window.event // IE:window.event
}</code></pre>
<p>这样的写法在除 Firefox（测试版本：3.0.12，下同） 外的浏览器上运行都不会有问题，但 Firefox 为什么例外呢？让我们这样一种情形：</p>
<pre><code>&lt;button id="btn" onclick="foo()"&gt;按钮&lt;/button&gt;

&lt;script&gt;
function foo(){
   var e =  getEvent();
   alert(e);
}
&lt;/script&gt;</code></pre>
<p>运行结果在 Firefox 中是 <code>undefined</code>，为什么呢？</p>
<p>在 Firefox 中调用其实是这样的，先调用执行的是：</p>
<pre><code>function onclick(event) {
    foo();
}</code></pre>
<p>然后调用执行的是：</p>
<pre><code>function foo(){
   var e =  getEvent();
   alert(e);
}</code></pre>
<p>会发现在  Firefox 下 <code>onclick="foo()"</code> 中的 <code>foo()</code> 无法自动传入事件对象参数，而默认传递给了系统生成的 onclick 函数，那本例我们可以通过 <code>getEvent.caller.caller.arguments[0]</code> 获得事件对象。</p>
<p>因此，我们的 getEvent 可以优化成（参照 yui_2.7.0b 中的 event/event-debug.js 中 getEvent 方法）：</p>
<pre><code>function getEvent(event) {
    var ev = event || window.event;

    if (!ev) {
        var c = this.getEvent.caller;
        while (c) {
            ev = c.arguments[0];
            if (ev &#038;&#038; (Event == ev.constructor || MouseEvent  == ev.constructor)) { //怿飞注：YUI 源码 BUG，ev.constructor 也可能是 MouseEvent，不一定是 Event
                break;
            }
            c = c.caller;
        }
    }

    return ev;
}</code></pre>
<p>当然还有一个很简单的解决方法，就是手动将参数传递给 <code>onclick="foo()"</code>：</p>
<pre><code>&lt;button id="btn" onclick="foo(event)"&gt;按钮&lt;/button&gt;</code></pre>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=258&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2009/07/24/tips_for_getting_event_in_javascript/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>JavaScript 实现 Konami Code</title>
		<link>http://www.planabc.net/2009/05/21/how_to_implement_konami_code_with_javascript/</link>
		<comments>http://www.planabc.net/2009/05/21/how_to_implement_konami_code_with_javascript/#comments</comments>
		<pubDate>Thu, 21 May 2009 09:21:24 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[Konami]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=245</guid>
		<description><![CDATA[在 Facebook 上有一个彩蛋： 登录 facebook.com ，点击你首页的任何地方，键盘输入 Up, Up, Down, Down, Left, Right, Left, Right, B, A, Enter 后，再点击页面或滚动一下滚动条，你会发现特殊的变化（如下图），嘿嘿 ^^ 玩过“魂斗罗”的朋友，肯定一眼就能看出输入的字符原来就是“魂斗罗”中的“秘技”。其实“秘技”的术语叫 Konami Code，详细见图示： 那如何用 JavaScript 也在自己的页面上添加一个类似的彩蛋呢？ Abhi 在 《Konami Code on Facebook : How to implement it on your site》 一文中提供了大体思路： var $ = { enabled: false, tmp: Array(), _konamiCode: Array(65,66,39,37,39,37,40,40,38,38), init: function() { this.tmp = [...... ]]></description>
			<content:encoded><![CDATA[<p>在 Facebook 上有一个彩蛋：</p>
<p>登录 <a href="http://www.facebook.com" title="Facebook"  target="_blank">facebook.com</a> ，点击你首页的任何地方，键盘输入 Up, Up, Down, Down, Left, Right, Left, Right, B, A, Enter 后，再点击页面或滚动一下滚动条，你会发现特殊的变化（如下图），嘿嘿 ^^</p>
<p><img src="http://www.planabc.net/wp-content/uploads/2009/05/facebook.jpg" alt="Facebook 上有一个彩蛋" /></p>
<p>玩过“魂斗罗”的朋友，肯定一眼就能看出输入的字符原来就是“魂斗罗”中的“秘技”。其实“秘技”的术语叫 <a href="http://en.wikipedia.org/wiki/Konami_Code" title="Konami Code"  target="_blank">Konami Code</a>，详细见图示：</p>
<p><img src="http://www.planabc.net/wp-content/uploads/2009/05/konami.png" alt="Konami Code" /></p>
<p>那如何用 JavaScript 也在自己的页面上添加一个类似的彩蛋呢？</p>
<p><a href="http://abhinavsingh.com/blog/" title="Abhi’s Weblog - PHP, Apache, MySQL, XMPP and Web Development"  target="_blank">Abhi</a> 在 <a href="http://abhinavsingh.com/blog/2009/05/konami-code-on-facebook-how-to-implement-it-on-your-site/" title="Konami Code on Facebook : How to implement it on your site"  target="_blank">《Konami Code on Facebook : How to implement it on your site》</a> 一文中提供了大体思路：</p>
<pre><code>var $ = {
    enabled: false,
    tmp: Array(),
    _konamiCode: Array(65,66,39,37,39,37,40,40,38,38),
    init: function() {
        this.tmp = Array(65,66,39,37,39,37,40,40,38,38);
    },
    konamiCode: function(e) {
        if(!this.enabled) {
            var t = this.tmp.pop();
            if((e.keyCode-t) == 0) {
                if(this.tmp.length == 0) {
                    this.enabled = true;
                }
            } else {
                this.init();
            }
        } else {
            this.action();
        }
    },
    // Change the action() function to whatever you want to
    action: function() {
        //alert("Konami Code Activated");
    }
}</code></pre>
<p>然后在 load 的时候调用 $.init() 方法，在 keydown 的时候调用 $.konamiCode(event) 方法。</p>
<p>不过 <a href="http://abhinavsingh.com/blog/" title="Abhi’s Weblog - PHP, Apache, MySQL, XMPP and Web Development"  target="_blank">Abhi</a> 的方法还是冗余了点，<a href="http://www.unwrongest.com/" title="Jan Jarfalk" target="_blank">Jan Jarfalk</a> 在留言中提供了一个短小精悍的代码：</p>
<pre><code>// Tweetable Konami code
var k=[];
function(e){
    k.push(e.keyCode);
    if(k.toString().indexOf("38,38,40,40,37,39,37,39,66,65")>=0) {
        //alert("Konami Code Activated");
    }
}</code></pre>
<p>PS：有兴趣的还可以在 <a href="https://www.google.com/reader/view/" target="_blank" title="Google Reader">Google Reader</a> 上尝试下此“秘技”，一般人我还不告诉他，嘿嘿。</p>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=245&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2009/05/21/how_to_implement_konami_code_with_javascript/feed/</wfw:commentRss>
		<slash:comments>31</slash:comments>
		</item>
		<item>
		<title>影响 reflow 的因素及其优化</title>
		<link>http://www.planabc.net/2009/04/13/reflow/</link>
		<comments>http://www.planabc.net/2009/04/13/reflow/#comments</comments>
		<pubDate>Mon, 13 Apr 2009 10:25:09 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[reflow]]></category>
		<category><![CDATA[优化]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=227</guid>
		<description><![CDATA[如果对于 reflow 这个概念你还不太清楚或者不知道，请先阅读： 《Notes on HTML Reflow》 《What is a reflow?》 《Gecko:Reflow Refactoring》 《reflow》 《形象化的reflow》 《样式的执行效率-reflow》 Yahoo! 性能工程师 Nicole Sullivan 在最新的文章 《Reflows &#38; Repaints: CSS Performance making your JavaScript slow?》 中总结了导致 reflow 发生的一些因素： 调整窗口大小（Resizing the window） 改变字体（Changing the font） 增加或者移除样式表（Adding or removing a stylesheet） 内容变化，比如用户在input框中输入文字（Content changes, such as a user typing text in an input [...... ]]></description>
			<content:encoded><![CDATA[<p>如果对于 <a title="Notes on HTML Reflow" href="http://www.mozilla.org/newlayout/doc/reflow.html" target="_blank">reflow</a> 这个概念你还不太清楚或者不知道，请先阅读：</p>
<ul>
<li><a title="Notes on HTML Reflow" href="http://www.mozilla.org/newlayout/doc/reflow.html" target="_blank">《Notes on HTML Reflow》</a></li>
<li><a title="What is a reflow?" href="http://dougt.wordpress.com/2008/05/24/what-is-a-reflow/" target="_blank">《What is a reflow?》</a></li>
<li><a title="Gecko:Reflow Refactoring" href="https://wiki.mozilla.org/Gecko:Reflow_Refactoring" target="_blank">《Gecko:Reflow Refactoring》</a></li>
<li><a title="reflow" href="http://realazy.org/blog/2007/09/09/reflow/" target="_blank">《reflow》</a></li>
<li><a title="形象化的reflow" href="http://www.aoao.org.cn/blog/2008/05/reflow/" target="_blank">《形象化的reflow》</a></li>
<li><a title="样式的执行效率-reflow" href="http://www.missormiss.com/reflow.html" target="_blank">《样式的执行效率-reflow》</a></li>
</ul>
<p>Yahoo! 性能工程师 <a title="http://www.stubbornella.org/content/" href="http://www.stubbornella.org/content/" target="_blank">Nicole Sullivan</a> 在最新的文章 <a title="Reflows &amp; Repaints: CSS Performance making your JavaScript slow?" href="http://www.stubbornella.org/content/2009/03/27/reflows-repaints-css-performance-making-your-javascript-slow/" target="_blank">《Reflows &amp; Repaints: CSS Performance making your JavaScript slow?》</a> 中总结了导致 reflow 发生的一些因素：</p>
<ol>
<li>调整窗口大小（Resizing the window）</li>
<li>改变字体（Changing the font）</li>
<li>增加或者移除样式表（Adding or removing a stylesheet）</li>
<li>内容变化，比如用户在input框中输入文字（Content changes, such as a user typing text in<br />
an input box）</li>
<li>激活 CSS 伪类，比如 :hover (IE 中为兄弟结点伪类的激活)（Activation of CSS pseudo classes such as :hover (in IE the activation of the pseudo class of a sibling)）</li>
<li>操作 class 属性（Manipulating the class attribute）</li>
<li>脚本操作 DOM（A script manipulating the DOM）</li>
<li>计算 offsetWidth  和 offsetHeight 属性（Calculating offsetWidth and offsetHeight）</li>
<li>设置 style 属性的值 （Setting a property of the style attribute）</li>
</ol>
<p><a title="Notes on HTML Reflow" href="http://www.mozilla.org/newlayout/doc/reflow.html" target="_blank">reflow</a> 会引起开销，影响页面的性能，那如何才能做到合理的优化呢？<a title="http://www.stubbornella.org/content/" href="http://www.stubbornella.org/content/" target="_blank">Nicole Sullivan</a> 也提供了部分建议：</p>
<ol>
<li>如果想设定元素的样式，通过改变元素的 class 名 (尽可能在 DOM 树的最里层)（Change classes on the element you wish to style (as low in the dom tree as possible)）</li>
<li>避免设置多项内联样式（Avoid setting multiple inline styles）</li>
<li>应用元素的动画，使用 position 属性的 fixed 值或 absolute 值（Apply animations to elements that are position fixed or absolute）</li>
<li>权衡平滑和速度（Trade smoothness for speed）</li>
<li>避免使用 table 布局（Avoid tables for layout）</li>
<li>避免使用CSS的 JavaScript 表达式 (仅 IE 浏览器)（Avoid JavaScript expressions in the CSS (IE only)）</li>
</ol>
<p>详细阅读：</p>
<ul>
<li><a title="Reflows &amp; Repaints: CSS Performance making your JavaScript slow?" href="http://www.stubbornella.org/content/2009/03/27/reflows-repaints-css-performance-making-your-javascript-slow/" target="_blank">《Reflows &amp; Repaints: CSS Performance making your JavaScript slow?》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=227&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2009/04/13/reflow/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>
