<?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>army8735</title>
	<atom:link href="http://army8735.org/feed" rel="self" type="application/rss+xml" />
	<link>http://army8735.org</link>
	<description>单音很痛苦，压音更痛苦，但是学会很美好。</description>
	<lastBuildDate>Thu, 11 Mar 2010 07:41:09 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Project Darkstar虽死，RedDwarf Server已活</title>
		<link>http://army8735.org/2010/03/11/731.html</link>
		<comments>http://army8735.org/2010/03/11/731.html#comments</comments>
		<pubDate>Thu, 11 Mar 2010 07:39:34 +0000</pubDate>
		<dc:creator>army</dc:creator>
				<category><![CDATA[其它]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=731</guid>
		<description><![CDATA[Sun被Oracle收购后，停掉了不少开源项目，其中就有我关心的《Project Darkstar》。今日突发奇想又登录了下老的网站主页，发现关闭之后多出了一个链接：
http://www.reddwarfserver.org/。
哦也，开源之火生生不息！目前的0.10.0版本其实就是Project Darkstar停之前最新的发布版本。期待你的表现，某人赶快给我研究去……嘿嘿……
]]></description>
			<content:encoded><![CDATA[<p>Sun被Oracle收购后，停掉了不少开源项目，其中就有我关心的《Project Darkstar》。今日突发奇想又登录了下老的网站主页，发现关闭之后多出了一个链接：</p>
<p><a href="http://www.reddwarfserver.org/">http://www.reddwarfserver.org/</a>。</p>
<p>哦也，开源之火生生不息！目前的0.10.0版本其实就是Project Darkstar停之前最新的发布版本。期待你的表现，<a href="http://www.david0446.com/" target="_blank">某人</a>赶快给我研究去……嘿嘿……</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/03/11/731.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TUI: Tudou’s Javascript Library</title>
		<link>http://army8735.org/2010/03/09/723.html</link>
		<comments>http://army8735.org/2010/03/09/723.html#comments</comments>
		<pubDate>Tue, 09 Mar 2010 01:59:18 +0000</pubDate>
		<dc:creator>army</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[tui]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=723</guid>
		<description><![CDATA[这是由Dexter同学厚积薄发的作品，分为第三方类库、核心、输入输出、视图、界面组件、工具以及插件等几大部分。

]]></description>
			<content:encoded><![CDATA[<p>这是由Dexter同学厚积薄发的作品，分为第三方类库、核心、输入输出、视图、界面组件、工具以及插件等几大部分。</p>
<p><a href="http://army8735.org/wp-content/uploads/2010/03/tui.png" target="_blank"><img class="alignnone size-medium wp-image-726" title="tui" src="http://army8735.org/wp-content/uploads/2010/03/tui-174x300.png" alt="" width="174" height="300" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/03/09/723.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>JAcld beta1</title>
		<link>http://army8735.org/2010/03/07/708.html</link>
		<comments>http://army8735.org/2010/03/07/708.html#comments</comments>
		<pubDate>Sun, 07 Mar 2010 09:41:58 +0000</pubDate>
		<dc:creator>army</dc:creator>
				<category><![CDATA[JAcld]]></category>
		<category><![CDATA[前端开发]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=708</guid>
		<description><![CDATA[http://code.google.com/p/jacld/
预览地址：http://jacld.googlecode.com/svn/trunk/bin/index.html
JAcld(Javascript &#38; Actionscript CapsLock Detection)是一款基于as和js的大写锁定检测工具，大小只有1k多，作用于web上。
今天终于搞定了针对webkit的特殊修正，所以目前主要浏览器都能使用了。不过还有2点遗憾：

webkit由于flash的聚焦bug，无法通过flash检测，因此使用了js检测。它跟flash相比起来体验方面要稍微差一点。
ie下亦存在聚焦问题，当使用tab键切换focus到输入框上时，有一定几率聚焦失败。

综合而言beta1倒是可以尝试使用的。谁能研究出搞定那2个bug的方法就谢天谢地了……
]]></description>
			<content:encoded><![CDATA[<p><a href="http://code.google.com/p/jacld/">http://code.google.com/p/jacld/</a></p>
<p>预览地址：<a href="http://jacld.googlecode.com/svn/trunk/bin/index.html">http://jacld.googlecode.com/svn/trunk/bin/index.html</a></p>
<p>JAcld(Javascript &amp; Actionscript CapsLock Detection)是一款基于as和js的大写锁定检测工具，大小只有1k多，作用于web上。</p>
<p>今天终于搞定了针对webkit的特殊修正，所以目前主要浏览器都能使用了。不过还有2点遗憾：</p>
<ol>
<li>webkit由于flash的聚焦bug，无法通过flash检测，因此使用了js检测。它跟flash相比起来体验方面要稍微差一点。</li>
<li>ie下亦存在聚焦问题，当使用tab键切换focus到输入框上时，有一定几率聚焦失败。</li>
</ol>
<p>综合而言beta1倒是可以尝试使用的。谁能研究出搞定那2个bug的方法就谢天谢地了……</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/03/07/708.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[译]理解删除</title>
		<link>http://army8735.org/2010/03/05/691.html</link>
		<comments>http://army8735.org/2010/03/05/691.html#comments</comments>
		<pubDate>Fri, 05 Mar 2010 07:57:28 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[删除]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=691</guid>
		<description><![CDATA[http://perfectionkills.com/understanding-delete/
原文作者是在读《Object-Oriented Javascript》的Function章节后有感，写下的一篇博文。
书中有一句话：function其实就是一个普通变量——它可以被复制到另一个变量中而不会被删除。如果这样解释的话，这里有个例子：
//army注：这一源代码片段是在Firebug下的Console控制台运行的，放在页面中需改写
var sum = function(a, b) {return a + b;}
var add = sum;
delete sum;
true
typeof sum;
"undefined"
忽略代码中省略的两个分号，你注意到这个代码片段的问题了吗？按那句话所说，删除sum不应该成功；delete语句返回的值不可能是true；typeof sum的结果不会是&#8221;undefined&#8221;。可是事实却非如此，所有的这一切都是因为：Javascript中不可能删除变量。至少不能以这种方式删除。
 这是为什么呢？
要回答这个问题，我们需要理解delete操作符是怎么运作的：即什么可以删除、什么不可以删除、为什么？
原理
我们为什么可以这样删除对象的属性？
var o = {...]]></description>
			<content:encoded><![CDATA[<p><a href="http://perfectionkills.com/understanding-delete/">http://perfectionkills.com/understanding-delete/</a></p>
<p>原文作者是在读<a href="http://www.amazon.com/Object-Oriented-JavaScript-high-quality-applications-libraries/dp/1847194141" target="_blank">《Object-Oriented Javascript》</a>的Function章节后有感，写下的一篇博文。</p>
<p>书中有一句话：function其实就是一个普通变量——它可以被复制到另一个变量中而不会被删除。如果这样解释的话，这里有个例子：</p>
<pre class="brush:js">//army注：这一源代码片段是在Firebug下的Console控制台运行的，放在页面中需改写
var sum = function(a, b) {return a + b;}
var add = sum;
delete sum;
true
typeof sum;
"undefined"</pre>
<p>忽略代码中省略的两个分号，你注意到这个代码片段的问题了吗？按那句话所说，删除sum不应该成功；delete语句返回的值不可能是true；typeof sum的结果不会是&#8221;undefined&#8221;。可是事实却非如此，所有的这一切都是因为：<strong>Javascript中不可能删除变量。至少不能以这种方式删除。</strong></p>
<p><strong> </strong>这是为什么呢？</p>
<p>要回答这个问题，我们需要理解delete操作符是怎么运作的：即什么可以删除、什么不可以删除、为什么？</p>
<h3>原理</h3>
<p>我们为什么可以这样删除对象的属性？</p>
<pre class="brush:js">var o = { x: 1 };
delete o.x; // true
o.x; // undefined</pre>
<p>但是变量就完全不同了：</p>
<pre class="brush:js">var x = 1;
delete x; // false
x; // 1</pre>
<p>方法声明也是：</p>
<pre class="brush:js">function x(){}
delete x; // false
typeof x; // "function"</pre>
<p>注意当一个属性<strong>不能被删除时</strong>，它会返回false。</p>
<p>要解释这些，我们首先要理解变量的实例化以及属性的特性——很遗憾，这些很少在Javascript书籍中被提到。下面这节将做个简明的介绍，它很容易理解。如果你不关心<strong>一切为何按照它目前的样子进行工作</strong>的话，可以跳过此节。</p>
<p><strong>代码类型</strong></p>
<p><strong></strong>在ECMAScript中共有3种执行方式：Global、Function、Eval。这些类型某种程度上正如其名，但还不足：</p>
<p>当一份源代码作为程序运行时，它在全局作用域内执行，就会被认为是Global代码。在浏览器环境中，script标签通常被解析为一段程序，因此它是全局代码。</p>
<p>直接运行在function内的代码显然是Function代码。例如浏览器中的&lt;p onclick=&#8221;&#8230;&#8221;&gt;。</p>
<p>另外，作为参数传入evel函数的源代码字符串会以Eval方式执行。我们很快就会看到其特殊性。</p>
<p><strong> 运行上下文</strong></p>
<p><strong></strong>当ECMAScript代码运行时，它是发生在一个运行上下文（execution context）中的。运行上下文是个抽象实体，可以帮助我们理解作用域和变量实例化是如何工作的。这3种代码各自有自己的运行上下文。当一个function执行时，就可以说控制器进入Function运行上下文中；当全局代码执行时，就进入了Global运行上下文。</p>
<p>如你所知，运行上下文可以逻辑递归。首先一段代码肯定有自己的Global运行上下文；如果调用了function，那么function也有自己的运行上下文；如果function又调用了另外一个function……如此递归。甚至function调用了它本身，每次也会产生一个新的运行上下文。</p>
<p><strong>激活变量对象/激活对象</strong></p>
<p><strong></strong>每一个运行上下文都和一个变量对象（Variable Object）相关联。类似运行上下文，变量对象是一个抽象实体，一种描述变量实例化的机制。现在有趣的事情来了，源代码中的变量和函数声明，实际上<strong>成为这个变量对象上的一组属性。</strong></p>
<p><strong></strong>当控制器进入Global方式的运行上下文时，一个全局对象被创建用来作为变量对象。这就解释了为何定义的全局方法成为了全局对象的属性。</p>
<pre class="brush:js">/* remember that `this` refers to global object when in global scope */
var GLOBAL_OBJECT = this;

var foo = 1;
GLOBAL_OBJECT.foo; // 1
foo === GLOBAL_OBJECT.foo; // true

function bar(){}
typeof GLOBAL_OBJECT.bar; // "function"
GLOBAL_OBJECT.bar === bar; // true</pre>
<p>ok，当全局变量成为全局对象的属性时，方法内的局部变量发生了什么事情呢？行为是类似的：它们变成了一个变量对象的一组属性。唯一的不同之处在于，此处的变量对象不是全局对象，但仍然是个激活对象。激活对象在每次进入方法创建运行上下文时都会被创建。</p>
<p>不仅function内的变量和方法声明会成为激活对象的属性，相同的事情还发生在传入的function参数身上，它在arguments下产生了一个Arguments对象。注意激活对象是内部机制，不能够被程序直接访问。</p>
<pre class="brush:js">(function(foo){

	var bar = 2;
	function baz(){}

	/*
	In abstract terms,

	Special `arguments` object becomes a property of containing function's Activation object:
	ACTIVATION_OBJECT.arguments; // Arguments object

	...as well as argument `foo`:
	ACTIVATION_OBJECT.foo; // 1

	...as well as variable `bar`:
	ACTIVATION_OBJECT.bar; // 2

	...as well as function declared locally:
	typeof ACTIVATION_OBJECT.baz; // "function"
	*/

})(1);</pre>
<p>最后，eval中的变量声明会作为<strong>它被引用的上下文的变量对象的属性</strong>。eval代码只是使用它被调用的运行环境的变量对象。</p>
<pre class="brush:js">var GLOBAL_OBJECT = this;

/* `foo` is created as a property of calling context Variable object,
which in this case is a Global object */

eval('var foo = 1;');
GLOBAL_OBJECT.foo; // 1

(function(){

	/* `bar` is created as a property of calling context Variable object,
	which in this case is an Activation object of containing function */

	eval('var bar = 1;');

	/*
	In abstract terms,
	ACTIVATION_OBJECT.bar; // 1
	*/

})();</pre>
<p><strong>属性特性</strong></p>
<p><strong></strong>终于说到这里了。现在我们已经清楚变量声明时发生的事情，剩下就是理解属性特性。每个属性可以有任意个以下特性——只读、不可枚举、不可删除、内部的。你可以把它们理解为属性上的选项——有或者没有。因为今天讨论的目的，这里只说不可删除特性。</p>
<p>当声明变量或者函数时，它们成为变量对象（对于function代码来说是激活对象，对于全局代码来说是全局对象）的属性，这些属性产生的同时具有不可删除特性。然而，任何直接或者内部的属性分配在创建属性时都没有不可删除特性。这就是为什么有的属性可以删除有的不可以。</p>
<pre class="brush:js">var GLOBAL_OBJECT = this;

/*  `foo` is a property of a Global object.
It is created via variable declaration and so has DontDelete attribute.
This is why it can not be deleted. */

var foo = 1;
delete foo; // false
typeof foo; // "number"

/*  `bar` is a property of a Global object.
It is created via function declaration and so has DontDelete attribute.
This is why it can not be deleted either. */

function bar(){}
delete bar; // false
typeof bar; // "function"

/*  `baz` is also a property of a Global object.
However, it is created via property assignment and so has no DontDelete attribute.
This is why it can be deleted. */

GLOBAL_OBJECT.baz = 'blah';
delete GLOBAL_OBJECT.baz; // true
typeof GLOBAL_OBJECT.baz; // "undefined"</pre>
<p><strong>内建和不可删除</strong></p>
<p><strong></strong>一个属性上的特殊特性控制了它能否被删除。注意一些内建的的属性一开始就有不可删除特性，它们是永远不能被删除的。例如arguments、函数实例的length属性等。</p>
<pre class="brush:js">(function(){

	/* can't delete `arguments`, since it has DontDelete */

	delete arguments; // false
	typeof arguments; // "object"

	/* can't delete function's `length`; it also has DontDelete */

	function f(){}
	delete f.length; // false
	typeof f.length; // "number"

})();</pre>
<p>函数的形参同样也有不可删除特性：</p>
<pre class="brush:js">(function(foo, bar){

	delete foo; // false
	foo; // 1

	delete bar; // false
	bar; // 'blah'

})(1, 'blah');</pre>
<p><strong>未声明的分配</strong></p>
<p><strong></strong>未声明的变量会在全局对象上生成属性，除非在作用域链中于全局对象之前找到这个变量声明。现在我们知道了属性分配和变量声明之间的区别——后者具有不可删除特性而前者没有——这就解释了为何未经声明的变量可以被删除。</p>
<pre class="brush:js">var GLOBAL_OBJECT = this;

/* create global property via variable declaration; property has DontDelete */
var foo = 1;

/* create global property via undeclared assignment; property has no DontDelete */
bar = 2;

delete foo; // false
typeof foo; // "number"

delete bar; // true
typeof bar; // "undefined"</pre>
<p>注意在属性创建时特性已经被定义了（除了ie），再次分配的话并不会修改已经存在的特性。</p>
<pre class="brush:js">/* `foo` is created as a property with DontDelete */
function foo(){}

/* Later assignments do not modify attributes. DontDelete is still there! */
foo = 1;
delete foo; // false
typeof foo; // "number"

/* But assigning to a property that doesn't exist,
creates that property with empty attributes (and so without DontDelete) */

this.bar = 1;
delete bar; // true
typeof bar; // "undefined"</pre>
<h3>Firebug的困惑</h3>
<p>那么在Firebug中发生了什么？为何在控制台console中的声明变量可以被删除？还记得前面说的吗，当eval代码进行变量声明时有个特殊的行为，这些声明的变量没有不可删除特性，所以它们可以被删除。</p>
<p><strong>通过eval删除变量</strong></p>
<p><strong></strong>由于eval行为的特殊性，再加上ECMAScript允许我们删除没有不可删除特性的属性，在同一运行上下文中函数声明可以覆盖已经声明的变量。</p>
<pre class="brush:js">function x(){ }
var x;
typeof x; // "function"</pre>
<p>注意函数声明是如何获得优先权并覆盖掉原有变量声明的，这是因为函数声明的实例化晚于变量声明的实例化，并且允许覆盖。不仅如此，它还会覆盖属性特性。假如我们通过eval方式来声明函数，也会这样。</p>
<pre class="brush:js">var x = 1;

/* Can't delete, `x` has DontDelete */

delete x; // false
typeof x; // "number"

eval('function x(){}');

/* `x` property now references function, and should have no DontDelete */

typeof x; // "function"
delete x; // should be `true`
typeof x; // should be "undefined"</pre>
<p>不幸的是，我在尝试这段代码的时候失败了，可能哪里出了点错误。</p>
<h3>浏览器的让步</h3>
<p>原理已然弄清，实践更为重要。浏览器们都按照标准做了吗？答案是：大部分是。</p>
<p>我对以下现代浏览器做了一个测试：Opera 7.54+、Firefox 1.0+、Safari 3.1.2+、Chrome 4+。<br />
Safari 2.x和3.0.4在删除函数参数时有问题，这些属性看上去没有不可删除特性，因此它们竟被删除了。Safari 2.x的问题还不止这些——删除没有引用的变量（如delete 1，army注：我感觉这应该是常量才对）会抛出异常；函数声明具有可删除特性（变量声明没有）；eval中的变量声明有不可删除特性（函数声明没有）。</p>
<p>类似的，Konqueror（3.5而非4.3）在删除没有引用的变量的时候会抛出异常，并且允许删除函数参数。</p>
<p><strong>Gecko的不可删除特性bug</strong></p>
<p><strong></strong>基于Gecko 1.8.x的浏览器——Firefox 2.x、Camino 1.x、Seamonkey 1.x等等——有个很有趣的bug：可以删除一个属性的不可删除特性，尽管这个属性是通过变量声明或者函数声明而产生的。</p>
<pre class="brush:js">function foo(){}
delete foo; // false (as expected)
typeof foo; // "function" (as expected)

/* now assign to a property explicitly */

this.foo = 1; // erroneously clears DontDelete attribute
delete foo; // true
typeof foo; // "undefined"

/* note that this doesn't happen when assigning property implicitly */

function bar(){}
bar = 1;
delete bar; // false
typeof bar; // "number" (although assignment replaced property)</pre>
<p>让人震惊的是，IE5.5到IE8居然全部通过了测试！除了那个删除没有引用的变量外。然而即使如此，IE却在另外的方面有着严重的bug！这些bug很隐晦，和全局变量有关。</p>
<h3>IE的bug</h3>
<p>在IE中（至少是6到8版本），下面的表达式会抛出异常。</p>
<pre class="brush:js">this.x = 1;
delete x; // TypeError: Object doesn't support this action</pre>
<p>再来看看另外一个，非常有趣：</p>
<pre class="brush:js">var x = 1;
delete this.x; // TypeError: Cannot delete 'this.x'</pre>
<p>这个情况看上去好像全局代码上的变量声明没有在全局对象上产生属性，甚至删除操作时会抛出异常。</p>
<pre class="brush:js">this.x = 1;

delete this.x; // TypeError: Object doesn't support this action
typeof x; // "number" (still exists, wasn't deleted as it should have been!)

delete x; // TypeError: Object doesn't support this action
typeof x; // "number" (wasn't deleted again)</pre>
<p>与之相反的是，未声明变量却具有可删除特性。</p>
<pre class="brush:js">x = 1;
delete x; // true
typeof x; // "undefined"</pre>
<p>但是你若通过this来引用全局的话却又会异常：</p>
<pre class="brush:js">x = 1;
delete this.x; // TypeError: Cannot delete 'this.x'</pre>
<p>如果概括一下的话，那就是在全局代码中delete this.x从未成功过。</p>
<p>我在2009年9月发现了的这一问题，Garrett Smith回答我说“IE中全局变量对象被实现为一个JScript对象，全局对象却被实现为一个宿主对象（host object）”。（army注：延伸阅读hax的文章：<a href="http://hax.javaeye.com/blog/349569" target="_blank">http://hax.javaeye.com/blog/349569</a>）</p>
<p>我们可以通过一些测试在某种程度上验证这一回答。注意this和window看上去引用了同一对象（如果===全等操作符可以信赖的话），但是变量对象（函数返回的值）却和this不同。</p>
<pre class="brush:js">/* in Global code */
function getBase(){ return this; }

getBase() === this.getBase(); // false
this.getBase() === this.getBase(); // true
window.getBase() === this.getBase(); // true
window.getBase() === getBase(); // false</pre>
<h3>删除和宿主对象</h3>
<p>关于delete操作的规则如下：</p>
<ul>
<li>如果操作数中没有引用值，返回true</li>
<li>如果对象没有直接属性，返回true</li>
<li>如果属性存在并且具有不可删除特性，返回false</li>
<li>其它情况，移除属性并且返回true</li>
</ul>
<p>然而，IE环境的delete操作在删除宿主对象时却经常让人捉摸不透。</p>
<pre class="brush:js">/* "alert" is a direct property of `window` (if we were to believe `hasOwnProperty`) */
window.hasOwnProperty('alert'); // true

delete window.alert; // true
typeof window.alert; // "function"</pre>
<p>最好的习惯是从不信任宿主对象。</p>
<h3>ES5严格模式</h3>
<p>ECMAScript 5的严格模式带来了哪些条规？它引进了诸多限制。比如删除引用变量、函数参数和标志符的时候会抛出SyntaxError。另外，如果属性包含[[Configurable]] == false键值对，删除时会抛出TypeError。</p>
<pre class="brush:js">(function(foo){

"use strict"; // enable strict mode within this function

var bar;
function baz(){}

delete foo; // SyntaxError (when deleting argument)
delete bar; // SyntaxError (when deleting variable)
delete baz; // SyntaxError (when deleting variable created with function declaration)

/* `length` of function instances has { [[Configurable]] : false } */

delete (function(){}).length; // TypeError

})();</pre>
<p>还有，删除未声明变量时也会抛出SyntaxError：</p>
<pre class="brush:js">"use strict";
delete i_dont_exist; // SyntaxError</pre>
<p>这和在严格模式中使用未声明的变量类似：</p>
<pre class="brush:js">"use strict";
i_dont_exist = 1; // ReferenceError</pre>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/03/05/691.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>[转]Google Nexus One上的Flash Player 10.1演示</title>
		<link>http://army8735.org/2010/02/25/689.html</link>
		<comments>http://army8735.org/2010/02/25/689.html#comments</comments>
		<pubDate>Thu, 25 Feb 2010 03:56:24 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[as、flex]]></category>
		<category><![CDATA[flash player 10.1]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=689</guid>
		<description><![CDATA[在7yue的blog上看到fp10.1的表现让人兴奋不已，希望Adobe摆脱“很有压力，才有推力”的形象……
http://www.7yue.com/post/430.html
]]></description>
			<content:encoded><![CDATA[<p>在7yue的blog上看到fp10.1的表现让人兴奋不已，希望Adobe摆脱“很有压力，才有推力”的形象……</p>
<p><a href="http://www.7yue.com/post/430.html">http://www.7yue.com/post/430.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/02/25/689.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>web端语法高亮原理：走进jssc的世界（五）</title>
		<link>http://army8735.org/2010/02/23/669.html</link>
		<comments>http://army8735.org/2010/02/23/669.html#comments</comments>
		<pubDate>Tue, 23 Feb 2010 06:45:47 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[jssc]]></category>
		<category><![CDATA[系列文章]]></category>
		<category><![CDATA[语法高亮原理]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=669</guid>
		<description><![CDATA[整体流程
终于说到整体流程上了。之前的文章一直在解说as中如何来做词法分析，js方面丝毫未提，更没说jssc到底在页面上是怎么工作的。现在，就来详细地解释一下。
环境需求：

现代浏览器（废话）
装有Adobe Flash Player插件，版本号为9或9以上
javascript启用
页面中放入jssc.swf文件

大致流程：

swf文件载入完毕
内嵌在swf中的js首先被执行，寻找页面中所有符合规则的pre节点
顺序遍历这些pre节点，提取每个pre节点的文本内容（即原始代码）
将原始代码传递给swf
swf对传递来的原始代码进行词法分析，并生成一段结果html片段（即一串li节点）
将结果传递回js
js对结果进行包装（一串li节点放入ol节点中，以及标题头、边距和复制等等）
将对应的原始pre节点隐藏（display:none）
将新生成的内容插入原始pre节点的前面

可以看出，js和as耦合的地方在哪里，以及它们之间是如何相互合作的。下面将对以上9个步骤细细说来，希望更多的开发者能提出改进意见～
1.载入swf
这里没什么好说的，在页面底部加入flash标签即可。值得注意的是不同浏览器中标签有所区别，理想情况下可以使用swfobject来插入标准的html标签。不过为了兼容以及简单，一般我是这样做的：
&#60;object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" id="jssc5" style="position:absolute;visibility:hidden;"&#62;
	&#60;param name="movie" value="jssc5.swf"/&#62;
	&#60;param name="flashvars" value="find=brush"/&#62;
	&#60;embed src="jssc5.swf" flashvars="find=brush" name="jssc5" type="application/x-shockwave-flash" style="position:absolute;visibility:hidden;"/&#62;
&#60;/object&#62;
这里面有许多可配置的地方。
object的id和embed的name必须一致且唯一（应该是唯二才对），为了满足不同浏览器的需要。我设为jssc5，这也是默认值。如果和页面上某个节点有冲突需要修改，那么就需要传入参数修改配置。比如说变成jssc5_modify，就得在flashvars中这样做：
&#60;object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" id="jssc5_modify" style="position:absolute;visibility:hidden;"&#62;
	&#60;param name="movie" value="jssc5.swf"/&#62;
	&#60;param...]]></description>
			<content:encoded><![CDATA[<h3>整体流程</h3>
<p>终于说到整体流程上了。之前的文章一直在解说as中如何来做词法分析，js方面丝毫未提，更没说jssc到底在页面上是怎么工作的。现在，就来详细地解释一下。</p>
<p>环境需求：</p>
<ol>
<li>现代浏览器（废话）</li>
<li>装有Adobe Flash Player插件，版本号为9或9以上</li>
<li>javascript启用</li>
<li>页面中放入jssc.swf文件</li>
</ol>
<p>大致流程：</p>
<ol>
<li>swf文件载入完毕</li>
<li>内嵌在swf中的js首先被执行，寻找页面中所有符合规则的pre节点</li>
<li>顺序遍历这些pre节点，提取每个pre节点的文本内容（即原始代码）</li>
<li>将原始代码传递给swf</li>
<li>swf对传递来的原始代码进行词法分析，并生成一段结果html片段（即一串li节点）</li>
<li>将结果传递回js</li>
<li>js对结果进行包装（一串li节点放入ol节点中，以及标题头、边距和复制等等）</li>
<li>将对应的原始pre节点隐藏（display:none）</li>
<li>将新生成的内容插入原始pre节点的前面</li>
</ol>
<p>可以看出，js和as耦合的地方在哪里，以及它们之间是如何相互合作的。下面将对以上9个步骤细细说来，希望更多的开发者能提出改进意见～</p>
<h3>1.载入swf</h3>
<p>这里没什么好说的，在页面底部加入flash标签即可。值得注意的是不同浏览器中标签有所区别，理想情况下可以使用swfobject来插入标准的html标签。不过为了兼容以及简单，一般我是这样做的：</p>
<pre class="brush:html">&lt;object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" id="jssc5" style="position:absolute;visibility:hidden;"&gt;
	&lt;param name="movie" value="jssc5.swf"/&gt;
	&lt;param name="flashvars" value="find=brush"/&gt;
	&lt;embed src="jssc5.swf" flashvars="find=brush" name="jssc5" type="application/x-shockwave-flash" style="position:absolute;visibility:hidden;"/&gt;
&lt;/object&gt;</pre>
<p>这里面有许多可配置的地方。</p>
<p><span style="color: #ff0000;">object</span>的<span style="color: #ff0000;">id</span>和<span style="color: #ff0000;">embed</span>的<span style="color: #ff0000;">name</span>必须一致且唯一（应该是唯二才对），为了满足不同浏览器的需要。我设为<span style="color: #ff0000;">jssc5</span>，这也是默认值。如果和页面上某个节点有冲突需要修改，那么就需要传入参数修改配置。比如说变成<span style="color: #ff0000;">jssc5_modify</span>，就得在flashvars中这样做：</p>
<pre class="brush:html">&lt;object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" id="jssc5_modify" style="position:absolute;visibility:hidden;"&gt;
	&lt;param name="movie" value="jssc5.swf"/&gt;
	&lt;param name="flashvars" value="find=brush&amp;swf=jssc5_modify"/&gt;
	&lt;embed src="jssc5.swf" flashvars="find=brush&amp;swf=jssc5_modify" name="jssc5_modify" type="application/x-shockwave-flash" style="position:absolute;visibility:hidden;"/&gt;
&lt;/object&gt;</pre>
<p>注意到param标签和embed标签中都得改，为了满足浏览器兼容需要……</p>
<p>同样，jssc会占用一个全局js变量名<span style="color: #ff0000;">jssc</span>（注意这个jssc是指变量名字符串，而前者是指软件），这是为了在js和as之间互相调用通信必须的。如果这个变量名和页面上的js变量有冲突，那么就会造成错误。所以它也可以进行修改配置，假如要改成<span style="color: #ff0000;">jssc_js</span>，就得在flashvars中这么做：</p>
<pre class="brush:html">&lt;object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" id="jssc5_modify" style="position:absolute;visibility:hidden;"&gt;
	&lt;param name="movie" value="jssc5.swf"/&gt;
	&lt;param name="flashvars" value="find=brush&amp;swf=jssc5_modify&amp;js=jssc_js"/&gt;
	&lt;embed src="jssc5.swf" flashvars="find=brush&amp;swf=jssc5_modify&amp;js=jssc_js" name="jssc5_modify" type="application/x-shockwave-flash" style="position:absolute;visibility:hidden;"/&gt;
&lt;/object&gt;</pre>
<p>最后，还有两个可以配置的参数：<span style="color: #ff0000;">css</span>和<span style="color: #ff0000;">url</span>。前者是指新生成的结果节点的class名称，你可以用firebug看到默认的亦是<span style="color: #ff0000;">jssc</span>；后者是指定swf的路径，这是为了在非ie浏览器下做copy使用（ie可以直接调用api做复制功能），从这个意义上说，整个swf文件其实是有两种功能——词法分析和复制按钮。这么做是为了减少http请求，同时js融合在as里也是为了减少http请求。</p>
<h3>2.内嵌的js执行</h3>
<p>js首先会寻找页面上所有符合规则的pre节点，那么是什么规则的？默认情况下，pre的class名称如果这样开头，js就会认为它是正确的（其实并不一定要求是开头，出现在其它位置也可以，但为了良好的习惯推荐如此）：</p>
<pre class="brush:html">&lt;pre class="brush:html"&gt;&lt;/pre&gt;</pre>
<p>这段被高亮的代码其实就是它本身。<span style="color: #ff0000;">brush</span>是<strong>键名</strong>，<span style="color: #ff0000;">html</span>是<strong>键值</strong>。假如想要高亮一段js代码，那么就需要更改键值：</p>
<pre class="brush:html">&lt;pre class="brush:js"&gt;&lt;/pre&gt;</pre>
<p>其它语言道理相同。另外，可以高亮的代码必须是jssc本身已经支持的语法种类，不支持的话也会被格式化，但没有高亮效果。</p>
<p>细心的人可能已经发现，<span style="color: #ff0000;">brush</span>键名和最开始那段代码中出现的相一致。没错，假如pre节点的class名称也有冲突，不能使用<span style="color: #ff0000;">brush</span>，要改成<span style="color: #ff0000;">brush_modify</span>，那么就要如此修改配置：</p>
<pre class="brush:html">&lt;object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" id="jssc5_modify" style="position:absolute;visibility:hidden;"&gt;
	&lt;param name="movie" value="jssc5.swf"/&gt;
	&lt;param name="flashvars" value="find=brush_modify&amp;swf=jssc5_modify&amp;js=jssc_js"/&gt;
	&lt;embed src="jssc5.swf" flashvars="find=brush_modify&amp;swf=jssc5_modify&amp;js=jssc_js" name="jssc5_modify" type="application/x-shockwave-flash" style="position:absolute;visibility:hidden;"/&gt;
&lt;/object&gt;</pre>
<p>注意param和embed中要修改2次。</p>
<p>在找到所有符合规则的pre节点之后，js将这些节点的引用存储下来，然后一一遍历它们。下面的经过就是每次遍历一个pre节点时所发生的。</p>
<h3>3.遍历提取内容</h3>
<p>提取pre节点的内容很简单，即我们熟知的<span style="color: #ff0000;">innerText</span>。不过在不同浏览器中也有让人头疼的地方。以前在做jssc4的时候，firefox2版本的<span style="color: #ff0000;">innerText</span>容量有限制，如果代码过多，提取的内容就不完整。另外也有低版本浏览器不支持<span style="color: #ff0000;">innerText</span>的可能性发生。所以综合考虑我写了个如下的js方法：</p>
<pre class="brush:js">function getText(node) {
	var code = node.textContent || node.innerText;
	if(!code &amp;&amp; node.firstChild) {
		code = node.firstChild.nodeValue;
	}
	return code || "";
}</pre>
<h3>4.传递内容给as</h3>
<p>这是as定义好的接口，js和as可以通过<span style="color: #ff0000;">ExternalInterface</span>类来实现互调。不再多说。</p>
<h3>5.as做词法分析</h3>
<p>这是前几篇一直在说的内容，不再多说。</p>
<h3>6.传递结果给js</h3>
<p>同第4点。</p>
<h3>7.js包装结果</h3>
<p>生成的结果其实是一串<span style="color: #ff0000;">li</span>节点，js要做的就是创建一个<span style="color: #ff0000;">ol</span>节点，然后设置<span style="color: #ff0000;">innerHTML</span>为传递来的结果字符串。如此最基本的高亮代码就完成了。之后是设置标题、起始行数、边距、复制功能和鼠标移入高亮当前行背景。</p>
<p>其中边距是指ol节点的<span style="color: #ff0000;">paddingLeft</span>值，因为代码行数不一样，所以左边距一定是根据行数动态进行计算的。在js中我的公式如下：</p>
<pre class="brush:js">oOl.style.paddingLeft = Math.max((line.length + 2) * 9, 30) + "px";</pre>
<h3>8.隐藏pre节点</h3>
<p>最后是将本次循环的这个pre节点隐藏。注意，这里是隐藏不是删除！因为后面还要用到代码复制功能，所以只能隐藏不能删除！</p>
<h3>9.插入结果</h3>
<p>将包装好的结果插入本次循环pre节点的前面，这轮循环就算走完了，再继续下一轮循环。</p>
<h3>结束语</h3>
<p>好了，整个系列文章算结束了。欢迎大家提出好的建议和想法，或者直接参与到开发当中来，亦或以别的方式来参与（比如说做wordpress的插件）。如果你能做出比jssc更好的高亮插件，那么我也会放弃jssc，投入新的、更好的项目中去的——这话说的好假：）</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/02/23/669.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>js和swf交互时不同浏览器中的顺序</title>
		<link>http://army8735.org/2010/02/20/649.html</link>
		<comments>http://army8735.org/2010/02/20/649.html#comments</comments>
		<pubDate>Sat, 20 Feb 2010 09:44:51 +0000</pubDate>
		<dc:creator>army</dc:creator>
				<category><![CDATA[JAcld]]></category>
		<category><![CDATA[前端开发]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=649</guid>
		<description><![CDATA[接上篇，继续做的测试，有了点新发现。解说测试之前，先说明下用flash检测大写锁定键是否开启的原理。
前提要求是：普通的input文本输入框（无论是text还是password），用户输入还是以html的input组件为基础，并非在其上加入透明的flash输入框或者其它技术平台的组件；无干扰性，用户输入没有任何其它干扰的感觉。
以此，唯一的解决办法就是js结合swf来控制。input的各种事件侦听（获得焦点、失去焦点、按键触发）都只能由js来完成，而对当前键盘状态的检测则交给flash来处理。它们之间有个很大的障碍——flash对当前键盘状态的识别必须首先获得焦点，而我们在input中输入内容的时候焦点却在input上，这成为最大的矛盾。
利用js控制页面焦点可以解决这一问题，除了webkit内核的浏览器。我们可以写出如下的伪代码：
oInput.onfocus = function() {
	oSwf.focus(); //flash获得焦点
	oSwf.detect(); //flash进行检测，并根据检测结果调用某个js函数
	setTimeout(function() {
		oInput.focus(); //input重新获得焦点，无干扰用户输入
	}, 0);
}
你可能困扰为何要用setTimeout来使input重新获得焦点，这是为了让用户输入时感觉不到焦点在input和flash之间走了一圈，就好象什么事都没有发生一样。至于为什么一定要用setTimeout，那是为了模拟线程同步，确保重新获得焦点这一操作发生在最后。
就几行代码看上去很简单，实际上有几个隐蔽的信息藏在其中。在flash获得焦点并进行检测之后，input重新获得焦点，试问：这样会不会再次触发onfocus？答案是肯定的，如果不做任何处理的话，这将是个死循环；另外，input还应该侦听onblur，没道理用户在离开输入框之后还在进行提示（除非你能忍受），那么onblur这个侦听函数也会进行不断重复；更恐怖的在下面，我为每一步标上了序号，以表示它们的执行步骤：
function result() {
	//1
	//调用的结果函数，用以让提示区域显示或隐藏
}
oInput.onfocus = function() {
	//2
	oSwf.focus(); //flash获得焦点
	oSwf.detect(); //flash进行检测，并根据检测结果调用结果函数
	//3
	setTimeout(function() {
		//4
		oInput.focus(); //input重新获得焦点，无干扰用户输入
	}, 0);
	//5
}
oInput.onblur =...]]></description>
			<content:encoded><![CDATA[<p><a href="/2010/02/20/646.html" target="_blank">接上篇</a>，继续做的测试，有了点新发现。解说测试之前，先说明下用flash检测大写锁定键是否开启的原理。</p>
<p>前提要求是：普通的input文本输入框（无论是text还是password），用户输入还是以html的input组件为基础，并非在其上加入透明的flash输入框或者其它技术平台的组件；无干扰性，用户输入没有任何其它干扰的感觉。</p>
<p>以此，唯一的解决办法就是js结合swf来控制。input的各种事件侦听（获得焦点、失去焦点、按键触发）都只能由js来完成，而对当前键盘状态的检测则交给flash来处理。它们之间有个很大的障碍——flash对当前键盘状态的识别必须首先获得焦点，而我们在input中输入内容的时候焦点却在input上，这成为<strong>最大的矛盾</strong>。</p>
<p>利用js控制页面焦点可以解决这一问题，除了webkit内核的浏览器。我们可以写出如下的伪代码：</p>
<pre class="brush:js">oInput.onfocus = function() {
	oSwf.focus(); //flash获得焦点
	oSwf.detect(); //flash进行检测，并根据检测结果调用某个js函数
	setTimeout(function() {
		oInput.focus(); //input重新获得焦点，无干扰用户输入
	}, 0);
}</pre>
<p>你可能困扰为何要用<strong>setTimeout</strong>来使input重新获得焦点，这是为了让用户输入时感觉不到焦点在input和flash之间走了一圈，就好象什么事都没有发生一样。至于为什么一定要用setTimeout，那是为了模拟线程同步，确保重新获得焦点这一操作发生在最后。</p>
<p>就几行代码看上去很简单，实际上有几个隐蔽的信息藏在其中。在flash获得焦点并进行检测之后，input重新获得焦点，试问：这样会不会再次触发onfocus？答案是肯定的，如果不做任何处理的话，这将是个死循环；另外，input还应该侦听onblur，没道理用户在离开输入框之后还在进行提示（除非你能忍受），那么onblur这个侦听函数也会进行不断重复；更恐怖的在下面，我为每一步标上了序号，以表示它们的执行步骤：</p>
<pre class="brush:js">function result() {
	//1
	//调用的结果函数，用以让提示区域显示或隐藏
}
oInput.onfocus = function() {
	//2
	oSwf.focus(); //flash获得焦点
	oSwf.detect(); //flash进行检测，并根据检测结果调用结果函数
	//3
	setTimeout(function() {
		//4
		oInput.focus(); //input重新获得焦点，无干扰用户输入
	}, 0);
	//5
}
oInput.onblur = function() {
	//6
	//失去焦点后该怎么做，略
}</pre>
<p>猜一猜这个步骤顺序是多少？你永远也答不对，因为即使在<span style="color: #ff0000;">trident</span>（ie为代表）和<span style="color: #ff0000;">gecko</span>（firefox为代表）两种内核下，它们也不相同（别提webkit了，搞定那个bug先）。</p>
<p>在trident中，input被用户激活后（比如说用鼠标点击），先触发focus，因此2会先执行；接下来flash获得焦点，再调用结果方法，1会被执行；继续回到onfocus方法，顺序走完3和5；onblur被触发，执行一下6；最后是setTimeout那里的4。</p>
<p>很奇怪的现象，因为在2时，input失去焦点而swf获得焦点，此时并没有触发onblur去执行6，反而是先执行的1；之后还有没执行完的3和5；在接下来才能轮到6。由此我们可以观察出一些trident内核的优先级顺序设计的端倪。</p>
<p>再来看看gecko，input被激活后，依然会先触发focus，2第一个执行；接下来也是flash获得焦点，不过在这之前input要先失去焦点，因此先触发了onblur，执行了6；然后flash进行判断，并调用结果方法，执行了1；再往后就类似了，3和5搞定后setTimeout的4最后。</p>
<p>探索完原理后，才有可能继续开发。我也只“刨根问底”到这个程度上，欢迎有兴趣的同学继续“往祖坟上刨”，彻底翻出来赤裸裸的现实。后续如果有新的进展，我会继续把它写出来。：）</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/02/20/649.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>webkit内核浏览器中swf获取焦点的bug</title>
		<link>http://army8735.org/2010/02/20/646.html</link>
		<comments>http://army8735.org/2010/02/20/646.html#comments</comments>
		<pubDate>Sat, 20 Feb 2010 04:17:42 +0000</pubDate>
		<dc:creator>army</dc:creator>
				<category><![CDATA[JAcld]]></category>
		<category><![CDATA[前端开发]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=646</guid>
		<description><![CDATA[http://army8735.org/2009/09/15/82.html
曾经考虑过用swf来检测密码输入框大写锁定键是否被打开，并且也进行了一个小实验，基本是成功的。时隔多日，最近想要把它完善一下，突然发现在chrome中根本不起作用！
一步步排查下来，终于发现在webkit内核的浏览器下，以往的使得swf获取焦点的方法根本不起作用。随之google一番，发现老外早已发现这个bug，并且目前webkit仍未解决：
http://stackoverflow.com/questions/594821/object-focus-problem-with-safari-and-chrome-browsers
这样就影响到了部分功能：比如想使用flash的检测按键功能，但它必须要求flash是在获得焦点的情况下才行。使用js可以在ie和gecko内核下轻松办到，webkit只能继续等待了。
]]></description>
			<content:encoded><![CDATA[<p><a href="http://army8735.org/2009/09/15/82.html">http://army8735.org/2009/09/15/82.html</a></p>
<p>曾经考虑过用swf来检测密码输入框大写锁定键是否被打开，并且也进行了一个小实验，基本是成功的。时隔多日，最近想要把它完善一下，突然发现在chrome中根本不起作用！</p>
<p>一步步排查下来，终于发现在webkit内核的浏览器下，以往的使得swf获取焦点的方法根本不起作用。随之google一番，发现老外早已发现这个bug，并且目前webkit仍未解决：</p>
<p><a href="http://stackoverflow.com/questions/594821/object-focus-problem-with-safari-and-chrome-browsers">http://stackoverflow.com/questions/594821/object-focus-problem-with-safari-and-chrome-browsers</a></p>
<p>这样就影响到了部分功能：比如想使用flash的检测按键功能，但它必须要求flash是在获得焦点的情况下才行。使用js可以在ie和gecko内核下轻松办到，webkit只能继续等待了。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/02/20/646.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>web中的区域对齐</title>
		<link>http://army8735.org/2010/02/10/626.html</link>
		<comments>http://army8735.org/2010/02/10/626.html#comments</comments>
		<pubDate>Wed, 10 Feb 2010 03:59:43 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[as、flex]]></category>
		<category><![CDATA[前端开发]]></category>
		<category><![CDATA[StageAlign]]></category>
		<category><![CDATA[对齐]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=626</guid>
		<description><![CDATA[说到对齐方式，一般联想到的无非四种：左对齐、右对齐、上对齐和下对齐。通过其中的两两组合，可以衍生出：左上角对齐、左下角对齐、右上角对齐和右下角对齐。在css中这些类型很常见，比如绝对定位：
.left_top {
	position: absolute;
	left: 0;
	top: 0;
}
.left_bottom {
	position: absolute;
	left: 0;
	bottom: 0;
}
.right_top {
	position: absolute;
	right: 0;
	top: 0;
}
.right_bottom {
	position: absolute;
	right: 0;
	bottom: 0;
}
我们很容易用css来作出四个角对齐或者四个方向居中对齐的效果。但是在as当中，则有一点点麻烦了。
说到对齐之前，首先要接触到StageScaleMode这个常量，它被赋给stage.scaleMode，用以指定整个swf程序的缩放模式，默认的是NO_SCALE，即不缩放。详细示例如下图：

当然StageScaleMode还有保持高宽比的SHOW_ALL常量以及保持高宽比并可能进行裁剪的NO_BORDER常量。上面只例举了两种对立的情形。当涉及到StageAlign的时候，我们默认为StageScaleMode就是默认的NO_SCALE，这样对齐才有意义。
在css中，也经常会遇到类似的情况。一个div区域有自己的高宽，超出区域范围之外的用overflow来控制可见度。譬如是右上角对齐的css，例子是这样：
&#60;style&#62;
.outer{position:relative;width:100px;height:100px;border:1px solid #f00;overflow:hidden;}
.inner{position:absolute;top:0;right:0;width:200px;height:200px;background:#000;}
&#60;/style&#62;

&#60;div class="outer"&#62;
	&#60;div...]]></description>
			<content:encoded><![CDATA[<p>说到对齐方式，一般联想到的无非四种：左对齐、右对齐、上对齐和下对齐。通过其中的两两组合，可以衍生出：左上角对齐、左下角对齐、右上角对齐和右下角对齐。在css中这些类型很常见，比如绝对定位：</p>
<pre class="brush:css">.left_top {
	position: absolute;
	left: 0;
	top: 0;
}
.left_bottom {
	position: absolute;
	left: 0;
	bottom: 0;
}
.right_top {
	position: absolute;
	right: 0;
	top: 0;
}
.right_bottom {
	position: absolute;
	right: 0;
	bottom: 0;
}</pre>
<p>我们很容易用css来作出四个角对齐或者四个方向居中对齐的效果。但是在as当中，则有一点点麻烦了。</p>
<p>说到对齐之前，首先要接触到<strong>StageScaleMode</strong>这个常量，它被赋给<strong>stage.scaleMode</strong>，用以指定整个swf程序的缩放模式，默认的是<strong>NO_SCALE</strong>，即不缩放。详细示例如下图：</p>
<p><img class="aligncenter size-full wp-image-629" title="stagealign1" src="http://army8735.org/wp-content/uploads/2010/02/stagealign1.png" alt="stagealign1" width="400" height="400" /></p>
<p>当然<strong>StageScaleMode</strong>还有保持高宽比的<strong>SHOW_ALL</strong>常量以及保持高宽比并可能进行裁剪的<strong>NO_BORDER</strong>常量。上面只例举了两种对立的情形。当涉及到<strong>StageAlign</strong>的时候，我们默认为<strong>StageScaleMode</strong>就是默认的<strong>NO_SCALE</strong>，这样对齐才有意义。</p>
<p>在css中，也经常会遇到类似的情况。一个div区域有自己的高宽，超出区域范围之外的用<strong>overflow</strong>来控制可见度。譬如是右上角对齐的css，例子是这样：</p>
<pre class="brush:html;">&lt;style&gt;
.outer{position:relative;width:100px;height:100px;border:1px solid #f00;overflow:hidden;}
.inner{position:absolute;top:0;right:0;width:200px;height:200px;background:#000;}
&lt;/style&gt;

&lt;div class="outer"&gt;
	&lt;div class="inner"&gt;&lt;/div&gt;
&lt;/div&gt;</pre>
<p>在as当中，想要做到同样的效果的话，可以借助<strong>StageAlign</strong>这个常量，它被赋给<strong>stage.align</strong>属性，所起的作用就和上面说的css对齐方式一样。同样是右上角对齐，我们只要在文档类中声明一下即可达到效果：</p>
<pre class="brush:as3">package {
	import flash.display.*;

	public class Main extends Sprite {
		public function Main():void {
			stage.align = StageAlign.TOP_RIGHT;
		}
	}
}</pre>
<p>用图片做示例，页面嵌入一个原始大小为50*50的swf文件，假如外部容器（object标签或者emebed标签）的高宽值被改变，swf仍然是右上角对齐：</p>
<p><img class="aligncenter size-full wp-image-642" title="stagealign2" src="http://army8735.org/wp-content/uploads/2010/02/stagealign2.png" alt="stagealign2" width="400" height="260" /></p>
<p>依此我们便可以作出自由指定显示swf内容范围的功能，比如初始swf原始大小为100*100，但是外部容器只设定宽高为50*50。当为右上角对齐时，swf只显示右上区域的50*50部分。当某件事发生时（比如鼠标点击一个按钮），外部容器款高变成100*100，如此便能看见全部的swf了。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/02/10/626.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>web端语法高亮原理：走进jssc的世界（四）</title>
		<link>http://army8735.org/2010/02/02/608.html</link>
		<comments>http://army8735.org/2010/02/02/608.html#comments</comments>
		<pubDate>Tue, 02 Feb 2010 06:11:39 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[jssc]]></category>
		<category><![CDATA[系列文章]]></category>
		<category><![CDATA[语法高亮原理]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=608</guid>
		<description><![CDATA[内嵌解析
很容易遇到这样的情况：在需要高亮的代码中还混淆着其它语言种类的代码（最常见的例子为Html内嵌css和js，以下也将以此为例）。这是一件让人头疼的事情，因为无论采用何种方法，内嵌的语言和原本语言的规则一定是不同的。这意味着必须将它们区分开来对待。从这一点出发，自然而然就能引出问题的关键所在——如何区分？
剥离内容
先来考虑最简单的情况：
&#60;head&#62;
&#60;script&#62;var i = 0;&#60;/script&#62;
&#60;/head&#62;
假如没有第2行的script标签以及其内部的代码，那么整个就是纯html代码高亮，这个没有什么难度（如果已经完全理解前三篇的此法分析的话）。然而不凑巧的是，关键点就在于script标签中会出现js代码，html的此法分析中并没有js的词法规则，两者不能等同。那么怎么办呢？
答案是将它们剥离出来。上例是最简单的例子，我们在对html进行此法分析的时候，一旦读到了&#60;script&#62;开始标签，接着便去寻找&#60;/script&#62;结束标签（一般会使用String.indexOf()来查找），然后将标签里面的内容单独提取出来。这是第一步，如果做完的话，此时html的高亮结果应该是：除了js代码没有高亮（即默认颜色）以外，其它的html代码均被正确高亮了。
更复杂的情况
剥离到此还没有结束，因为剥离要考虑其它一些复杂的元素。看以下代码：
&#60;head&#62;
&#60;script&#62;var i = 0;
//&#60;/script&#62;
&#60;/script&#62;
&#60;/head&#62;
代码的第3行中，出现了单行注释，其中有被注释掉的script结束标签。这点需要格外注意。假若使用String.indexOf()来查询script结束标签的话，那么就会在第3行结束。这样就错了，因为第3行实际上是个注释，真正的结束符在第4行。
以此延伸，除了上面的情况以外，引号中的字符串、多行注释、正则里面均会出现类似情况。因此，单纯的String.indexOf()是肯定不行的。我们必须对js代码部分进行预处理。
预处理
在as的解析部分，实际上主要分为两大块：词法分析和存储结果。词法分析即是前面几篇一直在讲解的内容；存储结果即是将分析出来的代码链接起来，说白了就是简单的字符串拼接。
在html中的js代码可能会出现混淆script结束标签的情况之下，唯一解决的办法就是对js也进行简单的词法分析预处理，但不存储结果。因为分析是“读”，而存储是“写”。写的耗时要比读多多了，而且预处理只是为了防止注释、字符串和正则的混淆，不需要真正地进行解析，实现复杂读也比较低。等完全将js代码从html中剥离后，再交给js解析器来做真正的工作，如此也保证了代码的不重复。
&#60;head&#62;
&#60;script&#62;
var i = 0;
//&#60;/script&#62;
"&#60;/script&#62;"
/*&#60;/script&#62;*/
/[&#60;/script&#62;]/
&#60;/script&#62;
&#60;/head&#62;
html中的预处理，至少要保证能将3到7行的js代码完全剥离出来交给js解析器处理，其它html代码则由本身来完成高亮。
状态区分
接下来的难点可能还是在如何在html解析的时候完成区分上面。在这里，我设置了一个state变量用以标识状态。仔细考虑下html，无非发现它主要有以下几种状态：

html节点：即&#60;&#62;内的tag，还有随之的一些属性内容。如：&#60;img width=&#8221;100px&#8221;/&#62;。
text节点：文本内容，段落p中最常见到。
css节点：style中的css代码。
script节点：script中的js代码。

在默认最开始的时候，是文本节点。一旦遇到了左尖括号，并且随后跟的是个正确的节点名（&#60;x&#62;绝对不是个正确的节点，所以不能当成节点来处理），那么就进入节点状态来解析；当节点解析完了之后，返回文本状态。css和script节点是个两个特殊的节点，因为在它们的开始标签结束之后，要进行预处理查找结束标签。实际上会做其中的一个，另外一个也就懂了。
值得注意的是，html标签中有单个类型的存在，比如&#60;br/&#62;，它不需要成对出现，甚至可以写成&#60;br&#62;。省略/的又是另外一种自闭合类型。它们在处理起来有点麻烦，特别是涉及到深度折叠的时候。解决的办法也是设置状态变量，标识当前节点属于那种类型，以此来区分判断。
这篇写得可能有点简单，因为的确是比较抽象的东西。我也偷偷懒，相信能做到前章所提的词法分析的情况下，纯理论来读本篇也不是什么难事了。可能直接读我的源代码反而会更容易些。在下一篇当中，我会介绍as和js的交互以及jssc的大概处理流程，它将作为结束篇章。
]]></description>
			<content:encoded><![CDATA[<h3>内嵌解析</h3>
<p>很容易遇到这样的情况：在需要高亮的代码中还混淆着其它语言种类的代码（最常见的例子为Html内嵌css和js，以下也将以此为例）。这是一件让人头疼的事情，因为无论采用何种方法，内嵌的语言和原本语言的规则一定是不同的。这意味着必须将它们区分开来对待。从这一点出发，自然而然就能引出问题的关键所在——如何区分？</p>
<h3>剥离内容</h3>
<p>先来考虑最简单的情况：</p>
<pre class="brush:html">&lt;head&gt;
&lt;script&gt;var i = 0;&lt;/script&gt;
&lt;/head&gt;</pre>
<p>假如没有第2行的script标签以及其内部的代码，那么整个就是纯html代码高亮，这个没有什么难度（如果已经完全理解前三篇的此法分析的话）。然而不凑巧的是，关键点就在于script标签中会出现js代码，html的此法分析中并没有js的词法规则，两者不能等同。那么怎么办呢？</p>
<p>答案是将它们剥离出来。上例是最简单的例子，我们在对html进行此法分析的时候，一旦读到了&lt;script&gt;开始标签，接着便去寻找&lt;/script&gt;结束标签（一般会使用String.indexOf()来查找），然后将标签里面的内容单独提取出来。这是第一步，如果做完的话，此时html的高亮结果应该是：除了js代码没有高亮（即默认颜色）以外，其它的html代码均被正确高亮了。</p>
<h3>更复杂的情况</h3>
<p>剥离到此还没有结束，因为剥离要考虑其它一些复杂的元素。看以下代码：</p>
<pre class="brush:html">&lt;head&gt;
&lt;script&gt;var i = 0;
//&lt;/script&gt;
&lt;/script&gt;
&lt;/head&gt;</pre>
<p>代码的第3行中，出现了单行注释，其中有被注释掉的script结束标签。这点需要格外注意。假若使用String.indexOf()来查询script结束标签的话，那么就会在第3行结束。这样就错了，因为第3行实际上是个注释，真正的结束符在第4行。</p>
<p>以此延伸，除了上面的情况以外，引号中的字符串、多行注释、正则里面均会出现类似情况。因此，单纯的String.indexOf()是肯定不行的。我们必须对js代码部分进行预处理。</p>
<h3>预处理</h3>
<p>在as的解析部分，实际上主要分为两大块：词法分析和存储结果。词法分析即是前面几篇一直在讲解的内容；存储结果即是将分析出来的代码链接起来，说白了就是简单的字符串拼接。</p>
<p>在html中的js代码可能会出现混淆script结束标签的情况之下，唯一解决的办法就是对js也进行<strong>简单的词法分析预处理</strong>，但不存储结果。因为分析是“读”，而存储是“写”。写的耗时要比读多多了，而且预处理只是为了防止注释、字符串和正则的混淆，不需要真正地进行解析，实现复杂读也比较低。等完全将js代码从html中剥离后，再交给js解析器来做真正的工作，如此也保证了代码的不重复。</p>
<pre class="brush:html">&lt;head&gt;
&lt;script&gt;
var i = 0;
//&lt;/script&gt;
"&lt;/script&gt;"
/*&lt;/script&gt;*/
/[&lt;/script&gt;]/
&lt;/script&gt;
&lt;/head&gt;</pre>
<p>html中的预处理，至少要保证能将3到7行的js代码完全剥离出来交给js解析器处理，其它html代码则由本身来完成高亮。</p>
<h3>状态区分</h3>
<p>接下来的难点可能还是在如何在html解析的时候完成区分上面。在这里，我设置了一个state变量用以标识状态。仔细考虑下html，无非发现它主要有以下几种状态：</p>
<ol>
<li>html节点：即&lt;&gt;内的tag，还有随之的一些属性内容。如：&lt;img width=&#8221;100px&#8221;/&gt;。</li>
<li>text节点：文本内容，段落p中最常见到。</li>
<li>css节点：style中的css代码。</li>
<li>script节点：script中的js代码。</li>
</ol>
<p>在默认最开始的时候，是文本节点。一旦遇到了左尖括号，并且随后跟的是个正确的节点名（&lt;x&gt;绝对不是个正确的节点，所以不能当成节点来处理），那么就进入节点状态来解析；当节点解析完了之后，返回文本状态。css和script节点是个两个特殊的节点，因为在它们的开始标签结束之后，要进行预处理查找结束标签。实际上会做其中的一个，另外一个也就懂了。</p>
<p>值得注意的是，html标签中有单个类型的存在，比如&lt;br/&gt;，它不需要成对出现，甚至可以写成&lt;br&gt;。省略/的又是另外一种自闭合类型。它们在处理起来有点麻烦，特别是涉及到深度折叠的时候。解决的办法也是设置状态变量，标识当前节点属于那种类型，以此来区分判断。</p>
<p>这篇写得可能有点简单，因为的确是比较抽象的东西。我也偷偷懒，相信能做到前章所提的词法分析的情况下，纯理论来读本篇也不是什么难事了。可能直接读我的源代码反而会更容易些。在<a href="http://army8735.org/2010/02/23/669.html" target="_blank">下一篇</a>当中，我会介绍as和js的交互以及jssc的大概处理流程，它将作为结束篇章。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/02/02/608.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>YY一下各种土豆</title>
		<link>http://army8735.org/2010/02/01/606.html</link>
		<comments>http://army8735.org/2010/02/01/606.html#comments</comments>
		<pubDate>Mon, 01 Feb 2010 06:54:07 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[其它]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=606</guid>
		<description><![CDATA[首先还是感谢下小麦，让偶有机会加入土豆大家庭。作为新土豆，来胡乱YY一下假想中的各种土豆吧，我只知道“黑豆”是高清……

红豆：SNS的代表，红色是社交的颜色，符合这个主题。
绿豆：健康的代表，绿豆本身就是很有营养价值的豆子，不都说绿色食品么。
金豆：理财投资的代表，这个没必要解释了吧。
紫豆：女性专题的代表，貌似粉豆也差不多。
蓝豆：科技的代表，科技是比较新潮梦幻的，蓝色也是梦幻的。
黄豆：城市消费？想不出来该是啥……
魔豆：没想好该是什么……

]]></description>
			<content:encoded><![CDATA[<p>首先还是感谢下<a href="http://www.mikkolee.com/" target="_blank">小麦</a>，让偶有机会加入<a href="http://www.tudou.com" target="_blank">土豆大家庭</a>。作为新土豆，来胡乱YY一下假想中的各种土豆吧，我只知道“<a href="http://hd.tudou.com" target="_blank">黑豆</a>”是高清……</p>
<ol>
<li>红豆：SNS的代表，红色是社交的颜色，符合这个主题。</li>
<li>绿豆：健康的代表，绿豆本身就是很有营养价值的豆子，不都说绿色食品么。</li>
<li>金豆：理财投资的代表，这个没必要解释了吧。</li>
<li>紫豆：女性专题的代表，貌似粉豆也差不多。</li>
<li>蓝豆：科技的代表，科技是比较新潮梦幻的，蓝色也是梦幻的。</li>
<li>黄豆：城市消费？想不出来该是啥……</li>
<li>魔豆：没想好该是什么……</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/02/01/606.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>如何访问作为命名空间的函数内部变量</title>
		<link>http://army8735.org/2010/01/26/591.html</link>
		<comments>http://army8735.org/2010/01/26/591.html#comments</comments>
		<pubDate>Tue, 26 Jan 2010 01:28:06 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=591</guid>
		<description><![CDATA[原文标题：《Functions as Namespaces, and How to Peek Inside》
原文地址：http://www.davidflanagan.com/2009/11/functions-as-na.html
很精彩的技巧，通过闭包和eval()的使用，达到标题中的功能。
var value = (function() {  // Wrapper function creates a local scope...]]></description>
			<content:encoded><![CDATA[<p>原文标题：《Functions as Namespaces, and How to Peek Inside》</p>
<p>原文地址：<a href="http://www.davidflanagan.com/2009/11/functions-as-na.html">http://www.davidflanagan.com/2009/11/functions-as-na.html</a></p>
<p>很精彩的技巧，通过闭包和eval()的使用，达到标题中的功能。</p>
<pre class="brush:js">var value = (function() {  // Wrapper function creates a local scope or namespace
	// your code goes here
	return value;  // Export a value from the namespace
})();  // Invoke the wrapper function to run your code</pre>
<p>以上代码是我们经常用到的技巧：赋值时通过执行一个匿名函数来防止一些代码变量污染全局空间，并且函数内部可以写很复杂的实现而无需担心对外部的影响。</p>
<p>但是有些情况下我们得到的js代码是一串字符串——例如，用xhr读取到的js代码。倘若想要使用它，可以用eval()，还可以用更方便的Function()构造器。</p>
<pre class="brush:js">var code = "alert(1);";  // A string of JS code to evaluate
var f = new Function(code);   // Wrap it in a function
f();    // And run the function

//army注：这段代码和下面是完全等同的：
function f() {
	alert(1);
}
f();</pre>
<p>所以，通过这种技巧，哪怕是从一段js字符串源代码来赋值，也是可行的：</p>
<pre class="brush:js">var code = "return 3;";
var f = new Function(code);
var i = f(); //i是3</pre>
<p>但是这里却有一个问题。因为是由Function()构造函数而来的，相当于创造了一个密封的命名空间（所有代码都在一个匿名function内执行），我们无法从外部访问它。倘若里面有一些定义的类或者函数之类的东西，那就难办了。比如这样：</p>
<pre class="brush:js">var code = "function Test() {};";
var f = new Function(code);
f();
//这相当于执行了以下方法：
function f() {
	function Test() {
	}
};
f();</pre>
<p>内部定义了一个Test类，我们很难访问到它。不过这里有个技巧——这也是本篇要介绍的主角——可以通过<strong>闭包+eval()</strong>结合使用来绕过这种限制。</p>
<pre class="brush:js">var code = "function Test() { alert('a test'); };";
var f = new Function(code + "return function(s) { return eval(s); };")(); //关键！还有后面的括号！
var Test = f("Test");
new Test();</pre>
<p>如何？内部的Test类成功从外部创建了。关键就在于第2行，这里有个小小限制，第3行传入的参数需和要使用的内部变量名相等。外部的Test其实相当于密封函数内的Test的一个copy，没想明白的话根据代码倒着走一遍就ok了。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/01/26/591.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>ecmascript中的命令链</title>
		<link>http://army8735.org/2010/01/25/567.html</link>
		<comments>http://army8735.org/2010/01/25/567.html#comments</comments>
		<pubDate>Mon, 25 Jan 2010 03:10:28 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[设计模式]]></category>
		<category><![CDATA[命令]]></category>
		<category><![CDATA[命令链]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=567</guid>
		<description><![CDATA[有人好奇JAse中的undo和redo是怎么做的，并且给出自己的设计做法也各有特色。我最初采用的是全文保存方法（这一方法也是最简单有效、使用最广泛的），后来被人痛批一顿换成了命令链。命令链其实是基于一种叫做“命令”的设计模式的，关键思想在于面向接口编程。这个东西随便搜搜有很多例子，我再重复制造一下轮子吧。
AS3中的命令链
as3对OOP的支持已经比较完善，所以可以据此写出很好的命令链，先从简单的例子来说起（以下代码均被简化）。假如我们要做一个Dog类，Dog的“说话”方式是bark（狗叫）：
class Dog {
	public function bark():void {
		trace("汪汪！");
	}
}
这很简单，没有什么特殊之处。可是奇怪的事情发生了，中学英语课本中有个闻名的澳洲野狗Dingo，它的叫声和普通的狗不一样：
class Dingo extends Dog {
	public override function bark():void {
		trace("呜呜——");
	}
}
也很简单，Dingo毕竟还是一只狗，只需继承并覆盖即可。好了现在需求来了：我们想听听这些动物的叫声是什么样的，只需要调用下相应的方法即可。
public class Test {
	public function Test():void {
		var...]]></description>
			<content:encoded><![CDATA[<p>有人好奇JAse中的undo和redo是怎么做的，并且给出自己的设计做法也各有特色。我最初采用的是全文保存方法（这一方法也是最简单有效、使用最广泛的），后来被人痛批一顿换成了命令链。命令链其实是基于一种叫做“命令”的设计模式的，关键思想在于<strong>面向接口编程</strong>。这个东西随便搜搜有很多例子，我再重复制造一下轮子吧。</p>
<h3>AS3中的命令链</h3>
<p>as3对OOP的支持已经比较完善，所以可以据此写出很好的命令链，先从简单的例子来说起（以下代码均被简化）。假如我们要做一个Dog类，Dog的“说话”方式是bark（狗叫）：</p>
<pre class="brush:as3">class Dog {
	public function bark():void {
		trace("汪汪！");
	}
}</pre>
<p>这很简单，没有什么特殊之处。可是奇怪的事情发生了，中学英语课本中有个闻名的澳洲野狗Dingo，它的叫声和普通的狗不一样：</p>
<pre class="brush:as3">class Dingo extends Dog {
	public override function bark():void {
		trace("呜呜——");
	}
}</pre>
<p>也很简单，Dingo毕竟还是一只狗，只需继承并覆盖即可。好了现在需求来了：我们想听听这些动物的叫声是什么样的，只需要调用下相应的方法即可。</p>
<pre class="brush:as3">public class Test {
	public function Test():void {
		var dog:Dog = new Dog();
		dog.bark();

		var dingo:Dog = new Dingo();
		dingo.bark();
	}
}</pre>
<p>目前为止一切顺利，可惜未来总是不像我们想象的那样。动物中又多了只公鸡，它的叫法完全不一样，是打鸣。</p>
<pre class="brush:as3">class Cock {
	public function crow():void {
		trace("喔喔——");
	}
}</pre>
<p>这下坏了，我们在听叫声的时候得记住，公鸡的叫法和狗不一样：</p>
<pre class="brush:as3">public class Test {
	public function Test():void {
		var dog:Dog = new Dog();
		dog.bark();

		var dingo:Dog = new Dingo();
		dingo.bark();

		var cock:Cock = new Cock();
		cock.crow();
	}
}</pre>
<p>倘若把这些动物排成一列，让它们依次各自叫一下的话就更麻烦了：</p>
<pre class="brush:as3">public class Test {
	public function Test():void {
		var list:Array = [new Dog(), new Dingo(), new Cock()];
		for (var i:int = 0; i &lt; list.length; i++) {
			var item = list[i];
			if (item is Dog) {
				item.bark();
			}
			else if (item is Cock) {
				item.crow();
			}
		}
	}
}</pre>
<p>我们得对每种类型做单独判断，可所谓不胜其烦，动物的种类是不可预知的，倘若再来一只猫，还要继续增加判别吗？当然不能。</p>
<p>显然，关于叫法这里我们需要解耦。不管你是什么动物，这些叫声其实都可以概括为“说话”（动物们也有自己的语言和说话方式）。在主调程序中，我们并不想关心动物是“怎么说话”、“说些什么”的，我们只想调用一个命令，动物对象就能自动按照自己的方式来“说话”，甚至我们不用关心这个动物到底是什么。通过分离做什么和怎么做来实现这个目标的方式，就称为<strong>命令模式</strong>。</p>
<p>好了，首先是命令接口：</p>
<pre class="brush:as3">public interface Command {
	function say():void;
}</pre>
<p>所有动物只要实现了这个接口就行，各自具体的执行方式自己来定：</p>
<pre class="brush:as3">class Dog implements Commad {
	public function say():void {
		bark();
	}
	protected function bark():void {
		trace("汪汪！");
	}
}

class Dingo extends Dog {
	protected override function bark():void {
		trace("呜呜——"");
	}
}

class Cock implements Commad {
	public function say():void {
		crow();
	}
	private function crow():void {
		trace("喔喔——");
	}
}</pre>
<p>这样的话，在主调程序中，管它排成一列的动物有哪些，统统视作Command接口的实现即可：</p>
<pre class="brush:as3">public class Test {
	public function Test():void {
		var list:Array = [new Dog(), new Dingo(), new Cock()];
		for (var i:int = 0; i &lt; list.length; i++) {
			(list[i] as Command).say();
		}
	}
}</pre>
<p>哪怕再多出来一只猫，我们只需要增加猫的这一类别，列表中多出只猫来即可，主调程序无需关心具体实现。甚至猫的方式更加复杂有感情：</p>
<pre class="brush:as3">class Cat implements Command {
	private var isHappy:Boolean = true;

	public function say():void {
		if (isHappy) {
			purr();
		}
		else {
			mew();
		}
	}

	private function purr():void {
		trace("咕噜……");
	}
	private function mew():void {
		trace("喵——");
	}
}

public class Test {
	public function Test():void {
		var list:Array = [new Dog(), new Dingo(), new Cock(), new Cat()];
		for (var i:int = 0; i &lt; list.length; i++) {
			(list[i] as Command).say();
		}
	}
}</pre>
<h3>undo、redo的命令链</h3>
<p>熟悉了这些后，undo和redo的做法同理：只需实现了命令接口，每个操作各是独自的命令，互不干涉，具体实现自己决定，最大程度上解耦。</p>
<p>定义命令接口（以下代码均被简化）：</p>
<pre class="brush:as3">package command {

	public interface ICommand {
		function redo():void;
		function undo():void;
	}

}</pre>
<p>输入命令——也就是在编辑器中敲入代码时：</p>
<pre class="brush:as3">package command {
	import flash.text.*;

	public class InputCommand implements ICommand {
		private var tf:TextField;
		private var index:int;
		private var text:String;

		public function InputCommand(tf:TextField, index:int, text:String):void {
			this.tf = tf;
			this.index = index;
			this.text = text;
		}

		public function redo():void {
			tf.setSelection(index + text.length, index + text.length);
		}
		public function undo():void {
			tf.replaceText(index, index + text.length, "");
		}
	}
}</pre>
<p>删除命令——当按Delete键将编辑器里的内容删除时：</p>
<pre class="brush:as3">package command {
	import flash.text.*;

	public class DeleteCommand implements ICommand {
		private var tf:TextField;
		private var index:int;
		private var end:int;
		private var text:String;

		public function DeleteCommand(tf:TextField, index:int, end:int, text:String):void {
			this.tf = tf;
			this.index = index;
			this.end = end;
			this.text = text;
		}

		public function redo():void {
			tf.replaceText(index, index + text.length, "");
		}
		public function undo():void {
			tf.replaceText(index, index, text);
		}
	}
}</pre>
<p>于是，每当我们输入一个字符时，向一个记录命令步骤的数组里存入一个输入命令；而在按下Delete键时，存入一个删除命令；其它多一个命令多建一个类实现，这样一个链连下来就是<strong>命令链</strong>。</p>
<pre class="brush:as3">package command {
	import flash.text.*;
	import edit.*;

	public class CommandList {
		private var undoList:Array, redoList:Array;

		public function CommandList():void {
			clear();
		}

		public function addCommand(cmd:ICommand):void {
			//超过最大命令链长度需先出队列一个
			if (undoList.length &gt; Editor.UNDO_SIZE) {
				undoList.shift();
			}
			//每添加一次命令，清空redoList
			if(redoList.length) {
				redoList = new Array();
			}
			undoList.push(cmd);
		}
		public function undo():Boolean {
			//undoList中有命令则执行，并将相应命令出栈存入redoList中
			if(undoList.length) {
				var cmd:ICommand = undoList.pop() as ICommand;
				cmd.undo();
				redoList.push(cmd);
				return true;
			}
			//为空返回false
			else {
				return false;
			}
		}
		public function redo():Boolean {
			//redoList中有命令则执行，并将相应命令出栈存入undoList中
			if (redoList.length) {
				var cmd:ICommand = redoList.pop() as ICommand;
				cmd.redo();
				undoList.push(cmd);
				return true;
			}
			//为空返回false
			else {
				return false;
			}
		}
		public function clear():void {
			undoList = new Array();
			redoList = new Array();
		}
	}

}</pre>
<p>其实基于fp10中as3的新特性，有个叫Vector的类，它是存储命令链数组的更好的替代者，因为它规定数组里所有元素的类型必须是相同的。这样在编译期间便能防止出错，于是undoList和redoList更好的定义方式是这样：</p>
<pre class="brush:as3">undoList = new Vector.&lt;ICommand&gt;();
redoList = new Vector.&lt;ICommand&gt;();</pre>
<p>熟悉Java的很容易理解：这就是Java5+中的泛型。</p>
<h3>js中的命令链</h3>
<p>扯了这么远才到js，而且篇幅也不会多长，真是愧对这标题。</p>
<p>js中并不存在接口，所以也就无从说起面向接口编程。然而以此就下定论说“不能使用命令链”尚为时过早。的确，js由于其本身特性原因，不能像传统OOP语言那样使用，但命令链模式其实就存在于日常生活当中：</p>
<pre class="brush:js">function Dog() {
}
Dog.prototype.say = function() {
	this.bark();
}
Dog.prototype.bark = function() {
	alert("汪汪！");
}

function Dingo() {
}
Dingo.prototype = new Dog();
Dingo.prototype.bark = function() {
	alert("呜呜——");
}

function Cock() {
}
Cock.prototype.say = function() {
	this.crow();
}
Cock.prototype.crow = function() {
	alert("喔喔——");
}

function Cat() {
	this.bHappy = true;
}
Cat.prototype.say = function() {
	if(this.bHappy) {
		this.purr();
	}
	else {
		this.mew();
	}
}
Cat.prototype.purr = function() {
	alert("咕噜……");
}
Cat.prototype.mew = function() {
	alert("喵——");
}

var aList = [new Dog(), new Dingo(), new Cock(), new Cat()];
for(var i = 0; i &lt; aList.length; i++) {
	aList[i].say();
}</pre>
<p>基于弱类型，每个类都可以拥有一个同名方法，广义上说这也可以看作是实现了一个“通用接口”。js没有编译过程，因此无法在前期（编译期）实现检查，只能靠后期（运行时）来确定。网上也有很多例子模拟出js的接口和命令链，使得一个类在实现接口但却没有实现接口定义的方法时抛出异常。这样做的好处是在运行出错时有详尽的异常信息供使用者检查，但在目前firebug等工具的情况下所提供的能效有限，在大型的开发中才会有很好的帮助。因此我们目前大部分情况下——似乎最好的办法就是<strong>相信别人</strong>。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/01/25/567.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>jssc 5.0 beta5</title>
		<link>http://army8735.org/2010/01/22/560.html</link>
		<comments>http://army8735.org/2010/01/22/560.html#comments</comments>
		<pubDate>Fri, 22 Jan 2010 01:28:25 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[jssc]]></category>
		<category><![CDATA[前端开发]]></category>
		<category><![CDATA[beta5]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=560</guid>
		<description><![CDATA[下载页面：http://code.google.com/p/jssc/downloads/list
源码地址：http://jssc.googlecode.com/svn/trunk/
预览效果：http://army8735.org/wp-content/uploads/jssc/
全部是细节方面的调整。
性能有所略微的提升，体积稍微减少一点，可能整体没啥感觉。因为性能瓶颈主要在两方面：as的分析阶段和js的显示阶段。分析阶段中主要是词法分析阶段和字符串拼接阶段，调整的是词法分析阶段，而这个部分占整体所耗时间不是最多的，所以提升不明显。
接口稍微改了下，具体的看源代码。php由原本的纯php代码变为了内嵌显示方式，像html那样。
这个可能是最后一个beta版了，rc如果放出基本都是针对bug的修补，另外会全面更新wiki的使用方法。
说下5.x系列的计划吧：

首先是增加语言：诸如jsp、ruby、csharp等等。语系的增加不会增加子版本号，如5.1，而是后缀的小版本号——5.0.1。
新特性增加将以子版本号形式出现，譬如5.1版本首先考虑的是缓存输出优化（针对代码行上万的高亮显示）。
也是以前考虑实现而没有实现的特性：自动格式化，这个可能难一点，在以后的计划之内吧。

暂且这么多，有想到新的或者别人的想法再列入计划里。
]]></description>
			<content:encoded><![CDATA[<p>下载页面：<a href="http://code.google.com/p/jssc/downloads/list">http://code.google.com/p/jssc/downloads/list</a></p>
<p>源码地址：<a href="http://jssc.googlecode.com/svn/trunk/">http://jssc.googlecode.com/svn/trunk/</a></p>
<p>预览效果：<a href="http://army8735.org/wp-content/uploads/jssc/">http://army8735.org/wp-content/uploads/jssc/</a></p>
<p><strong>全部是细节方面的调整。</strong></p>
<p>性能有所略微的提升，体积稍微减少一点，可能整体没啥感觉。因为性能瓶颈主要在两方面：as的分析阶段和js的显示阶段。分析阶段中主要是词法分析阶段和字符串拼接阶段，调整的是词法分析阶段，而这个部分占整体所耗时间不是最多的，所以提升不明显。</p>
<p>接口稍微改了下，具体的看源代码。php由原本的纯php代码变为了内嵌显示方式，像html那样。</p>
<p>这个可能是最后一个beta版了，rc如果放出基本都是针对bug的修补，另外会全面更新wiki的使用方法。</p>
<p>说下5.x系列的计划吧：</p>
<ol>
<li>首先是增加语言：诸如jsp、ruby、csharp等等。语系的增加不会增加子版本号，如5.1，而是后缀的小版本号——5.0.1。</li>
<li>新特性增加将以子版本号形式出现，譬如5.1版本首先考虑的是缓存输出优化（针对代码行上万的高亮显示）。</li>
<li>也是以前考虑实现而没有实现的特性：自动格式化，这个可能难一点，在以后的计划之内吧。</li>
</ol>
<p>暂且这么多，有想到新的或者别人的想法再列入计划里。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/01/22/560.html/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>智能JPEG优化技术</title>
		<link>http://army8735.org/2010/01/07/546.html</link>
		<comments>http://army8735.org/2010/01/07/546.html#comments</comments>
		<pubDate>Thu, 07 Jan 2010 03:09:16 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[其它]]></category>
		<category><![CDATA[前端开发]]></category>
		<category><![CDATA[翻译]]></category>
		<category><![CDATA[jpeg]]></category>
		<category><![CDATA[优化]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=546</guid>
		<description><![CDATA[同事发来了一篇《Clever JPEG Optimization Techniques》，文中提到的部分技术很细致，分享下。
大部分人在考虑图像压缩优化的时候，还只停留在设置图像处理软件的保存选项上。另外我们也有一些常用的优化工具，诸如OptiPNG和jpegtran。但是还有一些鲜为人知的方法，比如即将介绍的“8像素格优化方法”，它的原理是基于图像数据存储的格式上的。
8像素格
众所周知，jpeg图像存储是以8像素格为基本单位的，看下图示例：
 32×32 pixels, Quality: 10 (in Photoshop), 396 bytes.
两个白色块大小均为8×8像素，图像保存质量为低。可以看出，左上角的方块很清晰，右下角却出现了杂色，这是为什么呢？让我们放大图片并画出参考线格：

可以看出，左上角的方块恰好在8×8格子里面（占据4个），而右下角的却横跨了9个格子，除了中间部分占满一个格子外，周围8个都只占据一部分。
由于jpeg存储算法中，每8×8个像素格是单独进行优化的，算法会寻找这个基本单位格中的均色（jpeg是以颜色正弦波编码）。因此，图像处理时应该尽可能考虑到这点，使得元素的位置靠近每个8×8的像素格。
这个方法使用起来很简单，比如下面这个例子：
13.51 KB
12.65 KB
第一张图片中，微波炉的位置是随意放的，而第二张却经过了细微的调整。两者存储的质量相同，都是55。让我们放大点看，红线是参考线：

可以看到，在略微移动了几个像素之后，图像减少了大约1 KB，并且也更清晰了一点。
颜色优化
这部分主要介绍不常用的图片存储格式，它主要应用在电视上面，暂不介绍。
常见的JPEG优化方法
这里介绍一些常用的优化方法。
JPEG算法很严格，唯一的压缩准则是图像软件设置里的质量选项。你可能在Photoshop中存质量为55～60的图片，但是在其它软件中存80质量才拥有同样的尺寸和外观。
一定不要用100的质量来保存图片！这其实并不是最高的质量值，因为这只是个数学理论上限，如果你非要质量很高的图片的话，一般存到95就够了，5点的质量丢失几乎没有区别。
注意Photoshop中低于50质量的图片保存。因为在50之下的时候，jpeg优化会启动一个附加的算法——color down-sampling——它将均衡相邻的8×8像素格的颜色。
 48×48 pixels, Quality: 50...]]></description>
			<content:encoded><![CDATA[<p>同事发来了一篇<a href="http://www.smashingmagazine.com/2009/07/01/clever-jpeg-optimization-techniques/" target="_blank">《Clever JPEG Optimization Techniques》</a>，文中提到的部分技术很细致，分享下。</p>
<p>大部分人在考虑图像压缩优化的时候，还只停留在设置图像处理软件的保存选项上。另外我们也有一些常用的优化工具，诸如OptiPNG和jpegtran。但是还有一些鲜为人知的方法，比如即将介绍的“8像素格优化方法”，它的原理是基于图像数据存储的格式上的。</p>
<h3>8像素格</h3>
<p>众所周知，jpeg图像存储是以8像素格为基本单位的，看下图示例：</p>
<p style="text-align: center;"><img class="aligncenter size-full wp-image-547" title="8pixel-1" src="http://army8735.org/wp-content/uploads/2010/01/8pixel-1.png" alt="8pixel-1" width="236" height="128" /><span style="color: #ff0000;"> 32×32 pixels, Quality: 10 (in Photoshop), 396 bytes.</span></p>
<p>两个白色块大小均为8×8像素，图像保存质量为低。可以看出，左上角的方块很清晰，右下角却出现了杂色，这是为什么呢？让我们放大图片并画出参考线格：</p>
<p><img class="aligncenter size-full wp-image-548" title="8pixel-2" src="http://army8735.org/wp-content/uploads/2010/01/8pixel-2.png" alt="8pixel-2" width="527" height="527" /></p>
<p>可以看出，左上角的方块恰好在8×8格子里面（占据4个），而右下角的却横跨了9个格子，除了中间部分占满一个格子外，周围8个都只占据一部分。</p>
<p>由于jpeg存储算法中，每8×8个像素格是单独进行优化的，算法会寻找这个基本单位格中的均色（jpeg是以颜色正弦波编码）。因此，图像处理时应该尽可能考虑到这点，使得元素的位置靠近每个8×8的像素格。</p>
<p>这个方法使用起来很简单，比如下面这个例子：</p>
<p style="text-align: center;"><img class="aligncenter size-full wp-image-549" title="8grid-bad" src="http://army8735.org/wp-content/uploads/2010/01/8grid-bad.jpg" alt="8grid-bad" width="550" height="230" /><span style="color: #ff0000;">13.51 KB</span></p>
<p style="text-align: center;"><img class="aligncenter size-full wp-image-550" title="8grid-good" src="http://army8735.org/wp-content/uploads/2010/01/8grid-good.jpg" alt="8grid-good" width="550" height="230" /><span style="color: #ff0000;">12.65 KB</span></p>
<p>第一张图片中，微波炉的位置是随意放的，而第二张却经过了细微的调整。两者存储的质量相同，都是55。让我们放大点看，红线是参考线：</p>
<p><img class="aligncenter size-full wp-image-551" title="8grid-zoom" src="http://army8735.org/wp-content/uploads/2010/01/8grid-zoom.png" alt="8grid-zoom" width="418" height="628" /></p>
<p>可以看到，在略微移动了几个像素之后，图像减少了大约1 KB，并且也更清晰了一点。</p>
<h3>颜色优化</h3>
<p>这部分主要介绍不常用的图片存储格式，它主要应用在电视上面，暂不介绍。</p>
<h3>常见的JPEG优化方法</h3>
<p>这里介绍一些常用的优化方法。</p>
<p>JPEG算法很严格，唯一的压缩准则是图像软件设置里的质量选项。你可能在Photoshop中存质量为55～60的图片，但是在其它软件中存80质量才拥有同样的尺寸和外观。</p>
<p><strong>一定不要用100的质量来保存图片！</strong>这其实并不是最高的质量值，因为这只是个数学理论上限，如果你非要质量很高的图片的话，一般存到95就够了，5点的质量丢失几乎没有区别。</p>
<p>注意Photoshop中低于50质量的图片保存。因为在50之下的时候，jpeg优化会启动一个附加的算法——color down-sampling——它将均衡相邻的8×8像素格的颜色。</p>
<p style="text-align: center;"><img class="aligncenter size-full wp-image-552" title="q50" src="http://army8735.org/wp-content/uploads/2010/01/q50.jpg" alt="q50" width="240" height="240" /><span style="color: #ff0000;"> 48×48 pixels, Quality: 50 (in Photoshop), 530 bytes.</span></p>
<p style="text-align: center;"><img class="aligncenter size-full wp-image-553" title="q51" src="http://army8735.org/wp-content/uploads/2010/01/q51.jpg" alt="q51" width="240" height="240" /><span style="color: #ff0000;"> 48×48 pixels, Quality: 51 (in Photoshop), 484 bytes.</span></p>
<p>高一个质量反而更好更小。所以，如果图片拥有小尺寸、高差别的情况，请至少在Photoshop中保存质量为51。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/01/07/546.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>特性检测并非浏览器检测</title>
		<link>http://army8735.org/2010/01/01/534.html</link>
		<comments>http://army8735.org/2010/01/01/534.html#comments</comments>
		<pubDate>Fri, 01 Jan 2010 03:16:29 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[翻译]]></category>
		<category><![CDATA[浏览器检测]]></category>
		<category><![CDATA[特性检测]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=534</guid>
		<description><![CDATA[NCZ在他的同名博客《Feature detection is not browser detection》中，讲述了一直以来前端开发中的一个热门技术——检测用户的浏览器平台，并详细地叙说历史发展以及各种办法的优缺点。我大致翻译了部分文章，可能有理解错误的地方，敬请指正。值得一提的是，评论部分的争论亦值得一看。
特性检测
起初前端工程师们就极力反对浏览器检测，他们认为类似user-agent嗅探的方法是很不好的，理由是它并不是一种面向未来的代码，无法适应新版的浏览器。更好的做法是使用特性检测，就像这样：
if (navigator.userAgent.indexOf("MSIE 7") &#62; -1){
    //do something
}
而更好的做法是这样：
if(document.all){
    //do something
}
这两种方式并不相同。前者是检测浏览器的特殊名称和版本；后者却是检测浏览器的特性。UA嗅探能够精确得到浏览器的类型和版本（至少能得知浏览器类型），而特性检测却是去确定浏览器是否拥有某个对象或者支持某个方法。注意这两者是完全不同的。
因为特性检测依赖于哪些浏览器支持，当出现新版本浏览器的时候需要繁琐的确认工作。例如DOM标准刚出现的时候，并不是所有浏览器都支持getElementById()方法，所以一开始代码可能是这样：
if(document.getElementById){...]]></description>
			<content:encoded><![CDATA[<p>NCZ在他的同名博客<a href="http://www.nczonline.net/blog/2009/12/29/feature-detection-is-not-browser-detection/" target="_blank">《Feature detection is not browser detection》</a>中，讲述了一直以来前端开发中的一个热门技术——检测用户的浏览器平台，并详细地叙说历史发展以及各种办法的优缺点。我大致翻译了部分文章，可能有理解错误的地方，敬请指正。值得一提的是，评论部分的争论亦值得一看。</p>
<h3>特性检测</h3>
<p>起初前端工程师们就极力反对浏览器检测，他们认为类似<span style="color: #ff0000;">user-agent嗅探</span>的方法是很不好的，理由是它并不是一种面向未来的代码，无法适应新版的浏览器。更好的做法是使用<span style="color: #ff0000;">特性检测</span>，就像这样：</p>
<pre class="brush:js">if (navigator.userAgent.indexOf("MSIE 7") &gt; -1){
    //do something
}</pre>
<p>而更好的做法是这样：</p>
<pre class="brush:js">if(document.all){
    //do something
}</pre>
<p>这两种方式并不相同。前者是检测浏览器的特殊名称和版本；后者却是检测浏览器的特性。UA嗅探能够精确得到浏览器的类型和版本（至少能得知浏览器类型），而特性检测却是去确定浏览器是否拥有某个对象或者支持某个方法。注意这两者是完全不同的。</p>
<p>因为特性检测依赖于哪些浏览器支持，当出现新版本浏览器的时候需要繁琐的确认工作。例如DOM标准刚出现的时候，并不是所有浏览器都支持<span style="color: #ff0000;">getElementById()</span>方法，所以一开始代码可能是这样：</p>
<pre class="brush:js">if(document.getElementById){  //DOM
    element = document.getElementById(id);
} else if (document.all) {  //IE
    element = document.all[id];
} else if (document.layers){  //Netscape &lt; 6
    element = document.layers[id];
}</pre>
<p>这是特性检测很好的一个例子，亮点在于当其它浏览器开始支持<span style="color: #ff0000;">getElementById()</span>方法时不必修改代码。</p>
<h3>混合方式</h3>
<p>后来前端工程师们考虑改进的写法，代码变化成这样：</p>
<pre class="brush:js">//AVOID!!!
if (document.all) {  //IE
    id = document.uniqueID;
} else {
    id = Math.random();
}</pre>
<p>这个代码的问题是通过检测<span style="color: #ff0000;">document.all</span>属性来确定是否是IE。当确定是IE后，假定使用私有的<span style="color: #ff0000;">document.uniqueID</span>属性也是安全的。然而，目前所作的只是确定是否支持<span style="color: #ff0000;">document.all</span>，并非是去辨识浏览器是否为IE。仅仅支持<span style="color: #ff0000;">document.all</span>的话也不意味着<span style="color: #ff0000;">document.uniqueID</span>是可用的。</p>
<p>后来人们开始这样写，用下面那行代替上面的：</p>
<pre class="brush:js">var isIE = navigator.userAgent.indexOf("MSIE") &gt; -1;
//下面这行代替上面那行
var isIE = !!document.all;</pre>
<p>这些变化说明大家对“不要使用UA嗅探”存在误解——不再对浏览器的详细信息进行检测，取而代之的是通过特性的支持来推断。这种<strong>基于浏览器特性检测</strong>的方式非常不好。</p>
<p>后来前端们发现<span style="color: #ff0000;">document.all</span>并不可靠，更好的检测IE变为：</p>
<pre class="brush:js">var isIE = !!document.all &amp;&amp; document.uniqueID;</pre>
<p>这种实现方式陷入歧途。不仅需要费时费事地去识别浏览器所增加的特性支持，另外也不能确定其它浏览器开始支持相同的特性。</p>
<p>如果你认为这样的代码并未被广泛使用，那么看看来自于老版本的Mootools代码片段吧：</p>
<pre class="brush:js">//from MooTools 1.1.2
if (window.ActiveXObject) window.ie = window[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true;
else if (document.childNodes &amp;&amp; !document.all &amp;&amp; !navigator.taintEnabled) window.webkit = window[window.xpath ? 'webkit420' : 'webkit419'] = true;
else if (document.getBoxObjectFor != null || window.mozInnerScreenX != null) window.gecko = true;</pre>
<p>注意它是如何使用特性检测的。我可以指出它一系列的问题，比如通过检测<span style="color: #ff0000;">window.ie</span>会将ie8误认为ie7。</p>
<h3>余波</h3>
<p>随着浏览器的快速发展，使用特性检测变得越来越困难和不可靠。但是Mootools 1.2.4仍然使用这一方法，例如：<span style="color: #ff0000;">getBoxObjectFor()</span>。</p>
<pre class="brush:js">//from MooTools 1.2.4
var Browser = $merge({

	Engine: {name: 'unknown', version: 0},

	Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},

	Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},

	Plugins: {},

	Engines: {

		presto: function(){
			return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
		},

		trident: function(){
			return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
		},

		webkit: function(){
			return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
		},

		gecko: function(){
			return (!document.getBoxObjectFor &amp;&amp; window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18);
		}

	}

}, Browser || {});</pre>
<h3>应该怎么做？</h3>
<p>特性检测是个应该避免的方法，尽管直接进行特性检测是个很好的方法，并且大部分情况下能满足需求。一般只要在检测前知道这个特性是否被实现即可，而不会去考虑它们之间的关系。</p>
<p>我并非是说永远不使用浏览器特性检测而是基于UA嗅探，因为我相信它还是有很多用途的，然而我不相信它有很多合理的用途。如果你考虑UA嗅探的话，请先贯彻这一思想：唯一安全的方式是针对特定浏览器的特定版本，超出范围之外都是不可靠的——例如新出的浏览器版本。其实这样做也是个明智的办法，因为相较于向前兼容不确定的新版本而言，向后兼容老版本是最简单的做法。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2010/01/01/534.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>删除数组的重复项</title>
		<link>http://army8735.org/2009/12/27/523.html</link>
		<comments>http://army8735.org/2009/12/27/523.html#comments</comments>
		<pubDate>Sun, 27 Dec 2009 10:29:14 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[distinct]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=523</guid>
		<description><![CDATA[怿飞在同名文章《删除数组中重复项（uniq）》中分享了他的方法，时间复杂度和空间复杂度均为O（n），但还无法解决弱类型的问题。凑巧我也思考过类似的方法，在此基础上修改一下，可以KO弱类型问题。
&#60;script src="mootools-1.2.4-core-nc.js"&#62;&#60;/script&#62;
&#60;script&#62;
Array.implement({
	distinct: function() {
		if(this.length < 2) {
			return this;
		}
		var hash = new Hash(), value;
		outer:
		for(var i = 0, len = this.length; i...]]></description>
			<content:encoded><![CDATA[<p>怿飞在同名文章<a href="http://www.planabc.net/2009/12/26/array_uniq/" target="_blank">《删除数组中重复项（uniq）》</a>中分享了他的方法，时间复杂度和空间复杂度均为O（n），但还无法解决弱类型的问题。凑巧我也思考过类似的方法，在此基础上修改一下，可以KO弱类型问题。</p>
<pre class="brush:html">&lt;script src="mootools-1.2.4-core-nc.js"&gt;&lt;/script&gt;
&lt;script&gt;
Array.implement({
	distinct: function() {
		if(this.length < 2) {
			return this;
		}
		var hash = new Hash(), value;
		outer:
		for(var i = 0, len = this.length; i < len; i++) {
			if(hash.has(this[i])) {
				value = hash.get(this[i]);
				for(var j = 0, len2 = value.length; j < len2; j++) {
					if($type(this[i]) == value[j]) {
						this.splice(i, 1);
						i--;
						len--;
						continue outer;
					}
				}
				value.push($type(this[i]));
			}
			else {
				hash.set(this[i], [$type(this[i])]);
			}
		}
		return this;
	}
});
var arr = [0, 0, "0", '0', false, null, NaN, undefined, 1, "1", true];
alert(arr.distinct().join(", ")); //[0, "0", false, null, NaN, undefined, 1, "1", true]
&lt;/script&gt;</pre>
<p>基于mootools，我为Array类提供一个distinct方法，以供删除重复项。</p>
<p>基本思想是：遍历数组，建立一个hash用以统计出现过的每条项目。不过hash的key是数组每项的toString()值，value却是个类型数组。每遍历数组中的一个值时，首先检查hash中是否存在key，不存在设置value为一个新数组——并且里面有唯一的类型值（这个数组项的类型）；如果hash中存在key，则遍历value，查找此数组项的类型是否在value中，在则说明重复，不在将类型push到value里面。</p>
<p>这样就解决了弱类型的问题，但如果数组中不仅仅有基本类型，还有数组、对象、arguments、hash等，则会更加复杂。</pre>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/12/27/523.html/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>960gs中浮动清除新变化</title>
		<link>http://army8735.org/2009/12/24/520.html</link>
		<comments>http://army8735.org/2009/12/24/520.html#comments</comments>
		<pubDate>Thu, 24 Dec 2009 06:28:18 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[960gs]]></category>
		<category><![CDATA[clearfix]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=520</guid>
		<description><![CDATA[昨天下了新版本960gs，瞅瞅里面有无新的变化，正巧发现其中.clearfix的新变化：
/* `Clear Floated Elements
----------------------------------------------------------------------------------------------------*/

/* http://sonspring.com/journal/clearing-floats */

.clear {
	clear: both;
	display: block;
	overflow: hidden;
	visibility: hidden;
	width: 0;
	height: 0;
}

/* http://perishablepress.com/press/2009/12/06/new-clearfix-hack */

.clearfix:after {
	clear: both;
	content: ' ';
	display: block;
	font-size:...]]></description>
			<content:encoded><![CDATA[<p>昨天下了新版本<a href="http://960.gs/" target="_blank">960gs</a>，瞅瞅里面有无新的变化，正巧发现其中.clearfix的新变化：</p>
<pre class="brush:css">/* `Clear Floated Elements
----------------------------------------------------------------------------------------------------*/

/* http://sonspring.com/journal/clearing-floats */

.clear {
	clear: both;
	display: block;
	overflow: hidden;
	visibility: hidden;
	width: 0;
	height: 0;
}

/* http://perishablepress.com/press/2009/12/06/new-clearfix-hack */

.clearfix:after {
	clear: both;
	content: ' ';
	display: block;
	font-size: 0;
	line-height: 0;
	visibility: hidden;
	width: 0;
	height: 0;
}

/*
	The following zoom:1 rule is specifically for IE6 + IE7.
	Move to separate stylesheet if invalid CSS is a problem.
*/
* html .clearfix,
*:first-child+html .clearfix {
	zoom: 1;
}</pre>
<p>关键在于最后几行的更新，用单独的样式为ie6和ie7设置了hack（应该是zoom:1触发hasLayout），来解决极少数情况下浮动清除的bug。至于争议性的.clearfix到底用不用，则实在是个鱼与熊掌的问题。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/12/24/520.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JAse预览版</title>
		<link>http://army8735.org/2009/12/17/498.html</link>
		<comments>http://army8735.org/2009/12/17/498.html#comments</comments>
		<pubDate>Thu, 17 Dec 2009 04:20:14 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[JAse]]></category>
		<category><![CDATA[preview]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=498</guid>
		<description><![CDATA[很高兴能为大家带来JAse的预览版，一款基于as+js的网页语法编辑器。在经历了jssc的静态DFA语法解析高亮的研究和JAte文本编辑器的失败之后，JAse终于能够吸取两者的经验和不足，慢慢地开发至今。开头不多说了，来看预览地址（暂且只有js解析器，其它的都是不存在的；基于Flash Player 10）。
两个都可以：http://jase.googlecode.com/svn/trunk/jase1/bin/index.html；http://army8735.org/wp-content/uploads/jase/
它的下载地址：http://code.google.com/p/jase/downloads/list
它的svn：http://jase.googlecode.com/svn/trunk/jase1/
目前只能支撑千行级代码编辑，总体性能并不是卡在解析器上，而是卡在flash本身的input上。有过经验的人可能感受颇深，何时adobe能提供高性能的textfield？
JAse基本内容分为两块：编辑器提供基础编辑功能、解析器以插件形式提供高亮功能。两者之间通过一个接口连接，也就是说所有的外部解析器必须实现IParser接口，编辑器每次的更改也都是调用接口方法。如此实现了扩展语法的功能，只要实现了接口，你可以开发任意的高亮程序。当然，我自己也写了个AbstractParser基类，可以方便地在其基础之上拓展。
编辑器的undo、redo等功能还未和解析器链接上，因此使用这些button的话可能会造成高亮错误，这并不是程序本身原因。预览版放出只是展示基本输入修改已经ok了。其它将会在后续版本中逐渐开发。
JAse的整体流程是：寻找目标textarea（如果指定id直接获取，否则以document上第一个textarea为目标）=&#62;隐藏textarea，将自己替换掉它的位置=&#62;加载外部解析器（如果指定syntax直接加载，否则出现界面选择）=&#62;编辑内容=&#62;提交内容（如果指定url则使用urlloader，否则将内容放回textarea并寻找父级form提交）。
其间动态破损、修复、显示模型经历过3次算法变更得以成型，斜线动态区分perl正则和除法的算法也经历过2次变更，让我用图来简单说下吧。

在这种情况下，每行代码首先会被解析开始状态（如注释、字符串或者普通），同时渲染标明此行是否被高亮过。这两个状态List是和每行代码一一对应的，增加随之增加，修改随之修改，删除随之删除。而区块List则完全脱离于代码行索引，自成一家。
这样做的好处是分析和显示互相分开，每次编辑器内容被修改时先有破损和修复模型来完成行状态、渲染状态和区块的操作；然后由显示模型取得可视区域再从区块List中取出索引对每块进行高亮。
但坏处也很明显，每次操作对 区块List的改动太大了，前面增加一个字符，会导致所有后面的索引进行修改（自增1），因此很快就被废除，有了第2种想法。

废除区块索引。每次编辑器内容修改，都去计算行状态并且设置涉及行的渲染状态为否以便重新渲染。这个算法对分析阶段来说有了极大的便利，但是在显示阶段却叫苦不迭——在渲染每一行代码的时候，都要根据行状态重新对此行代码进行一遍DFA解析，而这个工作在前面的分析阶段已经做过了（大部分）。同样的功能需要两份代码显得冗余，要合并到一起的话又会变得难以维护，而且多分析一次也显得没有必要。
在经历了一段时间的纠结后，最终还是变更到现在这个样子。

区块List成为二维的，亦和每行对应。然后List中的List保存索引。不过和第1种方式略有不同，之前保存的是绝对索引（字符串的下标），而这里保存的是相对索引（相对于行代码第一个字符而言）。这样每次渲染的时候，只要知道行代码的第一个字符索引，然后遍历这行对应的区块List，相加得出绝对索引即可完成显示。而且每次修改后，后面行的相对索引都无需变更，综合了以上两种方法的优点。
至于斜线的动态区分，最终情况和这类似。静态DFA解析的情况下很容易确定一个斜线的含义究竟是除法还是正则——因为源代码扫猫是从头至尾的。动态情况下因为不确定性，不可能每次修改都从头遍历代码，代价太大了，最好能够从当前行或者前面几行判断出。最初我采用向前回溯的方式，这显得很愚蠢，而且前面行还可能存在单行注释、多行注释等情况干扰（词法分析无需考虑，因为注释空白一开始就被剔除了），基本上行不通。
后来我为每行增加一个布尔值来标明行起始状态如果出现斜线的话它的含义是什么，每次内容修改也会根据解析情况重置它，就和重置行状态和是否被渲染一样。
另外曾经让我非常头疼的一个地方是事件侦听部分。我希望的是每次编辑器内容修改时，所触发的侦听在修改后立刻执行，而as中所有的事件侦听都是在之前执行。这导致使用解析器进行分析显示的时候，实际上代码还根本未发生改变，显然这是个悖论。唯一的做法就是将侦听中模拟事件率先执行，然后通过preventDefault()来取消默认事件发生。这里有个问题，那就是像删除这样的事件（无论del还是backspace或者选区替换）是无法被取消的。所幸的是最终这些难题被一一解决——包括TAB键不能使用，感兴趣的可以查看编辑器源文件来了解是怎么做到的。只关心外部解析器的话不用考虑。
试用过程中有任何问题、反馈或者建议请不吝提出。在beta版完成之后，也会出系列文章来详细介绍JAse的所有技术，就像jssc一样。
]]></description>
			<content:encoded><![CDATA[<p>很高兴能为大家带来<a href="http://code.google.com/p/jase/" target="_blank">JAse</a>的预览版，一款基于as+js的网页语法编辑器。在经历了<a href="http://code.google.com/p/jssc/" target="_blank">jssc</a>的静态DFA语法解析高亮的研究和<a href="http://code.google.com/p/jate/" target="_blank">JAte</a>文本编辑器的失败之后，JAse终于能够吸取两者的经验和不足，慢慢地开发至今。开头不多说了，来看预览地址（暂且只有js解析器，其它的都是不存在的；基于Flash Player 10）。</p>
<p>两个都可以：<a href="http://jase.googlecode.com/svn/trunk/jase1/bin/index.html">http://jase.googlecode.com/svn/trunk/jase1/bin/index.html</a>；<a href="http://army8735.org/wp-content/uploads/jase/">http://army8735.org/wp-content/uploads/jase/</a></p>
<p>它的下载地址：<a href="http://code.google.com/p/jase/downloads/list">http://code.google.com/p/jase/downloads/list</a></p>
<p>它的svn：<a href="http://jase.googlecode.com/svn/trunk/jase1/">http://jase.googlecode.com/svn/trunk/jase1/</a></p>
<p>目前只能支撑千行级代码编辑，总体性能并不是卡在解析器上，而是卡在flash本身的input上。有过经验的人可能感受颇深，何时adobe能提供高性能的textfield？</p>
<p>JAse基本内容分为两块：编辑器提供基础编辑功能、解析器以插件形式提供高亮功能。两者之间通过一个接口连接，也就是说所有的外部解析器必须实现IParser接口，编辑器每次的更改也都是调用接口方法。如此实现了扩展语法的功能，只要实现了接口，你可以开发任意的高亮程序。当然，我自己也写了个AbstractParser基类，可以方便地在其基础之上拓展。</p>
<blockquote><p>编辑器的undo、redo等功能还未和解析器链接上，因此使用这些button的话可能会造成高亮错误，这并不是程序本身原因。预览版放出只是展示基本输入修改已经ok了。其它将会在后续版本中逐渐开发。</p></blockquote>
<p>JAse的整体流程是：寻找目标textarea（如果指定id直接获取，否则以document上第一个textarea为目标）=&gt;隐藏textarea，将自己替换掉它的位置=&gt;加载外部解析器（如果指定syntax直接加载，否则出现界面选择）=&gt;编辑内容=&gt;提交内容（如果指定url则使用urlloader，否则将内容放回textarea并寻找父级form提交）。</p>
<p>其间动态破损、修复、显示模型经历过3次算法变更得以成型，斜线动态区分perl正则和除法的算法也经历过2次变更，让我用图来简单说下吧。</p>
<p><img class="aligncenter size-full wp-image-499" title="jase-preview-1" src="http://army8735.org/wp-content/uploads/2009/12/jase-preview-1.png" alt="jase-preview-1" width="481" height="220" /></p>
<p>在这种情况下，每行代码首先会被解析开始状态（如注释、字符串或者普通），同时渲染标明此行是否被高亮过。这两个状态List是和每行代码一一对应的，增加随之增加，修改随之修改，删除随之删除。而区块List则完全脱离于代码行索引，自成一家。</p>
<p>这样做的好处是分析和显示互相分开，每次编辑器内容被修改时先有破损和修复模型来完成行状态、渲染状态和区块的操作；然后由显示模型取得可视区域再从区块List中取出索引对每块进行高亮。</p>
<p>但坏处也很明显，每次操作对 区块List的改动太大了，前面增加一个字符，会导致所有后面的索引进行修改（自增1），因此很快就被废除，有了第2种想法。</p>
<p><img class="aligncenter size-full wp-image-500" title="jase-preview-2" src="http://army8735.org/wp-content/uploads/2009/12/jase-preview-2.png" alt="jase-preview-2" width="321" height="220" /></p>
<p>废除区块索引。每次编辑器内容修改，都去计算行状态并且设置涉及行的渲染状态为否以便重新渲染。这个算法对分析阶段来说有了极大的便利，但是在显示阶段却叫苦不迭——在渲染每一行代码的时候，都要根据行状态重新对此行代码进行一遍DFA解析，而这个工作在前面的分析阶段已经做过了（大部分）。同样的功能需要两份代码显得冗余，要合并到一起的话又会变得难以维护，而且多分析一次也显得没有必要。</p>
<p>在经历了一段时间的纠结后，最终还是变更到现在这个样子。</p>
<p><img class="aligncenter size-full wp-image-503" title="jase-preview-3" src="http://army8735.org/wp-content/uploads/2009/12/jase-preview-31.png" alt="jase-preview-3" width="449" height="204" /></p>
<p>区块List成为二维的，亦和每行对应。然后List中的List保存索引。不过和第1种方式略有不同，之前保存的是绝对索引（字符串的下标），而这里保存的是相对索引（相对于行代码第一个字符而言）。这样每次渲染的时候，只要知道行代码的第一个字符索引，然后遍历这行对应的区块List，相加得出绝对索引即可完成显示。而且每次修改后，后面行的相对索引都无需变更，综合了以上两种方法的优点。</p>
<p>至于斜线的动态区分，最终情况和这类似。静态DFA解析的情况下很容易确定一个斜线的含义究竟是除法还是正则——因为源代码扫猫是从头至尾的。动态情况下因为不确定性，不可能每次修改都从头遍历代码，代价太大了，最好能够从当前行或者前面几行判断出。最初我采用向前回溯的方式，这显得很愚蠢，而且前面行还可能存在单行注释、多行注释等情况干扰（词法分析无需考虑，因为注释空白一开始就被剔除了），基本上行不通。</p>
<p>后来我为每行增加一个布尔值来标明行起始状态如果出现斜线的话它的含义是什么，每次内容修改也会根据解析情况重置它，就和重置行状态和是否被渲染一样。</p>
<p>另外曾经让我非常头疼的一个地方是事件侦听部分。我希望的是每次编辑器内容修改时，所触发的侦听在修改后立刻执行，而as中所有的事件侦听都是在之前执行。这导致使用解析器进行分析显示的时候，实际上代码还根本未发生改变，显然这是个悖论。唯一的做法就是将侦听中模拟事件率先执行，然后通过preventDefault()来取消默认事件发生。这里有个问题，那就是像删除这样的事件（无论del还是backspace或者选区替换）是无法被取消的。所幸的是最终这些难题被一一解决——包括TAB键不能使用，感兴趣的可以查看编辑器源文件来了解是怎么做到的。只关心外部解析器的话不用考虑。</p>
<p>试用过程中有任何问题、反馈或者建议请不吝提出。在beta版完成之后，也会出系列文章来详细介绍JAse的所有技术，就像<a href="http://army8735.org/2009/11/02/236.html" target="_blank">jssc</a>一样。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/12/17/498.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>第一次兼PM心得</title>
		<link>http://army8735.org/2009/12/15/484.html</link>
		<comments>http://army8735.org/2009/12/15/484.html#comments</comments>
		<pubDate>Tue, 15 Dec 2009 07:31:11 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[其它]]></category>
		<category><![CDATA[pm心得]]></category>

		<guid isPermaLink="false">http://army8735.org/?p=484</guid>
		<description><![CDATA[严格意义上来说这并不是我第一次带项目，第一次尝试Project Manager是不久前的Only Lady相册。不过那次是在产品设计早已完成的基础上自主进行的，仅由我和永赞两个人快速完成，需要协调和把握的东西很少。完成后想要出的前后端配合方式ppt也一直没来得及去弄～
这次则不一样，我不仅负责前端开发，还是带项目的，同时还涉及到部分产品经理的职责，可谓一次性扮演了3个角色，2个PM（Project + Product）。看过别人博客那么相关文章，这回也轮到我来喷一喷了，结合下实际情况，总结几点吧：
1.产品和项目
 说实话，当产品和项目同时落到你头上的时候，你能够决策的地方就非常多了。省去产品和项目之间的沟通是件好事，在规模不大的情况下还能够承受，但一旦规模增长，它只会让你力不从心。因为人的时间和精力是有限的，所负责的东西越多，就要越抽象。潘潘前端时间就在会议上说过类似的情况：作为产品经理不仅需要做到本职工作，诸多大小琐碎事务都必须亲自动手，最后导致的结果就是把自己累死，手下可以利用的资源却一直空闲。这是个恶性的循环，会将导致自己越来越忙，而其它人一直赋闲。
我研究心理学也不少日子了，武志红老师曾经在《宽容自己，才能宽以待人》中举过关于诸葛亮的例子：
作为中国历史上的文人典范，诸葛亮也是“严于律己”的代表。治国上，他是“鞠躬尽瘁，死而后已”，同时，他似乎也做到了“宽以待人”，你很少会找到他没有道理地苛责别人。他杀马谡，废李严，设计斩魏延，仿佛都合情合理，都是依法办事，或是形势所迫。
然而，在我看来，他这些做法的内在逻辑一样是“严以待人”。这个逻辑也体现在他的“鞠躬尽瘁”上，蜀国大大小小的事件，他都要过手，意识上，他说是要对得起刘备的看重，但潜意识上，这里面有很深地对别人的不信任，他对人才的要求太高，这种高标准最终导致，因为缺乏锻炼机会，蜀国优秀的文臣武将越来越少。
这是过于“严于律己”的一个必然结果，诸葛亮对自己苛刻的同时，最终也苛刻地对待别人，尽管从大面上看，他并没有做错什么，但整体上，这形成了一种苛刻的气氛，令他和蜀国很难锻炼人才。
信任下属，重用下属，用人不疑。让别人参与进来，让专业人员作出专业决策，不仅能带动整个团队氛围，也能让所有人有归属感。
2.倾诉
由于项目时间紧张，似乎人人都有危机感。在第一天时，设计和开发人员就不停地向我抱怨、倾诉，诉说各种各样的不满。说实话，第一次接受这么多的牢骚真得让我很紧张。但是转念一想，这也说明了信任我。当其它人向我倾诉时是件好事，怕得是没人说出心里话。一旦所有人开始支持并理解你的时候，一切就事半功倍了。
在整个过程中我希望自己是个服务者，而不是高高在上的管理者。随着项目的进展，所有人都变得非常主动，终于感觉这次PM没白当。：）
3.攻人之过勿太严，要思其堪受；教人以善勿过高，当使其可从。
这是我最喜欢的名言之一。别人有错，职责时的方式该如何？不应过严，要考虑他的心理承受能力；引导别人，该怎样去衡量标准？不应过高，要考虑他的实际改善能力。我相信能做到这两点，就能至少赢得下属的尊重和信任了。
4.初期定位
初期的产品定位和策划不明确，是件很恐怖的事情。假如时间再紧，就更雪上加霜了。这次项目就有这个特点，中间进行修改使得很多人都不爽。细节的完善并不会影响到人的情绪，大方向的调整会给人一种“我白做了那么多”的感觉。这是要极力避免的现象。
5.交流工具
我想任何企业都有内部的交流工具，比如MSN、RTX什么的。但我感觉，除非很简单明了的事情，否则一切都不如当面叙说来得好！面对面的谈话所传达的信息非常多，也利于理解和加深双方的熟悉感。前端时间豆浆和我在改一个小东西时就因为误解邮件中的一句极简单的话造成许多无谓的错误和重复工作，如果最初当面花1分钟说清楚的话，一切就简单多了。
6.懂技术的产品经理
PM懂技术是优势，因为能更好地把握产品和理解开发进度。但是技术出身的PM也具备一般程序员的弱项——主观意识强、交谈口才欠缺等。世界上果然没有十全十美的东西。
暂时写这些，后续想到补充。
]]></description>
			<content:encoded><![CDATA[<p>严格意义上来说这并不是我第一次带项目，第一次尝试Project Manager是不久前的<a href="http://army8735.org/2009/10/26/204.html" target="_blank">Only Lady相册</a>。不过那次是在产品设计早已完成的基础上自主进行的，仅由我和永赞两个人快速完成，需要协调和把握的东西很少。完成后想要出的前后端配合方式ppt也一直没来得及去弄～</p>
<p>这次则不一样，我不仅负责前端开发，还是带项目的，同时还涉及到部分产品经理的职责，可谓一次性扮演了3个角色，2个PM（Project + Product）。看过别人博客那么相关文章，这回也轮到我来喷一喷了，结合下实际情况，总结几点吧：</p>
<p><strong>1.产品和项目</strong></p>
<p><strong> </strong>说实话，当产品和项目同时落到你头上的时候，你能够决策的地方就非常多了。省去产品和项目之间的沟通是件好事，在规模不大的情况下还能够承受，但一旦规模增长，它只会让你力不从心。因为人的时间和精力是有限的，所负责的东西越多，就要越抽象。潘潘前端时间就在会议上说过类似的情况：作为产品经理不仅需要做到本职工作，诸多大小琐碎事务都必须亲自动手，最后导致的结果就是把自己累死，手下可以利用的资源却一直空闲。这是个恶性的循环，会将导致自己越来越忙，而其它人一直赋闲。</p>
<p>我研究心理学也不少日子了，<a href="http://blog.sina.com.cn/wuzii" target="_blank">武志红老师</a>曾经在<a href="http://blog.sina.com.cn/s/blog_5476455901000a0s.html" target="_blank">《宽容自己，才能宽以待人》</a>中举过关于诸葛亮的例子：</p>
<blockquote><p>作为中国历史上的文人典范，诸葛亮也是“严于律己”的代表。治国上，他是“鞠躬尽瘁，死而后已”，同时，他似乎也做到了“宽以待人”，你很少会找到他没有道理地苛责别人。他杀马谡，废李严，设计斩魏延，仿佛都合情合理，都是依法办事，或是形势所迫。</p>
<p>然而，在我看来，他这些做法的内在逻辑一样是“严以待人”。这个逻辑也体现在他的“鞠躬尽瘁”上，蜀国大大小小的事件，他都要过手，意识上，他说是要对得起刘备的看重，但潜意识上，这里面有很深地对别人的不信任，他对人才的要求太高，这种高标准最终导致，因为缺乏锻炼机会，蜀国优秀的文臣武将越来越少。</p>
<p>这是过于“严于律己”的一个必然结果，诸葛亮对自己苛刻的同时，最终也苛刻地对待别人，尽管从大面上看，他并没有做错什么，但整体上，这形成了一种苛刻的气氛，令他和蜀国很难锻炼人才。</p></blockquote>
<p>信任下属，重用下属，用人不疑。让别人参与进来，让专业人员作出专业决策，不仅能带动整个团队氛围，也能让所有人有归属感。</p>
<p><strong>2.倾诉</strong></p>
<p>由于项目时间紧张，似乎人人都有危机感。在第一天时，设计和开发人员就不停地向我抱怨、倾诉，诉说各种各样的不满。说实话，第一次接受这么多的牢骚真得让我很紧张。但是转念一想，这也说明了信任我。当其它人向我倾诉时是件好事，怕得是没人说出心里话。一旦所有人开始支持并理解你的时候，一切就事半功倍了。</p>
<p>在整个过程中我希望自己是个服务者，而不是高高在上的管理者。随着项目的进展，所有人都变得非常主动，终于感觉这次PM没白当。：）</p>
<p><strong>3.攻人之过勿太严，要思其堪受；教人以善勿过高，当使其可从。</strong></p>
<p>这是我最喜欢的名言之一。别人有错，职责时的方式该如何？不应过严，要考虑他的心理承受能力；引导别人，该怎样去衡量标准？不应过高，要考虑他的实际改善能力。我相信能做到这两点，就能至少赢得下属的尊重和信任了。</p>
<p><strong>4.初期定位</strong></p>
<p>初期的产品定位和策划不明确，是件很恐怖的事情。假如时间再紧，就更雪上加霜了。这次项目就有这个特点，中间进行修改使得很多人都不爽。细节的完善并不会影响到人的情绪，大方向的调整会给人一种“我白做了那么多”的感觉。这是要极力避免的现象。</p>
<p><strong>5.交流工具</strong></p>
<p>我想任何企业都有内部的交流工具，比如MSN、RTX什么的。但我感觉，除非很简单明了的事情，否则一切都不如当面叙说来得好！面对面的谈话所传达的信息非常多，也利于理解和加深双方的熟悉感。前端时间豆浆和我在改一个小东西时就因为误解邮件中的一句极简单的话造成许多无谓的错误和重复工作，如果最初当面花1分钟说清楚的话，一切就简单多了。</p>
<p><strong>6.懂技术的产品经理</strong></p>
<p>PM懂技术是优势，因为能更好地把握产品和理解开发进度。但是技术出身的PM也具备一般程序员的弱项——主观意识强、交谈口才欠缺等。世界上果然没有十全十美的东西。</p>
<p>暂时写这些，后续想到补充。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/12/15/484.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>第四届 D2前端技术论坛</title>
		<link>http://army8735.org/2009/12/09/477.html</link>
		<comments>http://army8735.org/2009/12/09/477.html#comments</comments>
		<pubDate>Wed, 09 Dec 2009 05:45:06 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[其它]]></category>
		<category><![CDATA[d2前端技术论坛]]></category>

		<guid isPermaLink="false">http://www.army8735.org/?p=477</guid>
		<description><![CDATA[2005 年前端工程师的职位开始在中国出现，2007 年第一届 D2 前端技术论坛在杭州举行，从此 D2 伴随着中国的前端一起成长，从此越来越多的互联网产品的背后有着职业前端工程师坚定的身影。在 D2 上我们曾交流过某项具体的前端技术，也曾探讨过团队协作的方式，那些美妙的话题都深入到我们频繁敲键盘的指尖。当架构、安全、性能等等在前端的领域扎根的时候，我们自己也在悄然发生质变，而推动这些变化的人和事正是 D2 想告诉你的。
我报名了，不知道何时能审核：

宣传视频：

]]></description>
			<content:encoded><![CDATA[<blockquote><p>2005 年前端工程师的职位开始在中国出现，2007 年第一届 D2 前端技术论坛在杭州举行，从此 D2 伴随着中国的前端一起成长，从此越来越多的互联网产品的背后有着职业前端工程师坚定的身影。在 D2 上我们曾交流过某项具体的前端技术，也曾探讨过团队协作的方式，那些美妙的话题都深入到我们频繁敲键盘的指尖。当架构、安全、性能等等在前端的领域扎根的时候，我们自己也在悄然发生质变，而推动这些变化的人和事正是 D2 想告诉你的。</p></blockquote>
<p>我报名了，不知道何时能审核：<br />
<a href="http://www.d2forum.org/d2/4/" target="_blank"><img src="http://www.d2forum.org/d2/4/assets/img/d2-180x250.gif" alt="第四届 D2前端技术论坛 (12月19日·杭州)" width="180" height="250" /></a><br />
宣传视频：<br />
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="400" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="align" value="middle" /><param name="src" value="http://player.youku.com/player.php/sid/XMTM2NDI0MDU2/v.swf" /><param name="quality" value="high" /><embed type="application/x-shockwave-flash" width="480" height="400" src="http://player.youku.com/player.php/sid/XMTM2NDI0MDU2/v.swf" quality="high" align="middle"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/12/09/477.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>逐渐使用css3</title>
		<link>http://army8735.org/2009/12/08/466.html</link>
		<comments>http://army8735.org/2009/12/08/466.html#comments</comments>
		<pubDate>Tue, 08 Dec 2009 08:41:07 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[css3]]></category>

		<guid isPermaLink="false">http://www.army8735.org/?p=466</guid>
		<description><![CDATA[pchome首页右栏今日进行了调整，最初设计稿效果如图：

可以看到，浮动框出来的样式，如果用以前方式来处理的话，需要拼合几张图片才能做到，而且产生的多余tag亦会让人不爽。基于渐进增强的思想，似乎使用css3来做是最好的办法。
来看个例子：/wp-content/uploads/2009/12/pchome-right.html
在支持css3的浏览器下，浮动框的阴影效果很好的表现了出来；然而不支持css3的浏览器在使用上并没有任何的影响——右侧和下方的阴影消失了——甚至以美学角度来看都无伤大雅。这种效果的代码结构和可维护性都很好，综合考虑下来最为可取，那么css3在是否可以以后的运用中越来越多地出现呢？
至于为何浮动层的矩形边线没有完整封闭，那是因为将其藏在右边列表下面，被列表的背景色给覆盖住造成的假象。
]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.pchome.net/" target="_blank">pchome首页</a>右栏今日进行了调整，最初设计稿效果如图：<br />
<img class="aligncenter size-full wp-image-467" title="pchome-right" src="/wp-content/uploads/2009/12/pchome-right.png" alt="pchome-right" width="443" height="642" /></p>
<p>可以看到，浮动框出来的样式，如果用以前方式来处理的话，需要拼合几张图片才能做到，而且产生的多余tag亦会让人不爽。基于<a href="http://lifesinger.org/blog/2008/10/understand-progressive-enhancement/" target="_blank">渐进增强</a>的思想，似乎使用css3来做是最好的办法。</p>
<p>来看个<a href="/wp-content/uploads/2009/12/pchome-right.html" target="_blank">例子</a>：<a href="/wp-content/uploads/2009/12/pchome-right.html" target="_blank">/wp-content/uploads/2009/12/pchome-right.html</a></p>
<p>在支持css3的浏览器下，浮动框的阴影效果很好的表现了出来；然而不支持css3的浏览器在使用上并没有任何的影响——右侧和下方的阴影消失了——甚至以美学角度来看都无伤大雅。这种效果的代码结构和可维护性都很好，综合考虑下来最为可取，那么css3在是否可以以后的运用中越来越多地出现呢？</p>
<p>至于为何浮动层的矩形边线没有完整封闭，那是因为将其藏在右边列表下面，被列表的背景色给覆盖住造成的假象。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/12/08/466.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>夭折的视频播放器</title>
		<link>http://army8735.org/2009/12/04/454.html</link>
		<comments>http://army8735.org/2009/12/04/454.html#comments</comments>
		<pubDate>Fri, 04 Dec 2009 03:32:59 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[as、flex]]></category>
		<category><![CDATA[onlylady]]></category>
		<category><![CDATA[幻世录2]]></category>
		<category><![CDATA[月空下的思念]]></category>
		<category><![CDATA[视频播放器]]></category>

		<guid isPermaLink="false">http://www.army8735.org/?p=454</guid>
		<description><![CDATA[最近onlylady有一个需求，要制作一款自定义flash视频播放器。晕啊，as视频编程我可没有接触过，以前只是拖个组件写个列表，为pchome视频频道页做过，和如今的要求相差太远。没办法，目前能写as的也只有我了，硬着头皮上了。好在今天完成了基础功能，心想总算过来了，哪知最坏的消息过来了……
早上收到美国总部CBS邮件通知，这款播放器初衷是和CBS的全球视频播放器接口相通，由他们提供各种信息资源，我们来播放就够了。orz，产品部在最初就弄错了，可谓相当大的失误。而以我们的技术实力，能不能做到还是个未知数。面对CBSi的密密麻麻的英文文档，我只有傻眼的份……
早上和前段时间刚离职的zol中关村在线同事聊天，还在说些技术和工作上的事。silverlight不知何年何月能够占有比较大的份额，那时不知我会不会去瞅瞅。如果sl也深入的话，一个人的精力恐怕是不够的。广泛和专精，似乎永远都是鱼与熊掌。前端这个领域，不知道我涉及得到底是不是正好呢？
目前做到的程度，视频是我曾经传到土豆上的一段游戏结尾曲《月空下的思念》（幻世录2）：

PS：期间把土豆和优库的视频播放器外壳下来看了下，两者都在100k左右，土豆的稍大一点。比较奇特的是两家风格大相径庭：土豆用了flex和pureMVC；优库完全自己的库，最吃惊的居然还是基于as2的。最终我只能远观了，视频编程方面我并不擅长，也没打算深入过。
PS2：更新了lrc支持。
]]></description>
			<content:encoded><![CDATA[<p>最近<a href="http://www.onlylady.com/" target="_blank">onlylady</a>有一个需求，要制作一款自定义flash视频播放器。晕啊，as视频编程我可没有接触过，以前只是拖个组件写个列表，为<a href="http://www.pchome.net/" target="_blank">pchome</a>视频频道页做过，和如今的要求相差太远。没办法，目前能写as的也只有我了，硬着头皮上了。好在今天完成了基础功能，心想总算过来了，哪知最坏的消息过来了……</p>
<p>早上收到美国总部<a href="http://www.cbs.com/" target="_blank">CBS</a>邮件通知，这款播放器初衷是和CBS的全球视频播放器接口相通，由他们提供各种信息资源，我们来播放就够了。orz，产品部在最初就弄错了，可谓相当大的失误。而以我们的技术实力，能不能做到还是个未知数。面对CBSi的密密麻麻的英文文档，我只有傻眼的份……</p>
<p>早上和前段时间刚离职的<a href="http://www.zol.com.cn/" target="_blank">zol中关村在线</a>同事聊天，还在说些技术和工作上的事。silverlight不知何年何月能够占有比较大的份额，那时不知我会不会去瞅瞅。如果sl也深入的话，一个人的精力恐怕是不够的。广泛和专精，似乎永远都是鱼与熊掌。前端这个领域，不知道我涉及得到底是不是正好呢？</p>
<p>目前做到的程度，视频是我曾经传到土豆上的一段游戏结尾曲《月空下的思念》（幻世录2）：</p>
<p align="center"><object id="video" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="631" height="527" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="flashvars" value="w=400&amp;h=320&amp;url=http://event.onlylady.com/ol_player/hsl2.f4v&amp;lrc=http://event.onlylady.com/ol_player/月空下的思念.lrc" /><param name="src" value="http://event.onlylady.com/ol_player/index.swf" /><param name="name" value="video" /><param name="allowfullscreen" value="true" /><embed id="video" type="application/x-shockwave-flash" width="631" height="527" src="http://event.onlylady.com/ol_player/index.swf" name="video" flashvars="w=400&amp;h=320&amp;url=http://event.onlylady.com/ol_player/hsl2.f4v&amp;lrc=http://event.onlylady.com/ol_player/月空下的思念.lrc" allowfullscreen="true"></embed></object></p>
<p>PS：期间把土豆和优库的视频播放器外壳下来看了下，两者都在100k左右，土豆的稍大一点。比较奇特的是两家风格大相径庭：土豆用了flex和pureMVC；优库完全自己的库，最吃惊的居然还是基于as2的。最终我只能远观了，视频编程方面我并不擅长，也没打算深入过。</p>
<p>PS2：更新了lrc支持。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/12/04/454.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>JAse开发点记</title>
		<link>http://army8735.org/2009/12/01/448.html</link>
		<comments>http://army8735.org/2009/12/01/448.html#comments</comments>
		<pubDate>Tue, 01 Dec 2009 10:19:39 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[JAse]]></category>
		<category><![CDATA[as、flex]]></category>
		<category><![CDATA[textfield]]></category>

		<guid isPermaLink="false">http://www.army8735.org/?p=448</guid>
		<description><![CDATA[JAse开源项目缓慢进展中，许多地方和许多算法不断地颠覆、重写，这也是预料当中的。目前来说总体架构是不会变的了：以一个swf文件为基础提供文本框和一些菜单栏的基本功能；语法分析插件以不同语法分发为各自的swf文件，用到的时候才去读取。
当然，所有的语法文件都是面向接口编程的（实现IParser接口即可，这个理念继承自jssc5），这也使得按需加载成为可能。废话不说，来看几张预览图。
开始的选择画面：

第一个被我“强奸”的是js（最熟么），其它的会陆续添上。我默认放了一段Mootools的代码：

拉一拉滚动条，性能还不错，就是有不少bug还没修正：

至于语法编辑器用到的破损、修复、高亮模型，我非常感谢http://www.cppblog.com/megax/archive/2008/07/09/55753.html这篇文章给予的提示，同时也感谢其作者和我互通邮件的帮助。不过Megax所采用的依然是分区正则法，并非DFA状态图分析。而C++的性能远非as3所能匹敌，所以JAse目前仅能做到千行级代码，上万行后就会感觉到卡。
as3的性能瓶颈其实并不是主因，主因是textfield的性能太弱。实际测试中只是在第一次装载大量文本（万行）会卡，在拖动分析的过程中却非常流畅。今天我装了CS4（以前一直用CS3），启用了GPU硬件加速来提高渲染textformat的速度，所以如果不计textfield的拖后腿的话，预计是能达到十万行的量的。如果分析状态的数据结构由数组换成Vector（类似Java的泛型ArrayList，基于性能考虑我决定JAse将基于Flash Player 10），那么百万行级也不是不可能。
所以在JAse基本完成后，如果自定义优化textfield的性能，则是重点。目前思路有点苗头，是否能实现就难说了。另外Text Layout Framework中并未包含什么高效的input组件，本来还想瞅瞅，结果失望而归。
]]></description>
			<content:encoded><![CDATA[<p><a href="http://code.google.com/p/jase/" target="_blank">JAse</a>开源项目缓慢进展中，许多地方和许多算法不断地颠覆、重写，这也是预料当中的。目前来说总体架构是不会变的了：以一个swf文件为基础提供文本框和一些菜单栏的基本功能；语法分析插件以不同语法分发为各自的swf文件，用到的时候才去读取。</p>
<p>当然，所有的语法文件都是面向接口编程的（实现IParser接口即可，这个理念继承自<a href="http://code.google.com/p/jssc/" target="_blank">jssc5</a>），这也使得按需加载成为可能。废话不说，来看几张预览图。</p>
<p>开始的选择画面：</p>
<p><img class="aligncenter size-full wp-image-449" title="jase1" src="http://www.army8735.org/wp-content/uploads/2009/12/jase1.png" alt="jase1" width="550" height="318" /></p>
<p>第一个被我“强奸”的是js（最熟么），其它的会陆续添上。我默认放了一段Mootools的代码：</p>
<p><img class="aligncenter size-full wp-image-450" title="jase2" src="http://www.army8735.org/wp-content/uploads/2009/12/jase2.png" alt="jase2" width="550" height="318" /></p>
<p>拉一拉滚动条，性能还不错，就是有不少bug还没修正：</p>
<p><img class="aligncenter size-full wp-image-451" title="jase3" src="http://www.army8735.org/wp-content/uploads/2009/12/jase3.png" alt="jase3" width="550" height="318" /></p>
<p>至于语法编辑器用到的破损、修复、高亮模型，我非常感谢<a href="http://www.cppblog.com/megax/archive/2008/07/09/55753.html">http://www.cppblog.com/megax/archive/2008/07/09/55753.html</a>这篇文章给予的提示，同时也感谢其作者和我互通邮件的帮助。不过Megax所采用的依然是分区正则法，并非DFA状态图分析。而C++的性能远非as3所能匹敌，所以JAse目前仅能做到千行级代码，上万行后就会感觉到卡。</p>
<p>as3的性能瓶颈其实并不是主因，主因是textfield的性能太弱。实际测试中只是在第一次装载大量文本（万行）会卡，在拖动分析的过程中却非常流畅。今天我装了CS4（以前一直用CS3），启用了GPU硬件加速来提高渲染textformat的速度，所以如果不计textfield的拖后腿的话，预计是能达到十万行的量的。如果分析状态的数据结构由数组换成Vector（类似Java的泛型ArrayList，基于性能考虑我决定JAse将基于Flash Player 10），那么百万行级也不是不可能。</p>
<p>所以在JAse基本完成后，如果自定义优化textfield的性能，则是重点。目前思路有点苗头，是否能实现就难说了。另外Text Layout Framework中并未包含什么高效的input组件，本来还想瞅瞅，结果失望而归。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/12/01/448.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>为TextField添加上tab缩进功能</title>
		<link>http://army8735.org/2009/11/25/435.html</link>
		<comments>http://army8735.org/2009/11/25/435.html#comments</comments>
		<pubDate>Wed, 25 Nov 2009 08:36:27 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[JAse]]></category>
		<category><![CDATA[as、flex]]></category>
		<category><![CDATA[tab缩进]]></category>
		<category><![CDATA[textfield]]></category>

		<guid isPermaLink="false">http://www.army8735.org/?p=435</guid>
		<description><![CDATA[今天尝试为JAse的代码输入框添加tab缩进功能，华丽地失败了。仔细查阅下手册：
指定此对象是否遵循 Tab 键顺序。 如果该对象遵循 Tab 键顺序，值为 true；否则值为 false。 默认情况下，值为 false，但以下情况除外：

对于 SimpleButton 对象，值为 true。
对于具有 type = &#8220;input&#8221; 的 TextField 对象，值为 true。
对于具有...]]></description>
			<content:encoded><![CDATA[<p>今天尝试为JAse的代码输入框添加tab缩进功能，华丽地失败了。仔细查阅下手册：</p>
<blockquote><p>指定此对象是否遵循 Tab 键顺序。 如果该对象遵循 Tab 键顺序，值为 true；否则值为 false。 默认情况下，值为 false，但以下情况除外：</p>
<ul>
<li>对于 SimpleButton 对象，值为 true。</li>
<li>对于具有 type = &#8220;input&#8221; 的 TextField 对象，值为 true。</li>
<li>对于具有 buttonMode = true 的 Sprite 对象或 MovieClip 对象，值为 true。</li>
</ul>
</blockquote>
<p>原来如此，侦听TextField的按键事件，根本就判断不到是否是tab键，因为默认系统就屏蔽了。这样就无法改写默认响应方法，tab键变成了在按钮之间切换选择状态。</p>
<p>得儿，得罪不起你，去请你的上司吧。直接侦听stage的按键事件，改写其默认行为搞定。唯一有点不友好的地方是聚焦会先出现在按钮上，然后由改写的行为迅速回到输入框上并进行缩进。没办法，也只好这样了。</p>
<pre class="brush:as">//全局侦听tag键，改变默认行为
addEventListener(Event.ADDED_TO_STAGE, function() {
	stage.addEventListener(KeyboardEvent.KEY_DOWN, function(event:KeyboardEvent):void {
		if (stage.focus == tf &#038;&#038; event.keyCode == Keyboard.TAB) {
			saveInput();
			getSelection();
			//如果鼠标为选择区域，则先保存删除命令链
			if (left != right) {
				saveDelete();
			}
			//增加2个全角空格，增加输入命令链
			commandList.addCommand(new InputCommand(tf, tf.caretIndex, "\u3000\u3000"));
			tf.replaceSelectedText("\u3000\u3000");
			setIndex();
			focus();
		}
	});
	stage.addEventListener(KeyboardEvent.KEY_UP, function(event:KeyboardEvent):void {
		if (event.keyCode == Keyboard.TAB) {
			focus();
		}
	});
});</pre>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/11/25/435.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>说说最近在开发的JAse语法编辑器</title>
		<link>http://army8735.org/2009/11/24/428.html</link>
		<comments>http://army8735.org/2009/11/24/428.html#comments</comments>
		<pubDate>Tue, 24 Nov 2009 06:30:12 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[JAse]]></category>

		<guid isPermaLink="false">http://www.army8735.org/?p=428</guid>
		<description><![CDATA[最近时间比较少，年底之前公布JAse预览版的可能性看来不高了，那就拖到农历年底之前吧，XD～
主要想说的是JAse进展得益于JAte的经验非常多，若不是之前有过长时间的尝试经验（尽管JAte作为实验品失败了），JAse不可能一帆风顺地进行过来。曾经读到一篇文章——《趁年轻，赶紧去做该做的傻事》，里面德鲁克的话让我感受颇深：
“我绝对不会把从未犯错的人升到高层领导岗位上，因为没有犯过大错的人必然是平庸之辈。更糟糕的是，没有犯过错的人将不会学到如何及早找出错误，并且改正错误。在工作中，如果一个同事谈论他的成功，我只会谨慎的倾听，小心的吸取；但如果一个人介绍过去的失误，我一定会竖起耳朵，绝不放过，因为这样的人往往经验丰富，思考全面，在失误基础上的成功也是最迷人的成功。（所以，在简历中，我们是否应该写上自己曾经做过哪些傻事？）”
JAse就是如此，若没有jssc的5版开发经历、没有JAte的研究结果，它就不会出现在我的手中了。至于JAte，最主要的失败原因是对flash对文本支持的不了解，flash对文本的支持实在有限，而图文混排更是可怜。我想得等as4时代来临，flash对富文本的支持才会好些吧。不过我倒是和KindEditor的作者Roddy经常聊天，也一直在催他（现在又要催了！哈哈！）把KE可视化原理写出来！或许以后JAte会变成基于KE（LGPL）用as制作toolbar，iframe做content的样式。
附上缓慢进展的JAse预览图一张，可以看到ui方面受以前影响颇多：

PS：JAte的图标取自于Open Office，JAse的图标取自于Flash Develop。
]]></description>
			<content:encoded><![CDATA[<p>最近时间比较少，年底之前公布<a href="http://code.google.com/p/jase/" target="_blank">JAse</a>预览版的可能性看来不高了，那就拖到农历年底之前吧，XD～</p>
<p>主要想说的是JAse进展得益于<a href="http://code.google.com/p/jate/" target="_blank">JAte</a>的经验非常多，若不是之前有过长时间的尝试经验（尽管JAte作为实验品失败了），JAse不可能一帆风顺地进行过来。曾经读到一篇文章——《<span>趁年轻，赶紧去做该做的傻事》，里面</span>德鲁克的话让我感受颇深：</p>
<blockquote><p>“我绝对不会把从未犯错的人升到高层领导岗位上，因为没有犯过大错的人必然是平庸之辈。更糟糕的是，没有犯过错的人将不会学到如何及早找出错误，并且改正错误。在工作中，如果一个同事谈论他的成功，我只会谨慎的倾听，小心的吸取；但如果一个人介绍过去的失误，我一定会竖起耳朵，绝不放过，因为这样的人往往经验丰富，思考全面，在失误基础上的成功也是最迷人的成功。（所以，在简历中，我们是否应该写上自己曾经做过哪些傻事？）”</p></blockquote>
<p>JAse就是如此，若没有<a href="http://code.google.com/p/jssc/" target="_blank">jssc</a>的5版开发经历、没有JAte的研究结果，它就不会出现在我的手中了。至于JAte，最主要的失败原因是对flash对文本支持的不了解，flash对文本的支持实在有限，而图文混排更是可怜。我想得等as4时代来临，flash对富文本的支持才会好些吧。不过我倒是和<a href="http://www.kindsoft.net/" target="_blank">KindEditor</a>的作者Roddy经常聊天，也一直在催他（现在又要催了！哈哈！）把KE可视化原理写出来！或许以后JAte会变成基于KE（<a href="http://www.opensource.org/licenses/lgpl-license.php" target="_blank">LGPL</a>）用as制作toolbar，iframe做content的样式。</p>
<p>附上缓慢进展的JAse预览图一张，可以看到ui方面受以前影响颇多：</p>
<p><img class="aligncenter size-full wp-image-430" title="jase" src="http://www.army8735.org/wp-content/uploads/2009/11/jase.png" alt="jase" width="552" height="315" /></p>
<p>PS：JAte的图标取自于<a href="http://zh.openoffice.org/index_gb.html" target="_blank">Open Office</a>，JAse的图标取自于<a href="http://www.flashdevelop.org/" target="_blank">Flash Develop</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/11/24/428.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>《Project Darkstar》中文文档</title>
		<link>http://army8735.org/2009/11/18/426.html</link>
		<comments>http://army8735.org/2009/11/18/426.html#comments</comments>
		<pubDate>Wed, 18 Nov 2009 05:39:37 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[Project Darkstar]]></category>
		<category><![CDATA[中文文档]]></category>

		<guid isPermaLink="false">http://www.army8735.org/?p=426</guid>
		<description><![CDATA[和我同住的某人开始把翻译好的《Project Darkstar》中文文档上传至Blog了，在这里宣传转一下。
地址：http://www.david0446.com/?p=6
]]></description>
			<content:encoded><![CDATA[<p>和我同住的某人开始把翻译好的<a href="http://www.david0446.com/?p=6" target="_blank">《Project Darkstar》中文文档</a>上传至Blog了，在这里宣传转一下。</p>
<p>地址：<a href="http://www.david0446.com/?p=6" target="_blank">http://www.david0446.com/?p=6</a></p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/11/18/426.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>动态加载css和js资源</title>
		<link>http://army8735.org/2009/11/16/416.html</link>
		<comments>http://army8735.org/2009/11/16/416.html#comments</comments>
		<pubDate>Mon, 16 Nov 2009 04:37:01 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[动态加载]]></category>

		<guid isPermaLink="false">http://www.army8735.org/?p=416</guid>
		<description><![CDATA[近日，在秦歌的blog中看到动态加载的内容，主要是说YUI3.0在动态加载js的一点小bug。当时想了个问题：如何侦听动态加载的内容被成功加载了，翻阅一番后记录下来。
Omar AL Zabir曾在他的Ensure library库中写过类似的东西，用以动态加载css和js文件。加载js时，基本伪代码是这样：
var script = document.createElement("script");
script.onreadystatechange = function() {
	//
}
script.onload = function () {
	//
}
script.setAttribute("src", "xxx.js");
document.head.appendChild(script);
原理就是创建一个script标签，设置src属性后附加到head区域。ie中侦听onreadystatechange，其它的侦听onload。不过有些较古老的浏览器对这两种方法都不支持（Omar举例为Safari 2），解决的办法是通过xhr加载js文件资源，再eval()出来。目前来看，似乎已没必要花费经历去为了旧版本兼容了。
凑巧，前几日Mootools官网也发布了一个延迟独立加载js的库Depender，它被包括在More中（貌似1.2.4.1还有些性能问题，我希望最终它能够被包含在Core中）。看看其核心实现：
new Element('script', {
	src: scriptPath +...]]></description>
			<content:encoded><![CDATA[<p>近日，在<a href="http://dancewithnet.com/2009/11/05/a-little-practice-about-js-lazy-load/" target="_blank">秦歌的blog中看到动态加载</a>的内容，主要是说YUI3.0在动态加载js的一点小bug。当时想了个问题：如何侦听动态加载的内容被成功加载了，翻阅一番后记录下来。</p>
<p><a href="http://msmvps.com/blogs/omar " target="_blank">Omar AL Zabir</a>曾在他的<span style="color: #0000ff;">Ensure library</span>库中写过类似的东西，用以动态加载css和js文件。加载js时，基本伪代码是这样：</p>
<pre class="brush:js">var script = document.createElement("script");
script.onreadystatechange = function() {
	//
}
script.onload = function () {
	//
}
script.setAttribute("src", "xxx.js");
document.head.appendChild(script);</pre>
<p>原理就是创建一个script标签，设置src属性后附加到head区域。ie中侦听<span style="color: #0000ff;">onreadystatechange</span>，其它的侦听<span style="color: #0000ff;">onload</span>。不过有些较古老的浏览器对这两种方法都不支持（Omar举例为Safari 2），解决的办法是通过xhr加载js文件资源，再eval()出来。目前来看，似乎已没必要花费经历去为了旧版本兼容了。</p>
<p>凑巧，前几日Mootools官网也发布了一个延迟独立加载js的库<a href="http://mootools.net/blog/2009/11/09/mootools-depender-a-build-tool-for-mootools-javascript-libraries/" target="_blank">Depender</a>，它被包括在<a href="http://www.mootools.net/more" target="_blank">More</a>中（貌似1.2.4.1还有些性能问题，我希望最终它能够被包含在<a href="http://www.mootools.net/core" target="_blank">Core</a>中）。看看其核心实现：</p>
<pre class="brush:js">new Element('script', {
	src: scriptPath + (this.options.noCache ? '?noCache=' + new Date().getTime() : ''),
	events: {
		load: loaded,
		readystatechange: function(){
			if (['loaded', 'complete'].contains(this.readyState)) loaded();
		},
		error: error
	}
}).inject(this.options.target || document.head);</pre>
<p>亦是一样。不过在我测试过程中，ie的<span style="color: #0000ff;">readystate</span>的值在加载完成后都是<span style="color: #0000ff;">loaded</span>（ie6、7、8），并没有出现<span style="color: #0000ff;">complete</span>的情况，不知道mt为何要多判别它。</p>
<p>另外关于css动态加载，似乎并没有可行的侦听的方法。虽然ie中侦听<span style="color: #0000ff;">onreadystatechange</span>的值为<span style="color: #0000ff;">complete</span>，但是firefox 3.5和chrome里却侦听不到<span style="color: #0000ff;">onload</span>事件。不过一般情况下，样式的加载都是异步的，同步的需求很少。除非考虑到某个地方必须等样式加载成功后才去执行显示，否则没必要使用同步。</p>
<p>我能想到的解决办法就是通过xhr读取css文件内容，然后插入到style标签中来实现同步读取，不知道可不可行。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/11/16/416.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>web端语法高亮原理：走进jssc的世界（三）</title>
		<link>http://army8735.org/2009/11/13/390.html</link>
		<comments>http://army8735.org/2009/11/13/390.html#comments</comments>
		<pubDate>Fri, 13 Nov 2009 02:55:58 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[jssc]]></category>
		<category><![CDATA[系列文章]]></category>
		<category><![CDATA[语法高亮原理]]></category>

		<guid isPermaLink="false">http://www.army8735.org/?p=390</guid>
		<description><![CDATA[区分正则
想要高亮正则表达式的前提是区分它。由于正则表达式以/开头，因此这和除法、单行注释、多行注释会产生混淆。那么究竟该如何辨别呢？

注释的区别最为简单，因为注释拥有最高优先级。除非注释符出现在字符串内，否则一旦开始，编译器便立刻识别//和/*。
如图所示，当向前看字符为斜线时，根据规则由初始状态0进入状态1。如果接下来读入的字符是*，则进入状态2，并成为多行注释；倘若是/，则进入状态3，变成单行注释；而其它的情况么，则是正则除法皆有可能。
我们不妨设想一下：正则和除法有哪些区别？从语境上说，原则性的区别就是除法的前面是被除数，正则的前面不是。而成为被除数的可能无非是以下3种：数字、变量、括号内的运算结果。我不知道js解析引擎是如何做的，它可能相当得严谨（也简单，因为它会忽略空白和注释）。但是在web端语法高亮的需求中，由于假设输入的代码一定是正确的，所以可以“投机取巧”般地识别二者：
//检查当前除号是否是perl风格正则表达式开头
protected function isPerlReg():Boolean {
	for (var j:int = index - 3; j &#62; -1; j--) {
		//先要忽略之前的单行注释
		if (j == singleCommentEnd) {
			j =...]]></description>
			<content:encoded><![CDATA[<h3>区分正则</h3>
<p>想要高亮正则表达式的前提是区分它。由于正则表达式以<span style="color: #0000ff;">/</span>开头，因此这和除法、单行注释、多行注释会产生混淆。那么究竟该如何辨别呢？</p>
<p><img class="aligncenter size-full wp-image-391" title="jssc-article-3-1" src="http://www.army8735.org/wp-content/uploads/2009/11/jssc-article-3-1.gif" alt="jssc-article-3-1" width="284" height="219" /></p>
<p>注释的区别最为简单，因为注释拥有<strong>最高优先级</strong>。除非注释符出现在字符串内，否则一旦开始，编译器便立刻识别<span style="color: #0000ff;">//</span>和<span style="color: #0000ff;">/*</span>。</p>
<p>如图所示，当向前看字符为斜线时，根据规则由初始状态0进入状态1。如果接下来读入的字符是<span style="color: #0000ff;">*</span>，则进入状态2，并成为多行注释；倘若是<span style="color: #0000ff;">/</span>，则进入状态3，变成单行注释；而其它的情况么，则是正则除法皆有可能。</p>
<p>我们不妨设想一下：正则和除法有哪些区别？从语境上说，原则性的区别就是除法的前面是被除数，正则的前面不是。而成为被除数的可能无非是以下3种：数字、变量、括号内的运算结果。我不知道js解析引擎是如何做的，它可能相当得严谨（也简单，因为它会忽略空白和注释）。但是在web端语法高亮的需求中，由于假设输入的代码一定是正确的，所以可以“投机取巧”般地识别二者：</p>
<pre class="brush:as">//检查当前除号是否是perl风格正则表达式开头
protected function isPerlReg():Boolean {
	for (var j:int = index - 3; j &gt; -1; j--) {
		//先要忽略之前的单行注释
		if (j == singleCommentEnd) {
			j = singleCommentStart;
			continue;
		}
		var cc:int = code.charCodeAt(j);
		//忽略空白
		if (Character.isBlank(cc) || Character.isLine(cc)) {
			continue;
		}
		//前面也是一个除号的话防止是多行结尾注释，需跳过注释段继续向前找
		else if (Character.isSlash(cc) &amp;&amp; Character.isStar(code.charCodeAt(j - 1))) {
			j = code.lastIndexOf("/*", j - 1);
		}
		//前面一个非空白字符若是字母数字下划线则为正则，否则除号
		else if (Character.isLetterOrDigit(cc) || Character.isUnderline(cc) || Character.isDollar(cc) || Character.isRightParenthese(cc)) {
			return false;
		}
		else {
			break;
		}
	}
	return true;
}</pre>
<p>程序开始<strong>回溯</strong>，我们要看在斜线符号之前的“东西”到底是什么——当然，空白符是不算在内的，因此循环体内首先要做的就是忽略空白。假如前面是标识符的组成或者右括号的话，那么显然它是个被除数，斜线也应该被解读为除号；而如果是其它情况的话，斜线则是正则表达式的开头。</p>
<p>除此外还有个很大的难点，就是斜线前面插入了一段注释，这也需要忽略（js引擎无需考虑这些，因为一开始注释就被剔除了，所以它会相对简单一些）。目前jssc 5 beta 3版仅能识别出多行注释，对于单行注释尚未解决，这是下个版本需要注意的问题。</p>
<blockquote><p>注：新版本已从语法层级上解决，方法为设置一个布尔标量，每次处理token时切换其正确状态，上述代码在新版本中已废弃。</p></blockquote>
<h3>识别正则</h3>
<p>区分出来之后别是识别正则，根据特点我们画出这样的DFA：</p>
<p><img class="aligncenter size-full wp-image-401" title="jssc-article-3-2" src="http://www.army8735.org/wp-content/uploads/2009/11/jssc-article-3-2.gif" alt="jssc-article-3-2" width="365" height="237" /></p>
<p>我们假设当前<span style="color: #0000ff;">/</span>已经被识别为正则表达式的开头，那么接下来的任务便是寻找另外一个结尾<span style="color: #0000ff;">/</span>。</p>
<p>在状态1中首先要明确对待的就是转义符，因为它会转义掉下面一个符号，所以要单独为其区分出一个状态2。而状态2上的条件就很宽松了：无论输入什么都会被转义掉，因此遇到任何字符（any）都会回到状态1。</p>
<p>值得注意的是，字符集（<span style="color: #0000ff;">[]</span>）是个特殊之处，因为它里面可以省略转义符（既可以有也可以没有），为此需要画出状态3和状态4来对待它（字符集中出现未被转义的<span style="color: #0000ff;">/</span>也是合法的，你不可能把它当作正则表达式的结束符）。</p>
<p>当真正出现结束符后进入状态5，此时正则并没有完全结束，因为后面还可能跟有譬如<span style="color: #0000ff;">i</span>这样的<strong>flag</strong>。在状态5上多加种条件循环会本身便可解决问题（更严谨的做法应该是flag仅允许出现一次）；而其它情况才是最终进入终结状态6。</p>
<pre class="brush:as">//perl正则
protected function dealPerlReg():void {
	var start:int = index - 2;
	var cc:int;
	outer:
	while (index &lt;= code.length) {
		readch();
		cc = peek.charCodeAt(0);
		//转义符
		if (Character.isBackslash(cc)) {
			readch();
			//后面是换行跳出
			if (Character.isLine(peek.charCodeAt(0))) {
				break;
			}
		}
		//[括号
		else if (Character.isLeftbracket(cc)) {
			while (index &lt;= code.length) {
				readch();
				cc = peek.charCodeAt(0);
				//转义符
				if (Character.isBackslash(cc)) {
					readch();
					if (Character.isLine(peek.charCodeAt(0))) {
						break outer;
					}
				}
				//]括号
				else if (Character.isRightbracket(cc)) {
					continue outer;
				}
			}
		}
		//行末尾
		else if (Character.isLine(cc)) {
			break;
		}
		//正则表达式/结束
		else if (Character.isSlash(cc)) {
			for (var i:int = 0; i &lt; 3 &amp;&amp; index &lt;= code.length; i++) {
				readch();
				cc = peek.charCodeAt(0);
				//是img中的一个，存入flag中，不分大小写
				if(cc == 103 || cc == 105 || cc == 109 || cc == 71 || cc == 73 || cc == 77) {
					//
				}
				//其它情况跳出
				else {
					break outer;
				}
			}
			break;
		}
	}
	//高亮
	result.append(HighLighter.regular(HtmlEscape.encode(code.slice(start, index - 1))));
}</pre>
<h3>其它</h3>
<p>其它符号的处理都很简单，不做任何高亮即可，只是要注意个别需要特殊编码的字符（如&amp;变为&amp;amp;）。在编译器的词法分析中，每个符号都是有语法意义的，有时符号的长度还不止一个（&lt;=就有2个符号）。幸运的是，在做语法高亮的情况下，这些全可以被“偷懒”掉。</p>
<p>下面贴一下c系列语言处理符号的代码，它相当得简单，可能唯一特殊之处就是其中对花括弧的深度处理了。</p>
<pre class="brush:as">//抽象类中的处理符号方法
protected function dealSign():void {
	result.append(HtmlEscape.encodeChar(peek));
	readch();
}

//c系列语言继承后覆盖的方法，处理符号同时要计算深度
protected override function dealSign():void {
	var cc:int = peek.charCodeAt(0);
	if (Character.isLeftBrace(cc)) {
		depth++;
	}
	else if (Character.isRightBrace(cc)) {
		depth--;
	}
	super.dealSign();
}</pre>
<p>在<a href="http://army8735.org/2010/02/02/608.html">下一篇</a>中，我将讲述jssc 5是如何做到嵌套高亮（html中内嵌css和js）的。</p>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/11/13/390.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>拼图游戏中的逆序和</title>
		<link>http://army8735.org/2009/11/09/373.html</link>
		<comments>http://army8735.org/2009/11/09/373.html#comments</comments>
		<pubDate>Mon, 09 Nov 2009 07:51:11 +0000</pubDate>
		<dc:creator>army8735</dc:creator>
				<category><![CDATA[as、flex]]></category>
		<category><![CDATA[数据结构、算法]]></category>
		<category><![CDATA[拼图游戏]]></category>
		<category><![CDATA[逆序和]]></category>
		<category><![CDATA[逆序对]]></category>

		<guid isPermaLink="false">http://www.army8735.org/?p=373</guid>
		<description><![CDATA[这是我第3次做拼图游戏了。算法简单得很，而且是最少的9宫拼图，拿到设计稿后不外几个小时搞定。
记得2008年7月份，刚毕业被EOL拉进PCHome的时候，工作所做的第一个就是flash游戏拼图（9、16、25宫）。那时为了研究出如何防止拼图死局，着实花了不少功夫（小时候玩的金山文曲星中就经常出现这个bug）。为此我特地发了个帖子求助数学大人，最终问题得以解决。
那么，什么是拼图死局呢？
以最简单的9宫拼图为例，拼图的最终目的是将随机的数块移动到如下状态：
1 2 3
4 5 6
7 8
第9格是空的，用以让玩家能够任意移动某一个数块。但是，初始随机情况可能会是这样：
1 2 3
4 5 6
8 7
这种情况下，想要变换到正确的结束状态是不可能的。至于为什么，繁琐的数学证明就不贴了。记得曾经《读者》杂志上刊登过这个东西，说是国外某位数学家悬赏1000w美金让人来拼这个拼图，结果无一人能做到。最后他才公布为何不能做到：因为这种情况的开始逆序和和目标逆序和不同。
数学定义：
n*n的方格中放入1,2,3,…,n-1及一个空格，在任何一种状态A下，
定义：f(i)=k，表示i放在第k个格子中，k按照从左至右，从上到下排序；
定义：g(i,j)=1,如果(f(i)-f(j))(i-j)&#60;0；否则为0；
定义：F(A)=SUMg(i,j)，对所有i,j求和；
当n=3时；
目标状态F(Aobj)=0，你给的状态F(Abegin)=1；
而移动空格只有2种方式：在同行中移动不改变F值，在不同行中移动F值+2，-2或不变；
利用F函数可以解决所有n为奇数的情况；
当n为偶数时，定义F1(A)=F(A)+空格所在行&#38;1；处理方式一样。
当初始随机情况出现死局时，逆序和就会不相等，那么发现之后该怎么办呢？当然不会重新去随机一次，这太傻了。解开锁的钥匙很简单，随便交换同行的2个数块的位置即可。
那个时候写的一个产生9、16宫随机初始数块顺序的工厂（现在看来无需用工厂方法，一个类就能搞定）：
//GenMatrixFactory
package matrix {
	public class GenMatrixFactory {

		public static function getArray(i:int):Array {
			var...]]></description>
			<content:encoded><![CDATA[<p>这是我第3次做拼图游戏了。算法简单得很，而且是最少的9宫拼图，拿到设计稿后不外几个小时搞定。</p>
<p>记得2008年7月份，刚毕业被<a href="http://eol.squarecn.com/" target="_blank">EOL</a>拉进PCHome的时候，工作所做的第一个就是flash游戏拼图（9、16、25宫）。那时为了研究出如何防止<strong>拼图死局</strong>，着实花了不少功夫（小时候玩的金山文曲星中就经常出现这个bug）。为此我特地发了个<a href="http://bbs.ffsky.com/disp.aspx?ID=1669960&amp;bid=11" target="_blank">帖子</a>求助数学大人，最终问题得以解决。</p>
<p>那么，什么是<strong>拼图死局</strong>呢？</p>
<p>以最简单的9宫拼图为例，拼图的最终目的是将随机的数块移动到如下状态：</p>
<p><span style="color: #ff0000;">1 2 3</span></p>
<p><span style="color: #ff0000;">4 5 6</span></p>
<p><span style="color: #ff0000;">7 8</span></p>
<p>第9格是空的，用以让玩家能够任意移动某一个数块。但是，初始随机情况可能会是这样：</p>
<p><span style="color: #ff0000;">1 2 3</span></p>
<p><span style="color: #ff0000;">4 5 6</span></p>
<p><span style="color: #ff0000;">8 7</span></p>
<p>这种情况下，想要变换到正确的结束状态是不可能的。至于为什么，繁琐的数学证明就不贴了。记得曾经《读者》杂志上刊登过这个东西，说是国外某位数学家悬赏1000w美金让人来拼这个拼图，结果无一人能做到。最后他才公布为何不能做到：因为这种情况的<strong>开始逆序和</strong>和<strong>目标逆序和</strong>不同。</p>
<p>数学定义：</p>
<blockquote><p>n*n的方格中放入1,2,3,…,n-1及一个空格，在任何一种状态A下，<br />
定义：f(i)=k，表示i放在第k个格子中，k按照从左至右，从上到下排序；<br />
定义：g(i,j)=1,如果(f(i)-f(j))(i-j)&lt;0；否则为0；<br />
定义：F(A)=SUMg(i,j)，对所有i,j求和；<br />
当n=3时；<br />
目标状态F(Aobj)=0，你给的状态F(Abegin)=1；<br />
而移动空格只有2种方式：在同行中移动不改变F值，在不同行中移动F值+2，-2或不变；<br />
利用F函数可以解决所有n为奇数的情况；<br />
当n为偶数时，定义F1(A)=F(A)+空格所在行&amp;1；处理方式一样。</p></blockquote>
<p>当初始随机情况出现死局时，逆序和就会不相等，那么发现之后该怎么办呢？当然不会重新去随机一次，这太傻了。解开锁的钥匙很简单，随便<strong>交换同行的2个数块的位置</strong>即可。</p>
<p>那个时候写的一个产生9、16宫随机初始数块顺序的工厂（现在看来无需用工厂方法，一个类就能搞定）：</p>
<pre class="brush:as">//GenMatrixFactory
package matrix {
	public class GenMatrixFactory {

		public static function getArray(i:int):Array {
			var m:Matrix;
			switch(i) {
				case 3:
					m = new Matrix3();
				break;
				case 4:
					m = new Matrix4();
				break;
				default:
					throw new Error("none of matrix");
			}
			return m.getMatrix();
		}
	}
}</pre>
<pre class="brush:as">//Matrix
package matrix {
	interface Matrix {
		function getMatrix():Array;
	}
}</pre>
<pre class="brush:as">//Matrix3
package matrix {

	class Matrix3 implements Matrix {
		private var gameArray:Array;
		private var numList:Array;

		public function Matrix3() {
			initGameArray();
			initNumList();
		}

		private function initGameArray():void {
			//创建游戏拼图数组
			gameArray = new Array(8);
			for (var i:int = 0; i &lt; gameArray.length; i++) {
				gameArray[i] = 0;
			}
		}
		private function initNumList():void {
			//最终状态数组
			numList = new Array(8);
			for (var i:int = 1; i &lt;= numList.length; i++) {
 				numList[i - 1] = i;
 			}
 		}
 		public function getMatrix():Array {
 			//获取最终状态逆序和
 			var reverse:int = getReverse();
 			//产生0～array长度的随机数填充array
 			var i:int = 0;
 			while (numList.length &gt; 0) {
				var rand:int = Math.floor(Math.random() * numList.length);
				gameArray[i++] = numList.splice(rand, 1)[0];
			}
			return Validate.validata(gameArray, reverse);
		}

		private function getReverse():int {
			var sum:int = 0;
			for (var i:int = 0; i &lt; numList.length - 1; i++) {
				for (var j:int = i + 1; j &lt; numList.length; j++) {
					if ((i - j) * (numList[i] - numList[j]) &lt; 0) {
						sum++;
					}
				}
			}
			return sum;
		}
	}
}</pre>
<pre class="brush:as">//Matrix4
package matrix {

	class Matrix4 implements Matrix {
		private var gameArray:Array;
		private var numList:Array;

		public function Matrix4() {
			initGameArray();
			initNumList();
		}

		private function initGameArray():void {
			//创建游戏拼图数组
			gameArray = new Array(15);
			for (var i:int = 0; i &lt; gameArray.length; i++) {
				gameArray[i] = 0;
			}
		}
		private function initNumList():void {
			//最终状态数组
			numList = new Array(15);
			for (var i:int = 1; i &lt;= numList.length; i++) {
 				numList[i - 1] = i;
 			}
 		}
 		public function getMatrix():Array {
 			//获取最终状态逆序和
 			var reverse:int = getReverse();
 			//产生0～array长度的随机数填充array
 			var i:int = 0;
 			while (numList.length &gt; 0) {
				var rand:int = Math.floor(Math.random() * numList.length);
				gameArray[i++] = numList.splice(rand, 1)[0];
			}
			return Validate.validata(gameArray, reverse);
		}

		private function getReverse():int {
			var sum:int = 0;
			for (var i:int = 0; i &lt; numList.length - 1; i++) {
				for (var j:int = i + 1; j &lt; numList.length; j++) {
					if ((i - j) * (numList[i] - numList[j]) &lt; 0) {
						sum++;
					}
				}
			}
			return sum;
		}
	}
}</pre>
<pre class="brush:as">//Validate
package matrix {

	class Validate {
		public static function validata(gameArray:Array, reverse:int):Array {
			var sum:int = 0;
			for (var i:int = 0; i &lt; gameArray.length - 1; i++) {
				for (var j:int = i + 1; j &lt; gameArray.length; j++) {
					if ((i - j) * (gameArray[i] - gameArray[j]) &lt; 0) {
						sum++;
					}
				}
			}
			//逆序和不同时死局，调整同行相邻两个
			if (sum % 2 != reverse % 2) {
				var temp:int = 0;
				temp = gameArray[0];
				gameArray[0] = gameArray[1];
				gameArray[1] = temp;
			}
			return gameArray;
		}
	}
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://army8735.org/2009/11/09/373.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
