<?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, 08 Nov 2011 08:30:31 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>哥俩好：oninput &amp; onpropertychange</title>
		<link>http://www.planabc.net/2011/10/18/oninput_and_onpropertychange/</link>
		<comments>http://www.planabc.net/2011/10/18/oninput_and_onpropertychange/#comments</comments>
		<pubDate>Tue, 18 Oct 2011 05:09:12 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[input]]></category>
		<category><![CDATA[oninput]]></category>
		<category><![CDATA[onpropertychange]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=712</guid>
		<description><![CDATA[传统对于文本框（input）的输入可通过键盘的 onkeydown / onkeypress / onkeyup 来监测，但在处理较多细节时存在诟病比如： cut（剪切） / paste（复制） / undo（撤销） / redo（重做） / drag &#038; drop（拖拽）/ 输入法等。 而 oninput &#038; onpropertychange 事件基本可以解决上面的诟病： oninput 事件作为 HTML5 中的标准事件，基本除了IE6 / IE7 / IE8 外的最新浏览器均支持（注：1、原先的 Opera 的虽支持，但依然存在部分传统的诟病，从 Opera 11+ 开始，已修复，更加完美；2、IE9 也支持）。 function(input, callback){ if ("onpropertychange" in input) { //IE6/IE7/IE8 input.onpropertychange = function(){ if (window.event.propertyName == "value"){ callback.call(this, [...... ]]></description>
			<content:encoded><![CDATA[<p>传统对于文本框（input）的输入可通过键盘的 onkeydown / onkeypress / onkeyup 来监测，但在处理较多细节时存在诟病比如： cut（剪切） / paste（复制） / undo（撤销） / redo（重做） / drag &#038; drop（拖拽）/ 输入法等。</p>
<p>而 oninput &#038; onpropertychange 事件基本可以解决上面的诟病：</p>
<p><a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#handler-oninput" title="oninput" target="_blank">oninput</a> 事件作为 HTML5 中的标准事件，基本除了IE6 / IE7 / IE8 外的最新浏览器均支持（注：1、原先的 Opera 的虽支持，但依然存在部分传统的诟病，从 Opera 11+ 开始，已修复，更加完美；2、IE9 也支持）。</p>
<pre><code>function(input, callback){
    if ("onpropertychange" in input) { //IE6/IE7/IE8
        input.onpropertychange = function(){
            if (window.event.propertyName == "value"){
                callback.call(this, window.event)
            }
        }
    } else {
        // Fix Firefox Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=195696
        input.addEventListener("input", callback, false);
    }
}</code></pre>
<p>提示：</p>
<ol>
<li>oninput 事件：当 JS 改变 value 值或从浏览器的自动下拉提示中选值时，不会触发。</li>
<li>onpropertychange 事件：当 input 设置为不可用（disable=true）时，不会触发。</li>
</ol>
<p>参考文章：</p>
<ul>
<li><a href="http://whattheheadsaid.com/2010/09/effectively-detecting-user-input-in-javascript" title="Effectively detecting user input in JavaScript" target="_blank">《Effectively detecting user input in JavaScript》</a></li>
<li><a href="http://blog.danielfriesen.name/2010/02/16/html5-browser-maze-oninput-support/" title="A HTML5 Browser maze, oninput support" target="_blank">《A HTML5 Browser maze, oninput support》</a></li>
<li><a href="http://stackoverflow.com/questions/3667484/how-can-i-retrieving-text-from-input-field-as-its-entered-in-javascript/3667507" title="how can i retrieving text from input field as its entered in JavaScript??" target="_blank">《how can i retrieving text from input field as its entered in JavaScript??》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=712&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2011/10/18/oninput_and_onpropertychange/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>使用 JScript 创建 .exe 或 .dll 文件</title>
		<link>http://www.planabc.net/2011/06/04/use_jscript_to_create_windows_apps_and_libraries/</link>
		<comments>http://www.planabc.net/2011/06/04/use_jscript_to_create_windows_apps_and_libraries/#comments</comments>
		<pubDate>Fri, 03 Jun 2011 16:59:37 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[dll]]></category>
		<category><![CDATA[exe]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JScript]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=653</guid>
		<description><![CDATA[什么是 JScript？ JScript 是由微软开发的活动脚本语言，基于 ECMAScript 规范实现。Internet Explorer 中的 JavaScript，实际上是指 JScript。JScript 已被 Windows Script Host（WSH）支持（WSH 中的 JavaScript shell scripting：C:\> cscript jslint.js）。JScript 最新的版本（JScript.NET）基于 ECMAScript 4.0 ，并且可以在 .Net 环境下编译。 .NET Framwork 中包含有 JScript 编译器 ：JScriptCompiler （C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\jsc.exe），其可以将 JScript 文件编译为一个 .exe 或者 .dll 文件。 为了方便使用，我们可以将 JScriptCompiler 的路径加入到环境变量（环境变量 &#8211;> 系统变量 &#8211;> Path）中。在 CMD 程序运行窗口中直接调用命令 “jsc”，就可以看到编译器相关的帮助选项。 jsc [选项] [[选项] &#8230;] JScript 编译器选项 [...... ]]></description>
			<content:encoded><![CDATA[<p><strong>什么是 JScript？</strong></p>
<p>JScript 是由微软开发的活动脚本语言，基于 ECMAScript 规范实现。Internet Explorer 中的 JavaScript，实际上是指 JScript。JScript 已被 Windows Script Host（WSH）支持（WSH 中的 JavaScript shell scripting：<code>C:\> cscript jslint.js</code>）。JScript 最新的版本（JScript.NET）基于 ECMAScript 4.0 ，并且可以在 .Net 环境下编译。</p>
<p>.NET Framwork 中包含有 JScript 编译器 ：<strong>JScriptCompiler</strong> （C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\jsc.exe），其可以将 JScript 文件编译为一个 .exe 或者 .dll 文件。</p>
<p>为了方便使用，我们可以将 JScriptCompiler 的路径加入到环境变量（环境变量 &#8211;> 系统变量 &#8211;> Path）中。在 CMD 程序运行窗口中直接调用命令 “jsc”，就可以看到编译器相关的帮助选项。</p>
<blockquote style="font-family: Consolas;font-size:12px"><p>jsc [选项] <源文件> [[选项] <源文件>&#8230;]</p>
<p>                         JScript 编译器选项</p>
<p>                           &#8211; 输出文件 -<br />
  /out:&lt;file&gt;              指定二进制输出文件的名称<br />
  /t[arget]:exe            创建控制台应用程序(默认)<br />
  /t[arget]:winexe         创建 Windows 应用程序<br />
  /t[arget]:library        创建库程序集<br />
  /platform:&lt;platform&gt;     限制此代码可以在其上运行的平台；必须是 x86、Itanium、x64 或 anycpu。默认为 anycpu</p>
<p>                           &#8211; 输入文件 -<br />
  /autoref[+|-]            基于导入的命名空间和完全限定名称自动引用程序集(默认情<br />
况下为 on)<br />
  /lib:&lt;path&gt;              指定要在其中搜索引用的附加目录<br />
  /r[eference]:&lt;file list&gt; 从指定的程序集文件引用元数据 &lt;file list&gt;: &lt;assembly name&gt;[;&lt;assembly name&gt;...]</p>
<p>                           &#8211; 资源 -<br />
  /win32res:&lt;file&gt;        指定 Win32 资源文件(.res)<br />
  /res[ource]:&lt;info&gt;       嵌入指定的资源 &lt;info&gt;: &lt;filename&gt;[,&lt;name&gt;[,public|private]]<br />
  /linkres[ource]:&lt;info&gt;   将指定的资源链接到此程序集 &lt;info&gt;: &lt;filename&gt;[,&lt;name&gt;[,public|private]]</p>
<p>                           &#8211; 代码生成 -<br />
  /debug[+|-]              发出调试信息<br />
  /fast[+|-]               禁用语言功能以使代码更好地生成<br />
  /warnaserror[+|-]        将警告视为错误<br />
  /w[arn]:&lt;level&gt;          设置警告等级(0-4)</p>
<p>                           &#8211; 杂项 -<br />
  @&lt;filename&gt;              有关更多选项，请阅读响应文件<br />
  /?                       显示帮助<br />
  /help                    显示帮助<br />
  /d[efine]:&lt;symbols&gt;      定义条件编译符号<br />
  /nologo                  不显示编译器版权标志<br />
  /print[+|-]              提供 print() 函数</p>
<p>                           &#8211; 高级 -<br />
  /codepage:&lt;id&gt;           使用指定的代码页 ID 打开源文件<br />
  /lcid:&lt;id&gt;               将指定的 LCID 用于消息和默认代码页<br />
  /nostdlib[+|-]           不导入标准库(mscorlib.dll)并将 autoref 默认值更改为 off<br />
  /utf8output[+|-]         以 UTF-8 字符编码形式发出编译器输出<br />
  /versionsafe[+|-]        为没有标记为“override”或“hide”的成员指定默认值</p></blockquote>
<p><strong>创建 .exe 文件</strong></p>
<p>先创建 JS 文件（C:\test\helloWorld.js），内容如下：</p>
<pre><code>var date = new Date();
print('Hello World! \nToday is ' + date );</code></pre>
<p>接着我们进行编译：</p>
<pre><code>C:\test>jsc helloWorld.js

Microsoft (R) JScript Compiler version 8.00.50727
for Microsoft (R) .NET Framework version 2.0.50727
Copyright (C) Microsoft Corporation 1996-2005。保留所有权利。</code></pre>
<p>你会惊讶的发现，C:\test 目录下多了个 helloWorld.exe 文件，非常简单吧，呵呵</p>
<p>最后我们可以直接执行 helloWorld.exe 文件：</p>
<pre><code>C:\test>helloWorld

Hello World!
Today is Fri Jun 3 23:13:20 UTC+8 2011</code></pre>
<p>大功告成！！</p>
<p><strong>创建 .dll 文件</strong></p>
<p>.dll 文件的创建也同样非常简单：</p>
<pre><code>package LibHW {
    class HelloWorld {
        function run() {
            var date = new Date();
            return 'Hello World! \nToday is ' + date;
        }
    }
}</code></pre>
<p>编译语句：</p>
<pre><code>C:\test>jsc /t:library LibHW.js</code></pre>
<p>对于生成的 LibHW.dll 文件，我们可以通过创建新的 .exe 文件（consumer.js &#8211;> consumer.exe）以导入模块的方式调用（类似于 Python）。</p>
<p>先创建 consumer.js 文件：</p>
<pre><code>import LibHW;
var hw = new LibHW.HelloWorld();
print(hw.run());</code></pre>
<p>然后编译 consumer.js 文件，执行 consumer.exe：</p>
<pre><code>C:\test>jsc consumer.js

Microsoft (R) JScript Compiler version 8.00.50727
for Microsoft (R) .NET Framework version 2.0.50727
Copyright (C) Microsoft Corporation 1996-2005。保留所有权利。

C:\test>consumer

Hello World!
Today is Sat Jun 4 00:42:35 UTC+8 2011</code></pre>
<p>当然你还可以创建 windows 的应用，上例中的 consumer.js 文件修改如下：</p>
<pre><code>import System.Windows.Forms; // this has a MessageBox class
import LibHW;

var hw = new LibHW.HelloWorld();
MessageBox.Show(
    hw.run(),
    "Dude!",
    MessageBoxButtons.OK,
    MessageBoxIcon.Exclamation
);</code></pre>
<p>编译语句：</p>
<pre><code>C:\test>jsc /t:winexe consumer.js</code></pre>
<p>双击新生成的 consumer.exe 文件，哈哈，是不是很有成就感！</p>
<p>从上面的例子可以看到 JScript 的潜力无限，没有做不到，只有想不到。</p>
<p>扩展阅读：</p>
<ul>
<li><a href="http://www.phpied.com/make-your-javascript-a-windows-exe/" target="_blank" title="Make your javascript a Windows .exe">《Make your javascript a Windows .exe》</a></li>
<li><a href="http://www.phpied.com/javascript-shell-scripting/" target="_blank" title="JavaScript shell scripting">《JavaScript shell scripting》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=653&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2011/06/04/use_jscript_to_create_windows_apps_and_libraries/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Simple JavaScript Template : substitute</title>
		<link>http://www.planabc.net/2011/05/31/simple_javascript_template_substitute/</link>
		<comments>http://www.planabc.net/2011/05/31/simple_javascript_template_substitute/#comments</comments>
		<pubDate>Tue, 31 May 2011 06:54:49 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[replace]]></category>
		<category><![CDATA[substitute]]></category>
		<category><![CDATA[template]]></category>
		<category><![CDATA[YUI3]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=637</guid>
		<description><![CDATA[我们在平常使用字符串拼接的时候（如下例），会发现代码的可维护性和易读性将变得更加糟糕（代码中一堆的变量、双引号、单引号, 加号等，相信当情况更为复杂时，头一定发晕）： var url= 'http://www.plannabc.net/', title= '落草为根——专注前端技术&#038;关注用户体验', text = '怿飞's Blog'; var link = '&#60;a href="' + url + '" title="' + title+ '"&#62;' + text+ '&#60;/a&#62;'; 如果上述代码变为： var obj = { url: "http://www.plannabc.net/", title: "落草为根——专注前端技术&#038;关注用户体验", text: "怿飞's Blog" }; var link = '&#60;a href="{url}" title="{title}"&#62;{text}&#60;/a&#62;'; substitute(link, obj) 一切变得怡然自得。 substitute 函数的实现思路其实很简单：使用 String 的 replace 函数，在 [...... ]]></description>
			<content:encoded><![CDATA[<p>我们在平常使用字符串拼接的时候（如下例），会发现代码的可维护性和易读性将变得更加糟糕（代码中一堆的变量、双引号、单引号, 加号等，相信当情况更为复杂时，头一定发晕）：</p>
<pre><code>var url= 'http://www.plannabc.net/',
    title= '落草为根——专注前端技术&#038;关注用户体验',
    text = '怿飞's Blog';

var link = '&lt;a href="' + url + '" title="' + title+ '"&gt;' + text+ '&lt;/a&gt;';</code></pre>
<p>如果上述代码变为：</p>
<pre><code>var obj = {
    url: "http://www.plannabc.net/",
    title: "落草为根——专注前端技术&#038;关注用户体验",
    text: "怿飞's Blog"
};
var link = '&lt;a href="{url}" title="{title}"&gt;{text}&lt;/a&gt;';
substitute(link, obj)</code></pre>
<p>一切变得怡然自得。</p>
<p>substitute 函数的实现思路其实很简单：使用 String 的 <strong>replace</strong> 函数，在 replace 函数中用正则匹配除模板中的要替换的标签（“{key}”），并进行替换：</p>
<pre><code>function substitute (str, obj) {
    if (!(Object.prototype.toString.call(str) === '[object String]')) {
        return '';
    }

    // {}, new Object(), new Class()
    // Object.prototype.toString.call(node=document.getElementById("xx")) : ie678 == '[object Object]', other =='[object HTMLElement]'
    // 'isPrototypeOf' in node : ie678 === false , other === true
    if(!(Object.prototype.toString.call(obj) === '[object Object]' &#038;&#038; 'isPrototypeOf' in obj)) {
        return str;
    }

    // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/replace
    return str.replace(/\{([^{}]+)\}/g, function(match, key) {
        var value = obj[key];
        return ( value !== undefined) ? ''+value :'';
    });
}</code></pre>
<p>substitute 函数将模板中的标签 <code>{key}</code> 替换成 obj 中对应的 value（<code>obj[key]</code> or <code>obj[key].toString()</code>），如果不存在，则替换成空字符。</p>
<p>如果模板中某些内容不需要替换的怎么办？比如：<code>{some text need brace}</code> </p>
<ol>
<li>可以增加新的语法，人为控制不需要替换的模板标签，比如：<code>\{key\}</code></li>
<li>使用更为少见的字符作为模板标签，避免与常规情况撞车，比如：<code>{{key}}</code>
</ol>
<p>当然如果输入的数据 obj 为不完全信任的数据（比如：XSS）时，可以增加字符的转义：</p>
<pre><code>function escape(s) {
    s = String(s === null ? "" : s);
    return s.replace(/&#038;(?!\w+;)|["'<>\\]/g, function(s) {
        switch(s) {
            case "&#038;": return "&amp;amp;";
            case "\\": return "\\\\";
            case '"': return '&amp;quot;';
            case "'": return '&amp;#39;';
            case "<": return "&amp;lt;";
            case ">": return "&amp;gt;";
            default: return s;
        }
    });
}</code></pre>
<p>我们再看看 YUI3 中的实现 ：<a href="https://github.com/yui/yui3/blob/master/src/substitute/js/substitute.js" target="_blank">https://github.com/yui/yui3/blob/master/src/substitute/js/substitute.js</a></p>
<p>YUI3中做了更多的处理 <code>substitute(s, o, f, recurse)</code>：</p>
<ol>
<li>允许传入 fn，fn 将对 obj 中对应的 key/value 进行处理，返回新的 value。</li>
<li>如果支持 <a href="https://github.com/yui/yui3/blob/master/src/dump/js/dump.js" target="_blank" title="Y.dump 函数">Y.dump 函数</a>，将把 value 是对象的转成一定格式的字符串，如果不支持，直接返回对象的 toString。</li>
<li>value 值为非对象、非字符串和非数字时 ，保持原标签不作替换。</li>
<li>如果 recurse 参数设置为 true，将进行标签的递归替换。</li>
</ol>
<p>其实一般的时候简单的方式就够用了。面对自己编写库或组件的时候，都会有如下的选择：</p>
<ol>
<li>大而全，涵盖所有的扩展，能捕捉到所有的异常。</li>
<li>小而精，满足一般的需求，特殊情况可定制，异常可通过约定控制。</li>
</ol>
<p>选择最合适的才是最重要的，对于自己，通常偏向后者。</p>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=637&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2011/05/31/simple_javascript_template_substitute/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>domReady Function</title>
		<link>http://www.planabc.net/2011/05/26/domready_function/</link>
		<comments>http://www.planabc.net/2011/05/26/domready_function/#comments</comments>
		<pubDate>Thu, 26 May 2011 11:57:42 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[domReady]]></category>
		<category><![CDATA[function]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=627</guid>
		<description><![CDATA[上次写过 《模拟兼容性的 addDOMLoadEvent 事件》，昨天抽时间整理成了 domReady 函数。 使用非常方便： domReady(function () { // Dom is loaded! You can do anything! }); 测试案例：http://blank.github.com/domready/test/ 代码如下（注释较为详尽，就不再说明了）： /** * domready.js - Specify a function to execute when the DOM is fully loaded. * Copyright (c) 2011 Blank Zheng (blankzheng@gmail.com) * http://www.planabc.net */ (function (doc, win) { var isReady = 0, isBind [...... ]]></description>
			<content:encoded><![CDATA[<p>上次写过 <a href="http://www.planabc.net/2009/07/30/adddomloadevent/" target="_blank" title="模拟兼容性的 addDOMLoadEvent 事件">《模拟兼容性的 addDOMLoadEvent 事件》</a>，昨天抽时间整理成了 <a href="https://github.com/blank/domready" target="_blank">domReady 函数</a>。</p>
<p>使用非常方便：</p>
<pre><code>domReady(function () {
    // Dom is loaded! You can do anything!
});</code></pre>
<p>测试案例：<a href="http://blank.github.com/domready/test/" target="_blank">http://blank.github.com/domready/test/</a></p>
<p>代码如下（注释较为详尽，就不再说明了）：</p>
<pre><code>/**
 *  domready.js - Specify a function to execute when the DOM is fully loaded.
 *  Copyright (c) 2011 Blank Zheng (blankzheng@gmail.com)
 *  http://www.planabc.net
 */

(function (doc, win) {
    var isReady = 0,
        isBind = 0,
        fns = [],
        testEl = doc.createElement('p'),
        bindReady,
        init;

        win.domReady = function(fn){
            bindReady(fn);

            if (isReady) {
                fn();
            } else {
                fns.push(fn);
            }
        };

        bindReady = function (){
            if(isBind) return;
            isBind = 1;

            // Catch cases where domReady is called after the browser event has already occurred.
            // readyState: "uninitalized"、"loading"、"interactive"、"complete" 、"loaded"
            if(doc.readyState === "complete") {
                init();
            } else if (doc.addEventListener) {
                doc.addEventListener("DOMContentLoaded", function () {
                    doc.removeEventListener("DOMContentLoaded", arguments.callee, false);
                    init();
                }, false);
                win.addEventListener("onload", init, false);
            } else if(doc.attachEvent) {
                // In IE, ensure firing before onload, maybe late but safe also for iframes.
                doc.attachEvent("onreadystatechange", function() {
                    if (doc.readyState === "complete") {
                        doc.detachEvent("onreadystatechange", arguments.callee);
                        init();
                    }
                });
                win.attachEvent("onload", init);

                // If IE and not a frame, continually check to see if the document is ready.
                if(testEl.doScroll &#038;&#038; win == win.top){
                    doScrollCheck();
                }
            }
        };

        // Process items when the DOM is ready.
        init = function () {
            isReady = 1;

            // Make sure body exists, at least, in case IE gets a little overzealous.
            // This is taked directly from jQuery's implementation.
            if (!doc.body) {
                setTimeout(init, 10);
                return;
            }

            for (var i = 0, l = fns.length; i < l; i++) {
                fns[i]();
            }
            fns = [];
        };

        function doScrollCheck() {
            if(isReady) return;

            try {
                // If IE is used, use the trick by Diego Perini
                // http://javascript.nwbox.com/IEContentLoaded/
                testEl.doScroll('left');
            } catch (e) {
                setTimeout(doScrollCheck, 10);
                return;
            }

            init();
        }

})(document, window);

/**
 * Ref:
 * http://www.planabc.net/2009/07/30/adddomloadevent/
 * https://github.com/ded/domready/blob/master/ready.js
 * https://github.com/Cu7l4ss/DomReady-script/blob/master/DomReady.js
 * https://github.com/jakobmattsson/onDomReady/blob/master/ondomready.js
 **/</code></pre>
<p>如果平时做一些简单测试，可以使用下面 MINI 的 domReady 函数：</p>
<pre><code>function domReady(fn) {
    // "uninitalized"、"loading"、"interactive"、"complete" 、"loaded"
    /in/.test(document.readyState) ? window.setTimeout(function() { domReady(fn); }, 10) : fn();
}</code></pre>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=627&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2011/05/26/domready_function/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>URI 中的一些规范</title>
		<link>http://www.planabc.net/2011/05/26/rfc-3986_url_generic_syntax/</link>
		<comments>http://www.planabc.net/2011/05/26/rfc-3986_url_generic_syntax/#comments</comments>
		<pubDate>Thu, 26 May 2011 05:13:39 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[uri]]></category>
		<category><![CDATA[URL]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=618</guid>
		<description><![CDATA[In RFC 3986： //URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" sub-delims = "!" / "$" / "&#038;" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" reserved [...... ]]></description>
			<content:encoded><![CDATA[<p> <strong>In <a href="http://www.apps.ietf.org/rfc/rfc3986.html" target="_blank" title="RFC 3986">RFC 3986</a>：</strong></p>
<pre><code>//URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] 

gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
sub-delims = "!" / "$" / "&#038;" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="

reserved = gen-delims / sub-delims
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
percent-encoded = "%" HEXDIG HEXDIG

pchar = unreserved / percent-encoded / sub-delims / ":" / "@"

query = *( pchar / "/" / "?" ) // "?" query
fragment = *( pchar / "/" / "?" ) //"#" fragment</code></pre>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=618&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2011/05/26/rfc-3986_url_generic_syntax/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>如何用 JavaScript 开发迷宫游戏</title>
		<link>http://www.planabc.net/2011/03/22/maze_game/</link>
		<comments>http://www.planabc.net/2011/03/22/maze_game/#comments</comments>
		<pubDate>Tue, 22 Mar 2011 11:51:17 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[game]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[maze]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=608</guid>
		<description><![CDATA[3月6日在杭州 Google GTUG 活动上的分享《Maze Game》，本想讲 《Android 手机开发及HTML5在手机开发中的应用》，但由于 Google 邀约得比较晚，来不及准备，就在原先准备在团队内分享的未完成 PPT ——《Maze Game》的基础上，做了补充和完善。 游戏部分思路来源于同事 季札 开发的迷宫游戏：www.sansi.org/static/maze/maze.htm... ]]></description>
			<content:encoded><![CDATA[<p>3月6日在杭州 Google  GTUG 活动上的分享《Maze Game》，本想讲 《Android 手机开发及HTML5在手机开发中的应用》，但由于 Google 邀约得比较晚，来不及准备，就在原先准备在团队内分享的未完成 PPT ——《Maze Game》的基础上，做了补充和完善。</p>
<p><object id="__sse7344953" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=maze-110322062850-phpapp01&#038;stripped_title=maze-game-7344953&#038;userName=blankzheng" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse7344953" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=maze-110322062850-phpapp01&#038;stripped_title=maze-game-7344953&#038;userName=blankzheng" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object> </p>
<p>游戏部分思路来源于同事 <a href="http://oldj.net/" target="_blank" title="老杰的博客">季札</a> 开发的迷宫游戏：<a href="http://www.sansi.org/static/maze/maze.html" target="_blank">www.sansi.org/static/maze/maze.html</a></p>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=608&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2011/03/22/maze_game/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Script 元素 type 属性的妙用</title>
		<link>http://www.planabc.net/2011/03/04/use_script_invalid_type_attribute/</link>
		<comments>http://www.planabc.net/2011/03/04/use_script_invalid_type_attribute/#comments</comments>
		<pubDate>Fri, 04 Mar 2011 12:23:52 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[parallel]]></category>
		<category><![CDATA[type]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=602</guid>
		<description><![CDATA[我们经常使用动态创建 JavaScript 的方式来实现 JavaScript 文件的无阻塞（Non-blocking）、并行下载（Parallel ）。其实还可以添加自定义的 type 属性（比如 text/cache），达到预加载但不执行的效果： var doc = document, el = doc.createElement("script"), head = doc.getElementsByTagName('head')[0]; el.src = "http://yui.yahooapis.com/2.8.2r1/build/yahoo-dom-event/yahoo-dom-event.js"; el.type = "text/cache"; head.insertBefore(el, head.firstChild); 注：Firefox 3.6.x 不请求 JavaScript 文件。 扩展阅读： 《Preload CSS/JavaScript without execution》 《Preloading JS and CSS as Print Stylesheets... ]]></description>
			<content:encoded><![CDATA[<p>我们经常使用动态创建 JavaScript 的方式来实现 JavaScript 文件的无阻塞（Non-blocking）、并行下载（Parallel ）。其实还可以添加自定义的 type 属性（比如 text/cache），达到预加载但不执行的效果：</p>
<pre><code>var doc = document,
    el = doc.createElement("script"),
    head = doc.getElementsByTagName('head')[0];

    el.src = "http://yui.yahooapis.com/2.8.2r1/build/yahoo-dom-event/yahoo-dom-event.js";
    el.type = "text/cache";
    head.insertBefore(el, head.firstChild);</code></pre>
<p>注：Firefox 3.6.x 不请求 JavaScript 文件。</p>
<p>扩展阅读：</p>
<ul>
<li><a href="http://www.phpied.com/preload-cssjavascript-without-execution/" target="_blank" title="Preload CSS/JavaScript without execution">《Preload CSS/JavaScript without execution》</a></li>
<li><a href="http://www.adequatelygood.com/2010/1/Preloading-JS-and-CSS-as-Print-Stylesheets" target="_blank" title="Preloading JS and CSS as Print Stylesheets">《Preloading JS and CSS as Print Stylesheets》</a></li>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=602&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2011/03/04/use_script_invalid_type_attribute/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>前端代码 Review 的一些思考</title>
		<link>http://www.planabc.net/2011/02/23/code_review/</link>
		<comments>http://www.planabc.net/2011/02/23/code_review/#comments</comments>
		<pubDate>Wed, 23 Feb 2011 10:33:29 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[review]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=593</guid>
		<description><![CDATA[查看大图：http://www.planabc.net/wp-content/uploads/2011/codereview.png 欢迎大家针对其中的问题，一起思考与交流！ 扩展阅读： 《探讨前端代码Review... ]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.planabc.net/wp-content/uploads/2011/codereview.png" alt="前端代码 Review 的一些思考" /></p>
<p>查看大图：<a href="http://www.planabc.net/wp-content/uploads/2011/codereview.png" target="_blank">http://www.planabc.net/wp-content/uploads/2011/codereview.png</a></p>
<p>欢迎大家针对其中的问题，一起思考与交流！</p>
<p>扩展阅读：</p>
<ul>
<li><a href="http://hikejun.com/blog/?p=610" title="探讨前端代码Review" target="_blank">《探讨前端代码Review》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=593&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2011/02/23/code_review/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>模拟实现 Range 的 insertNode() 方法</title>
		<link>http://www.planabc.net/2010/11/26/range_insertnode/</link>
		<comments>http://www.planabc.net/2010/11/26/range_insertnode/#comments</comments>
		<pubDate>Fri, 26 Nov 2010 09:53:02 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[node]]></category>
		<category><![CDATA[range]]></category>
		<category><![CDATA[selection]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=579</guid>
		<description><![CDATA[最近对 Range 和 Selection 比较感兴趣。 基本非 IE 的浏览器都支持 DOM Level2 中的 Range，而 IE 中仅有自己的简单处理方法（Text Rang）。 扩展阅读： 《TextRange Prototype》 《Document Object Model Range》 《Javascript标准DOM Range操作(1)》（节选自《JavaScript 高级程序设计》） 《Javascript标准DOM Range操作(2)》（节选自《JavaScript 高级程序设计》） 《Javascript标准DOM Range操作(3)》（节选自《JavaScript 高级程序设计》） 而 IE 下的 Text Rang 主要用来处理文本，并非 DOM 节点，那如何在 IE 下模拟 DOM Level2 中的 Range 呢？ 根据规范的 API，我们需要模拟下述属性和方法： function zRange() { // Inital states this.startContainer [...... ]]></description>
			<content:encoded><![CDATA[<p>最近对 <a href="http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html" title="Document Object Model Range" target="_blank">Range</a> 和 <a href="http://www.w3.org/TR/2009/WD-html5-20090423/editing.html#selection" title="The text selection APIs" target="_blank">Selection</a> 比较感兴趣。</p>
<p>基本非 IE 的浏览器都支持 DOM Level2 中的 <a href="http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html" title="Document Object Model Range" target="_blank">Range</a>，而 IE 中仅有自己的简单处理方法（<a href="http://msdn.microsoft.com/en-us/library/dd347140(v=VS.85).aspx" title="TextRange Prototype" target="_blank">Text Rang</a>）。</p>
<p>扩展阅读：</p>
<ul>
<li><a href="http://msdn.microsoft.com/en-us/library/dd347140(v=VS.85).aspx" target="_blank">《TextRange Prototype》</a></li>
<li><a href="http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html" target="_blank">《Document Object Model Range》</a></li>
<li><a href="http://never-online.net/blog/article.asp?id=113" target="_blank">《Javascript标准DOM Range操作(1)》</a>（节选自《JavaScript 高级程序设计》）</li>
<li><a href="http://never-online.net/blog/article.asp?id=114" target="_blank">《Javascript标准DOM Range操作(2)》</a>（节选自《JavaScript 高级程序设计》）</li>
<li><a href="http://never-online.net/blog/article.asp?id=115" target="_blank">《Javascript标准DOM Range操作(3)》</a>（节选自《JavaScript 高级程序设计》）</li>
</ul>
<p>而 IE 下的 Text Rang 主要用来处理文本，并非 DOM 节点，那如何在 IE 下模拟 DOM Level2 中的 Range 呢？</p>
<p>根据规范的 API，我们需要模拟下述属性和方法：</p>
<pre><code>function zRange() {
    // Inital states
    this.startContainer = document;
    this.startOffset = 0;
    this.endContainer = document;
    this.endOffset = 0;
    this.commonAncestorContainer = document;
    this.collapsed = true; 

    // Range constants
    this.START_TO_START = 0;
    this.START_TO_END = 1;
    this.END_TO_END = 2;
    this.END_TO_START = 3;
}

zRange.prototype = {
    // Public methods
    setStart : function(node, offset){},
    setEnd : function(node, offset){},
    setStartBefore : function(node){},
    setStartAfter : function(node){},
    setEndBefore : function(node){},
    setEndAfter : function(node){},
    collapse : function(toStart) {},
    selectNode : function(node) {},
    selectNodeContents : function(node){},
    deleteContents : function() {},
    extractContents : function(){},
    cloneContents : function() {},
    surroundContents : function () {},
    insertNode : function(node) {},
    cloneRange : function() {},
    detach : function() {},
    compareBoundaryPoints : function (how, sourceRange) {},
    constructor : zRange
}</code></pre>
<p>我们还可以看一组使用 Range 方法和属性的统计数据，对于 2/8 原则的实践或许有帮助：</p>
<pre><code>/**
 * Resource reference : http://kb.operachina.com/node/147
 *
 * collapse             51,435
 * setStartBefore       43,138
 * setStartAfter        40,270
 * selectNodeContents   37,027
 * collapsed            12,862
 * selectNode            4,636
 * deleteContents        3,935
 * setStart              3,171
 * startOffset           3,150
 * setEnd                3,086
 * detach                2,732
 * startContainer        2,659
 * endOffset             2,647
 * insertNode            2,321
 * cloneContents         2,261
 * endContainer          2,236
 * cloneRange            1,993
 * setEndAfter           1,911
 *
 */</code></pre>
<p>下面我们简单讨论一下 Range 的 insertNode() 方法的模拟实现：</p>
<blockquote><p>The insertNode() method inserts the specified node into the Range&#8217;s context tree. The node is inserted at the start boundary-point of the Range, without modifying it.</p>
<p>If the start boundary point of the Range is in a Text node, the insertNode operation splits the Text node at the boundary point. If the node to be inserted is also a Text node, the resulting adjacent Text nodes are not normalized automatically; this operation is left to the application.</p>
<p>The Node passed into this method can be a DocumentFragment. In that case, the contents of the DocumentFragment are inserted at the start boundary-point of the Range, but the DocumentFragment itself is not. Note that if the Node represents the root of a sub-tree, the entire sub-tree is inserted.</p></blockquote>
<p>从上面的引用得知 insertNode() 方法用来在选区的开头插入节点，固我们先获取 Range 对象的 startContainer（Range 是从那个节点中开始的，即选区中第一个节点的父节点） 和 startOffset（在 startContainer 中 Range 开始的偏移位置） 属性。</p>
<pre><code>var sc = this.startContainer,
    so = this.startOffset;</code></pre>
<p><strong>注：</strong> 如果 startContainer 是文本节点、注释节点或者是 CData 节点，startOffset 是指 Range 开始前的字符数，否则，偏移是 Range 中的第一个节点在其父节点中的索引。</p>
<p>接下来，我们需将情况分为下面两种：</p>
<p>第一种情形：startContainer 节点为文本节点、注释节点或者是 CData 节点。“startOffset 是指 Range 开始前的字符数”。</p>
<ul>
<li>如果 startOffset 等于 0，则表示 Range 是从 startContainer 起始位置开始，应将 node 插入到 startContainer 节点之前 <code>sc.parentNode.insertBefore(node, sc);</code></li>
<li>如果 startOffset 大于等于 startContainer 本身的节点长度，则表示 Range 是从 startContainer 末尾位置开始，应将 node 插入到 startContainer 节点之后，即如果存在下一节点，则插入到下一之前 <code> sc.parentNode.insertBefore(node, sc.nextSibling);</code>，如果不存在下一节点，则加入到 startContainer父节点最后 <code>sc.parentNode.appendChild(node);</code></li>
<li>如果 startOffset 在 startContainer 本身的节点长度之内，我们通过<code>oSplitNode = object.splitText( [iIndex])</code>将节点在startOffset 一分为二 <code>nn = sc.splitText(so);</code>，分为两个节点，则应将 node 插入到新生成的第二个节点之前 <code>sc.parentNode.insertBefore(node, nn);</code>。<br />
<blockquote><p>The text node that invokes the splitText method has a nodeValue equal to the substring of the value, from zero to iIndex. The new text node has a nodeValue of the substring of the original value, from the specified index to the value length. Text node integrity is not preserved when the document is saved or persisted. </p></blockquote>
</li>
</ul>
<pre><code>if (so===0) {
    // At the start of text
    sc.parentNode.insertBefore(node, sc);
} else if (so >= sc.nodeValue.length) {
    // At the end of text
    if (ns) {
        sc.parentNode.insertBefore(node, sc.nextSibling);
    } else {
        sc.parentNode.appendChild(node);
    }
} else {
    // Middle, need to split
    // http://msdn.microsoft.com/zh-cn/library/ms536764.aspx
    nn = sc.splitText(so);
    sc.parentNode.insertBefore(node, nn);
}</code></pre>
<p>第二种情形：startContainer 节点为非 第一种情形中的节点。“偏移是 Range 中的第一个节点在其父节点中的索引”。获取 startContainer 中子节点偏移量为 startOffset 的节点 <code>cn = sc.childNodes[so];</code>，如果存在，则按照 insertNode 方法的定义，应将 node 插入到该节点之前 <code>sc.insertBefore(node, cn);</code>，如果不存在，即 startOffset 大于等于 sc.childNodes.length，则应将 node 插入到 startContainer 的子节点最后<code>sc.appendChild(node);</code>。</p>
<pre><code>if (sc.childNodes.length > 0) {
    cn = sc.childNodes[so];
}

if (cn) {
    sc.insertBefore(node, cn);
} else {
    sc.appendChild(node);
}</code></pre>
<p>详细代码实现如下：</p>
<pre><code>zRange.prototype.insertNode = function(node) {
    var sc = this.startContainer,
        so = this.startOffset,
        p = sc.parentNode,
        ns = sc.nextSibling,
        nn, cn;

    // 如果节点是 TEXT_NODE 或者 CDATA_SECTION_NODE
    if ((sc.nodeType === 3 || sc.nodeType === 4 || sc.nodeType === 8 ) &#038;&#038; sc.nodeValue) {
        if (so === 0) {
            // At the start of text
            p.insertBefore(node, sc);
        } else if (so >= sc.nodeValue.length) {
            // At the end of text
            if (ns) {
                p.insertBefore(node, ns);
            } else {
                p.appendChild(node);
            }
        } else {
            // Middle, need to split
            // http://msdn.microsoft.com/zh-cn/library/ms536764.aspx
            nn = sc.splitText(so);
            p.insertBefore(node, nn);
        }
    } else {
        if (sc.childNodes.length > 0) {
            cn = sc.childNodes[so];
        }

        if (cn) {
            sc.insertBefore(node, cn);
        } else {
            sc.appendChild(node);
        }
    }
}</code></pre>
<p>剩下的方法，大家可以尝试着去模拟一把，其实并不复杂，也许会其乐无穷，呵呵</p>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=579&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/11/26/range_insertnode/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>DOM 元素如何获得焦点</title>
		<link>http://www.planabc.net/2010/11/18/how_to_get_focus/</link>
		<comments>http://www.planabc.net/2010/11/18/how_to_get_focus/#comments</comments>
		<pubDate>Thu, 18 Nov 2010 07:30:11 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[focus]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=568</guid>
		<description><![CDATA[首先让我们看看哪些元素可以直接获得焦点（element..focus()）： // Form : http://www.w3.org/TR/html5/editing.html#focusable a elements that have an href attribute link elements that have an href attribute button elements that are not disabled input elements whose type attribute are not in the Hidden state and that are not disabled select elements that are not disabled textarea elements that are not disabled command elements [...... ]]></description>
			<content:encoded><![CDATA[<p>首先让我们看看哪些元素可以直接获得焦点（<code>element..focus()</code>）：</p>
<blockquote><p>// Form : http://www.w3.org/TR/html5/editing.html#focusable</p>
<ul>
<li>a elements that have an href attribute</li>
<li>link elements that have an href attribute</li>
<li>button elements that are not disabled</li>
<li>input elements whose type attribute are not in the Hidden state and that are not disabled</li>
<li>select elements that are not disabled</li>
<li>textarea elements that are not disabled</li>
<li>command elements that do not have a disabled attribute</li>
<li>Elements with a draggable attribute set, if that would enable the user agent to allow the user to begin a drag operations for those elements without the use of a pointing device</li>
<li>Each shape that is generated for an area element</li>
<li> &#8230; </li>
</ul>
</blockquote>
<p>而除上面以外的元素（比如：div，p）一般都无法直接获得焦点，那如何处理呢？</p>
<ol>
<li>给元素添加 contenteditable 属性。<br />
<blockquote><p>// From : http://www.w3.org/TR/2009/WD-html5-20090423/editing.html#attr-contenteditable</p>
<p>User agents must make <strong>editing hosts </strong><strong>focusable</strong> (which typically means they enter the tab order).</p></blockquote>
</li>
<li>给元素添加 tabindex 属性。<br />
<blockquote><p>// From : http://www.w3.org/TR/2009/WD-html5-20090423/editing.html#attr-tabindex</p>
<p>The <strong>tabindex</strong> content attribute specifies <strong>whether the element is focusable</strong>, whether it can be reached using sequential focus navigation, and the relative order of the element for the purposes of sequential focus navigation. </p></blockquote>
<p>非常推荐使用  <code>tabindex = -1</code> ，基本无副作用！！</p>
<blockquote><p>// From : http://www.w3.org/TR/2009/WD-html5-20090423/editing.html#attr-tabindex</p>
<p><strong>If the value is a negative integer: </strong>The user agent must allow the element to be focused, <strong>but should not allow the element to be reached using sequential focus navigation</strong>.</p></blockquote>
</li>
</ol>
<p>综述，使无法直接获得焦点的元素获得焦点的最佳实践就是：给元素添加 <code>tabindex = -1</code> 属性。</p>
<p>当然你还可以通过子元素（focusable）的 focus 事件的捕获或冒泡来模拟，但需要处理事件的浏览器兼容（<a href="http://www.planabc.net/2010/01/30/how_to_use_focus_and_blur_event_in_event_delegation/" target="_blank" title="如何在事件代理中正确使用 focus 和 blur 事件">《如何在事件代理中正确使用 focus 和 blur 事件》</a>）。</p>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=568&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/11/18/how_to_get_focus/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>获取 Textarea 的光标位置</title>
		<link>http://www.planabc.net/2010/11/17/get_textarea_cursor_position/</link>
		<comments>http://www.planabc.net/2010/11/17/get_textarea_cursor_position/#comments</comments>
		<pubDate>Wed, 17 Nov 2010 11:47:24 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[selection]]></category>
		<category><![CDATA[textarea]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=549</guid>
		<description><![CDATA[在任何编辑器中，获取光标位置都是非常重要的，很多人可能认为较难，其实只要处理好浏览器的兼容，还是比较容易实现的。 下面我们一起来看看如何获取到 Textarea 元素中的光标位置（测试地址）。 首先，我们用 rangeData 对象作为数据存储，并获得焦点： var rangeData = {start: 0, end: 0, text: "" }; textarea.focus(); 对于非 IE 浏览器获取选区的起始和末尾位置其实非常容易： rangeData.start= el.selectionStart; rangeData.end = el.selectionEnd; 通过截取我们可以得到光标的选区内容： rangeData.text = (rangeData.start != rangeData.end) ? el.value.substring(rangeData.start, rangeData.end): ""; 而对于 IE 浏览器处理起来就比较麻烦了，但我们依旧可以获取到选区： oS = document.selection.createRange(); 同时还可获取 Textarea 元素的选区： // 为了使 oR 与 oS 在同一等级上比较，请勿使用：oR = textarea.createTextRange() ... ]]></description>
			<content:encoded><![CDATA[<p>在任何编辑器中，获取光标位置都是非常重要的，很多人可能认为较难，其实只要处理好浏览器的兼容，还是比较容易实现的。</p>
<p>下面我们一起来看看如何获取到 Textarea 元素中的光标位置（<a href="http://www.planabc.net/demo/range/textarea-cursor-position.html" target="_blank">测试地址</a>）。</p>
<p>首先，我们用 rangeData 对象作为数据存储，并获得焦点：</p>
<pre><code>var rangeData = {start: 0, end: 0, text: "" };
textarea.focus();</code></pre>
<p>对于非 IE 浏览器获取选区的起始和末尾位置其实非常容易：</p>
<pre><code>rangeData.start= el.selectionStart;
rangeData.end = el.selectionEnd;</code></pre>
<p>通过截取我们可以得到光标的选区内容：</p>
<pre><code>rangeData.text = (rangeData.start != rangeData.end) ? el.value.substring(rangeData.start, rangeData.end): "";</code></pre>
<p>而对于 IE 浏览器处理起来就比较麻烦了，但我们依旧可以获取到选区：</p>
<pre><code>oS = document.selection.createRange();</code></pre>
<p>同时还可获取 Textarea 元素的选区：</p>
<pre><code>// 为了使 oR 与 oS 在同一等级上比较，请勿使用：oR = textarea.createTextRange()
oR = document.body.createTextRange();
oR.moveToElementText(textarea);</code></pre>
<p>如果光标在 Textarea 元素内，很自然 oS.text 就是我们需要的选区内容：</p>
<pre><code>rangeData.text = oS.text;</code></pre>
<p>并且我们可以通过 oS.getBookmark() 方法获取到选区的位置数据，该位置数据可以通过 moveToBookmark() 方法设置回去。</p>
<blockquote><p><a href="http://msdn.microsoft.com/en-us/library/ms536432%28v=VS.85%29.aspx" title="getBookmark Method" target="_blank">getBookmark</a>: Retrieves a bookmark (opaque string) that can be used with moveToBookmark to return to the same range. </p>
<p><a href="http://msdn.microsoft.com/en-us/library/ms536628%28v=VS.85%29.aspx" title="moveToBookmark Method" target="_blank">moveToBookmark</a>: Moves to a bookmark.</p></blockquote>
<p>我们用 rangeData.bookmark 来记录该位置数据：</p>
<pre><code>rangeData.bookmark = oS.getBookmark();</code></pre>
<p>下面是最重要的步骤：我们比较 oR 与 oS 的选区起始位置（使用 <code>object.compareEndPoints(sType, oRange)</code> 方法比较），如果 oR 的起始位置在 oS 之前，我们向前移动 oS 的起始位置1个字符（使用 <code>object.moveStart(sUnit [, iCount])</code> 方法移动），一直当 oS 的起始位置在 oR 之前停止，移动的位置，则是选区的起始位置。</p>
<blockquote><p><a href="http://msdn.microsoft.com/en-us/library/ms536373%28v=VS.85%29.aspx" title="compareEndPoints Method" target="_blank">compareEndPoints</a>: Compares an end point of a TextRange object with an end point of another range. </p>
<p><a href="http://msdn.microsoft.com/en-us/library/ms536623%28v=VS.85%29.aspx" title="moveStart Method" target="_blank">moveStart</a>: Changes the start position of the range.</p></blockquote>
<pre><code>for (i = 0; oR.compareEndPoints('StartToStart', oS) < 0 &#038;&#038; oS.moveStart("character", -1) !== 0; i ++) {}
rangeData.start = i;</code></pre>
<p>但由于在 IE 中，Textarea 元素中的所有换行符都占 1  个字符，可以通过 <code>alert(textarea.value.length)</code> 查看，故要对上面的代码做部分处理：</p>
<pre><code>for (i = 0; oR.compareEndPoints('StartToStart', oS) < 0 &#038;&#038; oS.moveStart("character", -1) !== 0; i ++) {
    // Why? You can alert(textarea.value.length)
    if (textarea.value.charAt(i) == '\n') {
        i ++;
    }
}
rangeData.start = i;</code></pre>
<p>既然得到了选区的起始位置和选区字符串的字符，很自然我们可以计算得到选区的末尾位置：</p>
<pre><code>rangeData.end = rangeData.text.length + rangeData.start;</code></pre>
<p>获取 Textarea 的光标位置的 getCursorPosition 函数方法整理如下：</p>
<pre><code>/**
* getCursorPosition Method
*
* Created by Blank Zheng on 2010/11/12.
* Copyright (c) 2010 PlanABC.net. All rights reserved.
*
* The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license.
*/
function getCursorPosition(textarea) {
    var rangeData = {text: "", start: 0, end: 0 };
	textarea.focus();
    if (textarea.setSelectionRange) { // W3C
        rangeData.start= textarea.selectionStart;
        rangeData.end = textarea.selectionEnd;
        rangeData.text = (rangeData.start != rangeData.end) ? textarea.value.substring(rangeData.start, rangeData.end): "";
    } else if (document.selection) { // IE
        var i,
            oS = document.selection.createRange(),
            // Don't: oR = textarea.createTextRange()
            oR = document.body.createTextRange();
        oR.moveToElementText(textarea);

        rangeData.text = oS.text;
        rangeData.bookmark = oS.getBookmark();

        // object.moveStart(sUnit [, iCount])
        // Return Value: Integer that returns the number of units moved.
        for (i = 0; oR.compareEndPoints('StartToStart', oS) < 0 &#038;&#038; oS.moveStart("character", -1) !== 0; i ++) {
            // Why? You can alert(textarea.value.length)
            if (textarea.value.charAt(i) == '\n') {
                i ++;
            }
        }
        rangeData.start = i;
        rangeData.end = rangeData.text.length + rangeData.start;
    }

    return rangeData;
}</code></pre>
<p>得到 Textarea 元素光标位置，当Textarea 中的光标丢失了，再设置回来就简单多了：</p>
<pre><code>/**
* setCursorPosition Method
*
* Created by Blank Zheng on 2010/11/12.
* Copyright (c) 2010 PlanABC.net. All rights reserved.
*
* The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license.
*/
function setCursorPosition(textarea, rangeData) {
    if(!rangeData) {
        alert("You must get cursor position first.")
    }
    if (textarea.setSelectionRange) { // W3C
        textarea.focus();
        textarea.setSelectionRange(rangeData.start, rangeData.end);
    } else if (textarea.createTextRange) { // IE
        var oR = textarea.createTextRange();
        // Fixbug :
        // In IE, if cursor position at the end of textarea, the setCursorPosition function don't work
        if(textarea.value.length === rangeData.start) {
            oR.collapse(false)
            oR.select();
        } else {
            oR.moveToBookmark(rangeData.bookmark);
            oR.select();
        }
    }
}</code></pre>
<p>测试地址：<a href="http://www.planabc.net/demo/range/textarea-cursor-position.html" target="_blank">http://www.planabc.net/demo/range/textarea-cursor-position.html</a></p>
<p>扩展阅读：</p>
<ul>
<li><a href="http://msdn.microsoft.com/en-us/library/dd347140%28v=VS.85%29.aspx" title="TextRange Prototype" target="_blank">《TextRange Prototype》</a></li>
<li><a href="http://geekswithblogs.net/svanvliet/archive/2005/03/24/textarea-cursor-position-with-javascript.aspx" title="TextArea Cursor Position with JavaScript" target="_blank">《TextArea Cursor Position with JavaScript》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=549&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/11/17/get_textarea_cursor_position/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>类型转换的小乐趣</title>
		<link>http://www.planabc.net/2010/11/01/funny_type_conversion_in_javascript/</link>
		<comments>http://www.planabc.net/2010/11/01/funny_type_conversion_in_javascript/#comments</comments>
		<pubDate>Mon, 01 Nov 2010 04:50:50 +0000</pubDate>
		<dc:creator>怿飞</dc:creator>
				<category><![CDATA[JS/Ajax/AS/Flex]]></category>
		<category><![CDATA[Array]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[type]]></category>

		<guid isPermaLink="false">http://www.planabc.net/?p=534</guid>
		<description><![CDATA[在 玉伯 的文章 《一道大题目，嘿嘿》 中有这样一段代码： [] == ![] 也许很多同学迷惑：咦，这个如何转换呢？ 首先，我们了解下逻辑 NOT（!）运算与等号（==）运算的一些基本规则： 逻辑 NOT 运算中，如果运算数是对象，返回 fasle； 等号运算中，如果一个运算数是 Boolean 值，在运算前，会将其转换成数字：false -> 0，true -> 1； 等号运算中，如果一个运算数是对象，另一个是数字，在运算前，会将对象转换成数字。 2010年11月2日补充：对象不能直接转换成数字型，可以将其转换成字符型，再将字符型转换成数字型。 根据上面的一些规则，我们再来看看原来的表达式如何演变： Step01: [] == ![] // 由于[]是对象，则 ![] 返回fasle，演变为 Step02。 Step02: [] == fasle // 由于 fasle 是 Boolean 值，则转换成数字 0，演变为 Step03。 Step03: [] == 0 // 由于 0是数字，[]是对象... ]]></description>
			<content:encoded><![CDATA[<p>在 <a href="http://lifesinger.org/" target="_blank">玉伯</a> 的文章 <a href="http://lifesinger.org/blog/2010/10/a-hard-quiz/" title="一道大题目，嘿嘿" target="_blank">《一道大题目，嘿嘿》</a> 中有这样一段代码：</p>
<pre><code>[] == ![]</code></pre>
<p>也许很多同学迷惑：咦，这个如何转换呢？</p>
<p>首先，我们了解下逻辑 NOT（!）运算与等号（==）运算的一些基本规则：</p>
<ol>
<li>逻辑 NOT 运算中，如果运算数是对象，返回 fasle；</li>
<li>等号运算中，如果一个运算数是 Boolean 值，在运算前，会将其转换成数字：false -> 0，true -> 1；</li>
<li>等号运算中，如果一个运算数是对象，另一个是数字，在运算前，会将对象转换成数字。</li>
</ol>
<p><strong>2010年11月2日补充：</strong>对象不能直接转换成数字型，可以将其转换成字符型，再将字符型转换成数字型。</p>
<p>根据上面的一些规则，我们再来看看原来的表达式如何演变：</p>
<pre><code>Step01: [] == ![] // 由于[]是对象，则 ![] 返回fasle，演变为 Step02。
Step02: [] == fasle // 由于 fasle 是 Boolean 值，则转换成数字 0，演变为 Step03。
Step03: [] == 0 // 由于 0是数字，[]是对象，<del datetime="2010-11-02T02:08:01+00:00">[] 转换成数字 Number([]) -> 0，</del>[] 转换成字符型 [].toString() -> ""，"" 再转换成数字型 -> 0 ，演变为 Step04。
Step04: 0 == 0 // 返回最终结果：true</code></pre>
<p>思考题：</p>
<pre><code>{} == ! {}; //返回什么值？
[] == [] //返回什么值？
</code></pre>
<p>PS：如果你对玉伯的题目很感兴趣，推荐阅读：</p>
<ul>
<li><a href="http://www.cn-cuckoo.com/main/wp-content/uploads/2009/12/named-function-expressions-demystified.html" title="命名函数表达式探秘" target="_blank">《命名函数表达式探秘》</a></li>
</ul>
<img src="http://www.planabc.net/?ak_action=api_record_view&id=534&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.planabc.net/2010/11/01/funny_type_conversion_in_javascript/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<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+）中，都提供了 “mousewheel” 事件。但杯具的是 Firefox 3.5+ 却不支持此事件，不过庆幸 Firefox 3.5+ 中提供了另外一个等同的事件：”DOMMouseScroll” （事件和事件属性的测试案例）。 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">“mousewheel”</a> 事件。但杯具的是 Firefox 3.5+ 却不支持此事件，不过庆幸 Firefox 3.5+ 中提供了另外一个等同的事件：”DOMMouseScroll” （<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>“mousewheel” 事件中的 “event.wheelDelta” 属性值：返回的值，如果是正值说明滚轮是向上滚动，如果是负值说明滚轮是向下滚动；返回的值，均为 120 的倍数，即：幅度大小 = 返回的值 / 120。 </li>
<li>“DOMMouseScroll” 事件中的 “event.detail” 属性值：返回的值，如果是负值说明滚轮是向上滚动（与 “event.wheelDelta” 正好相反），如果是正值说明滚轮是向下滚动；返回的值，均为 3 的倍数，即：幅度大小 = 返回的值 / 3。 </li>
<li>“mousewheel” 事件在 Opera 10+ 中却是个特例，既有 “event.wheelDelta” 属性，也有 “event.detail” 属性。</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, “detail” returns the same value as it does in FF, so for the big O you should rely on “detail” instead of “wheelDelta”, 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 属性与其他浏览器中的表现完全一致，未发现异常与错误，从接口角度来说，代码中应优先使用 “event.wheelDelta” 属性。</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>9</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>6</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>22</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>15</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>7</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>28</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>15</slash:comments>
		</item>
	</channel>
</rss>

