生成随机字符串的密封类

2010-5-11 11:35:11 开发者 抢沙发(0)

C# 代码 

 

  1. using   System;   
  2.   using   System.Security.Cryptography;   
  3.     
  4.   namespace   YNIT.Components   
  5.   {   
  6.   ///   <summary>   
  7.   ///   生成随机字符串的密封类,不能被继承   
  8.   ///   </summary>   
  9.   public   sealed   class   RandomStr     
  10.   {   
  11.   private   static   readonly   int   defaultLength   =   8;   
  12.     
  13.   private   static   int   GetNewSeed()   
  14.   {   
  15.   byte[]   rndBytes   =   new   byte[4];   
  16.   RNGCryptoServiceProvider   rng   =   new   RNGCryptoServiceProvider();   
  17.   rng.GetBytes(rndBytes);   
  18.   return   BitConverter.ToInt32(rndBytes,0);   
  19.   }   
  20.     
  21.   private   static   string   BuildRndCodeAll(int   strLen)     
  22.   {   
  23.   System.Random   RandomObj   =   new   System.Random(GetNewSeed());     
  24.   string   buildRndCodeReturn   =   null;   
  25.   for(int   i=0;   i<strLen;   i++)     
  26.   {   
  27.   buildRndCodeReturn   +=   (char)RandomObj.Next(33,125);   
  28.   }   
  29.   return   buildRndCodeReturn;   
  30.   }   
  31.     
  32.   #region   输出随机字符串   
  33.   ///   <summary>   
  34.   ///   输出长度为8的随机字符串   
  35.   ///   </summary>   
  36.   ///   <returns>长度为8的随机字符串</returns>   
  37.   public   static   string   GetRndStrOfAll()     
  38.   {   
  39.   return   BuildRndCodeAll(defaultLength);   
  40.   }   
  41.     
  42.   ///   <summary>   
  43.   ///   输出指定长度的随机字符串   
  44.   ///   </summary>   
  45.   ///   <param   name="LenOf">长度</param>   
  46.   ///   <returns>指定长度的随机字符串</returns>   
  47.   public   static   string   GetRndStrOfAll(int   LenOf)     
  48.   {   
  49.   return   BuildRndCodeAll(LenOf);   
  50.   }   
  51.   #endregion   
  52.         
  53.   private   static   string   sCharLow   =   "abcdefghijklmnopqrstuvwxyz";   
  54.   private   static   string   sCharUpp   =   "ABCDEFGHIJKLMNOPQRSTUVWXYZ";   
  55.   private   static   string   sNumber   =   "0123456789";   
  56.     
  57.   private   static   string   BuildRndCodeOnly(string   StrOf,int   strLen)     
  58.   {   
  59.   System.Random   RandomObj   =   new   System.Random(GetNewSeed());     
  60.   string   buildRndCodeReturn   =   null;   
  61.   for(int   i=0;   i<strLen;   i++)   
  62.   {   
  63.   buildRndCodeReturn   +=   StrOf.Substring(RandomObj.Next(0,StrOf.Length-1),1);   
  64.   }   
  65.   return   buildRndCodeReturn;   
  66.   }   
  67.     
  68.   #region   输出指定范围随机字符串   
  69.   ///   <summary>   
  70.   ///   输出长度为8的小写字母加数字的字符串   
  71.   ///   </summary>   
  72.   ///   <returns>长度为8的小写字母加数字的字符串</returns>   
  73.   public   static   string   GetRndStrOnlyFor()     
  74.   {   
  75.   return   BuildRndCodeOnly(sCharLow   +   sNumber,defaultLength);   
  76.   }   
  77.     
  78.   ///   <summary>   
  79.   ///   输出指定长度的小写字母加数字的字符串   
  80.   ///   </summary>   
  81.   ///   <param   name="LenOf">长度</param>   
  82.   ///   <returns>指定长度的小写字母加数字的字符串</returns>   
  83.   public   static   string   GetRndStrOnlyFor(int   LenOf)     
  84.   {   
  85.   return   BuildRndCodeOnly(sCharLow   +   sNumber,LenOf);   
  86.   }   
  87.     
  88.   ///   <summary>   
  89.   ///   输出长度为8的指定字符串   
  90.   ///   </summary>   
  91.   ///   <param   name="bUseUpper">是否含有大写字母</param>   
  92.   ///   <param   name="bUseNumber">是否含有数字</param>   
  93.   ///   <returns>长度为8的指定字符串</returns>   
  94.   public   static   string   GetRndStrOnlyFor(bool   bUseUpper,bool   bUseNumber)     
  95.   {   
  96.   string   strTmp   =   sCharLow;   
  97.   if   (bUseUpper)   strTmp   +=   sCharUpp;   
  98.   if   (bUseNumber)   strTmp   +=   sNumber;   
  99.     
  100.   return   BuildRndCodeOnly(strTmp,defaultLength);   
  101.   }   
  102.         
  103.   ///   <summary>   
  104.   ///   输出指定长度的指定字符串   
  105.   ///   </summary>   
  106.   ///   <param   name="LenOf">长度</param>   
  107.   ///   <param   name="bUseUpper">是否含有大写字母</param>   
  108.   ///   <param   name="bUseNumber">是否含有数字</param>   
  109.   ///   <returns>指定长度的指定字符串</returns>   
  110.   public   static   string   GetRndStrOnlyFor(int   LenOf,bool   bUseUpper,bool   bUseNumber)     
  111.   {   
  112.   string   strTmp   =   sCharLow;   
  113.   if   (bUseUpper)   strTmp   +=   sCharUpp;   
  114.   if   (bUseNumber)   strTmp   +=   sNumber;   
  115.     
  116.   return   BuildRndCodeOnly(strTmp,LenOf);   
  117.   }   
  118.   #endregion   
  119.   }    

 

[不得不转]如何更好地控制input输入框的高度

2010-5-11 10:24:29 开发者 抢沙发(0)

按:看到如此细致的文章,真的很爱~~ 转载如此。向作者致敬~! 

 

 

如何更好地控制input输入框的高度

作者:nwind类别:HTML/CSS, 前端技术 | 评论(3)

很久以前Roger Johansson就在他的blog上做了一个用样式控制表单元素的测试, 告诉我们企图用样式控制表单元素是一件不可能的事情

using CSS to style form controls to look exactly the same across browsers and platforms is impossible

甚至css2.1规范中也没有明确这方面的规定, 而是打算将它fix in future

CSS 2.1 does not define which properties apply to form controls and frames, or how CSS can be used to style them. User agents may apply CSS properties to these elements. Authors are recommended to treat such support as experimental. A future level of CSS may specify this further.

所以如果想要让表单元素在各个浏览器下完全一致, 最好的解决方法莫过于完全不理会操作系统的样式, 用自定义的ui风格, 就像bing或Google的Jazz UI那样

然而, 这会导致界面和用户的系统格格不入, 目前google主要还是针对浏览器做了些特殊处理, 如webkit下用gradient使得按钮看上去好些

mac下webkit的按钮不好控制

本文将就输入框高度的问题进行调研, 寻找更好的解决方法

 

输入框高度

首先, 这个调研的一个主要原因是, 搜索结果页打算进入标准模式, 这会导致盒模型的变化, 造成输入框高度和原来不一样, 所以为了和线上效果保持一致, 我们需要找到一个最佳的解决方案

有同学可能会不解, 有那么难么? 设置一个height不就解决了么?

<input type="text" style="height:28px" />

然而, 经测试发现这里面的细节问题还是还挺多, 由于资源有限, 这里只测试了主要的浏览器和平台, 包括目前主要用到的5个浏览器

  • IE6(xp)
  • IE7(xp)
  • IE8(win7)
  • Firefox 3.5(xp)
  • Firefox 3.5(win7)
  • Firefox 3.5(mac 10.6.2)
  • Firefox 3.5(ubuntu 10.4)
  • Chrome 5(xp)
  • Chrome 5(win7)
  • Chrome 5(mac 10.6.2)
  • Chrome 5(ubuntu 10.4)

通过设定height的方式

我们的目标是和目前搜索框大小保持一致, 既28px

首先测试的是最简单的height, 先看目前线上的方案(简单起见就直接写到style中了)

<input type="text" style="font: 16px arial; height: 1.78em; padding-top:2px" />

从样式上推导, 由于盒模型问题, 在IE下的大小将是1.78 * 16 = 28px, 而Firefox等浏览器应该是1.78 * 16 + 2px + border-width * 2 = 30 + ? px

测试结果是

浏览器 height + padding-top + padding-bottom + border-top-width + border-bottom-width
IE6(xp) 21 + 2 + 1 + 2 + 2 = 28
IE7(xp) 21 + 2 + 1 + 2 + 2 = 28
IE8(win7) 21 + 2 + 1 + 2 + 2 = 28
Firefox 3.5(xp) 21 + 2 + 1 + 2 + 2 = 28
Firefox 3.5(win7) 23 + 2 + 1 + 1 + 1 = 28
Firefox 3.5(mac 10.6.2) 19 + 2 + 1 + 3 + 3 = 28
Firefox 3.5(ubuntu 10.04) 19 + 2 + 1 + 3 + 3 = 28
Chrome 5(xp) 21 + 2 + 1 + 2 + 2 = 28
Chrome 5(win7) 21 + 2 + 1 + 2 + 2 = 28
Chrome 5(mac 10.6.2) 21 + 2 + 1 + 2 + 2 = 28
Chrome 5(ubuntu 10.04) 21 + 2 + 1 + 2 + 2 = 28

效果相当理想, 所有浏览器都是28px, 看来即使是Firefox和Chrom在quirks模式下的input都没有遵循盒模型, 所以线上的输入框高度在各个浏览器下很完美地保持一致

然而如果是在standards模式下, 结果则是

浏览器 height + padding-top + padding-bottom + border-top-width + border-bottom-width
IE6(xp) 28 + 2 + 1 + 2 + 2 = 35
IE7(xp) 28 + 2 + 1 + 2 + 2 = 35
IE8(win7) 28 + 2 + 1 + 2 + 2 = 35
Firefox 3.5(xp) 28 + 2 + 1 + 2 + 2 = 35
Firefox 3.5(win7) 28 + 2 + 1 + 1 + 1 = 32
Firefox 3.5(mac 10.6.2) 28 + 2 + 1 + 3 + 3 = 37
Firefox 3.5(ubuntu 10.04) 28 + 2 + 1 + 3 + 3 = 37
Chrome 5(xp) 28 + 2 + 1 + 2 + 2 = 35
Chrome 5(win7) 28 + 2 + 1 + 2 + 2 = 35
Chrome 5(mac 10.6.2) 28 + 2 + 1 + 2 + 2 = 35
Chrome 5(ubuntu 10.04) 28 + 2 + 1 + 2 + 2 = 35

就仅仅加了一句

<!DOCTYPE html>

, 却导致浏览器差距变得如此大, 仔细观察发现, 主要问题在Firefox上 它的border在win7下是1像素, xp下是2像素, mac下是3像素, 令人很头疼, 于是打算换一种方案试试

padding的方式

由于Firefox的border问题, 设定height是不可能保证高度一致的, 除非判断再去判断操作系统类型, 但那样做太麻烦了, 而且说不定mobile版又不一样

那是否可以不通过设置height来控制? 在目前的大搜索首页也是standards模式, 它是采用padding的方式来实现28px的高度的

<input type="text" style="font: 16px arial; padding:3px" />

这种写法的测试结果是

浏览器 height + padding-top + padding-bottom + border-top-width + border-bottom-width
IE6(xp) 18 + 3 + 3 + 2 + 2 = 28
IE7(xp) 18 + 3 + 3 + 2 + 2 = 28
IE8(win7) 18 + 3 + 3 + 2 + 2 = 28
Firefox 3.5(xp) 19 + 3 + 3 + 2 + 2 = 29
Firefox 3.5(win7) 19 + 3 + 3 + 1 + 1 = 27
Firefox 3.5(mac 10.6.2) 20 + 3 + 3 + 3 + 3 = 32
Firefox 3.5(ubuntu 10.04) 19 + 3 + 3 + 3 + 3 = 31
Chrome 5(xp) 19 + 3 + 3 + 2 + 2 = 29
Chrome 5(win7) 19 + 3 + 3 + 2 + 2 = 29
Chrome 5(mac 10.6.2) 18 + 3 + 3 + 2 + 2 = 28
Chrome 5(ubuntu 10.04) 19 + 3 + 3 + 2 + 2 = 29

在不设定输入框高度的情况下, 浏览器会自行指定一个, 而且都有差距, mac上的Firefox更是高出了4像素, 但总的来说, 效果虽然不完美, 但还是可以接受, 大部分情况下都只差一个像素

然而这种方法带来了很多不确定性, 内容区的高度是随着字体大小而变的, 假设font-size是14px, 浏览器的高度又保持一致了

浏览器 height
IE6(xp) 16
IE7(xp) 16
IE8(win7) 16
Firefox 3.5(xp) 16
Firefox 3.5(win7) 16
Firefox 3.5(mac 10.6.2) 16
Firefox 3.5(ubuntu 10.04) 16
Chrome 5(xp) 16
Chrome 5(win7) 16
Chrome 5(mac 10.6.2) 16
Chrome 5(ubuntu 10.04) 16

是否还有更好的方案呢?

box-sizing

height和padding都无法完美控制输入框高度, 而border的大小又不能改, 难道就真的没办法了么? 不禁怀念quirks模式下的方便, 设定一个高度就完美了, 要是能既进standards模式, 又能用到旧盒模型就好了, 很自然地就想到了一个从来没用过的css属性box-sizing, 以前一直没想好这属性到底能用在哪里, 终于这下派上用场了, 使用它我们就可以解决Firefox下3种border的区别, 让Firefox自己去算内容区的高度

然而由于IE6/7不支持这个属性, 所以需要写hack, 由于IE下的默认border值是2, padding是1, 所以height需要减6像素, 也就是

-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
height: 28px;
*height: 22px;

这样, 就能保证绝大部分的浏览器下效果一致了, box-sizing属性的支持情况如下表所示, 来自mozilla, 支持的访问非常广

Browser Lowest Version Support of
Internet Explorer 8.0 box-sizing
Firefox (Gecko) 1.0 (1.0) -moz-box-sizing
Opera 7.0 box-sizing
Safari (WebKit) 3.0 (522) -webkit-box-sizing

不过, 事情还没有结束, 刚才假定了IE下默认padding是1像素, 然而目前很多css reset都会将input的padding设为0, 于是, IE下的差别将不是6像素, 而是4像素, 所以为了避免受到影响, 建议将padding设为0

padding-top: 0;
padding-bottom: 0;
height: 28px;
*height: 24px;

浏览器在quirks下的实现方法

回过头来看Firefox和Chrome在quirks模式下使用了非标准的盒模型, 看样子是有意去做的, 它是如何实现的呢?

于是在webkit源码中寻找, 一开始以为它是在源码中对quirks下的input做了特殊处理, 但没看找到又什么特别的地方, 而在看到计算box高度的时候

int RenderBox::calcContentBoxHeight(int height) const
{
    if (style()->boxSizing() == BORDER_BOX)
        height -= (borderTop() + borderBottom() + paddingTop() + paddingBottom());
    return max(0, height);
}

忽然想到, 会不会是通过浏览器默认样式来实现的呢? 将这种特殊的逻辑直接写在代码中确实太恶心了, 既然支持box-sizing属性, 直接将它写在quirks的默认样式不就完美解决了么

果然, 在Firefox的res/quirk.css中发现了这句

/*
* Quirk: Use border-box box sizing for text inputs, password inputs, and
* textareas.  (b=184478 on why we use content-box sizing in standards mode)
*/


/* Note that all other
<input>s already use border-box
sizing, so we're ok with this selector */

input:not([type=image]), textarea {
  -moz-box-sizing: border-box;
}

在webkit源码中的WebCore/css/quirks.css发现了这句

/* This will apply only to text fields, since all other inputs already use border box sizing */
input:not([type=image]), textarea {
    -webkit-box-sizing: border-box;
}

原来浏览器就是这么解决的, 那么在标准模式下用它将是一种比较好的方案

one more thing

不过这种写法在Firefox 3.5以下的版本会有个问题, 那就是输入框内容将无法垂直居中, 以英文为例, 3.5中和顶部的差距是5像素, 而3.6是7像素, 目前还没想到解决方案

幸好在Firefox 3.6中解决了这个问题, 而且3.5会默认升级到3.6, 所以这个问题也就不需要考虑了

结论

从这个例子可以痛苦地体验, 如果没有统一的规范, 要兼容不同浏览器是如此的困难, 而且这还仅仅是一个很不完全的测试, 好在浏览器还是尽可能做到了最大兼容, 比如, 假设windows下默认主题和经典主题有区别, 就意味着所有windows下的测试都要乘2

[转载]HG:Mercurial - 分布式版本控制系统

2010-4-30 10:16:35 开发者 抢沙发(0)

 

1、下载地址:Mercurial 主页,安装时一定要同意其自启动。
        Mercurial 是一个轻量级的分布式版本控制系统,它以方便的控制、极强的扩展性赢得了众多开放源代码项目的青睐。本文从版本控制系统中的基本概念、操作和扩展性等方面,有侧重的介绍了 Mercurial。此外本文列出了几个其他常用的分布式版本控制系统,并和 Mercurial 作了简单的比较。通过阅读本文,读者可以了解基础的Mercurial操作,进而熟悉这个备受青睐的工具。
Mercurial 是一种轻量级分布式版本控制系统,采用 Python 语言实现,易于学习和使用,扩展性强。其是基于 GNU General Public License (GPL) 授权的开源项目。相对于传统的版本控制,具有如下优点:
  • 更轻松的管理。传统的版本控制系统使用集中式的 repository,一些和 repository相关的管理就只能由管理员一个人进行。由于采用了分布式的模型,Mercurial 中就没有这样的困扰,每个用户管理自己的 repository,管理员只需协调同步这些repository。
  • 更健壮的系统。分布式系统比集中式的单服务器系统更健壮,单服务器系统一旦服务器出现问题整个系统就不能运行了,分布式系统通常不会因为一两个节点而受到影响。
  • 对网络的依赖性更低。由于同步可以放在任意时刻进行,Mercurial 甚至可以离线进行管理,只需在有网络连接时同步。

版本控制系统中的 repository 就像一个仓库一样,用来存储被管理的数据文件,包含数据文件的不同版本。传统的版本控制系统中,这样的repository 是集中式的。除了这样一个集中式的 repository 之外,每个用户会有一份自己的工作版本拷贝。用户通过命令同步自己的拷贝和集中式的repository。
分布式版本控制系统中的 repository 则采用的是对等网络式的方式。传统集中式的管理中,只有一份 repository,其他的只是工作拷贝,不包含额外的版本。分布式的管理当中,每个用户所持有的都是一个真实的 repository,当中存储有不同的版本信息和维护一个 repository 的必要的辅助元数据。这样一个对等工作模式当中,用户通过交换下文即将提到的 changeset 来完成同步。
这样做的一些优点在于,工作的并行度将大大的提高。每个用户都可以带着这样的repository,从这里他可以把当前的工作拷贝切换到 repository 里面存储的任何一个版本。这个版本可以是之前正在工作的版本,现在需要合并进一些别人的意见,也可以是用户私有的一个版本,当前正在做很多前瞻性的工作,还没有能同步给其他用户使用。也同样是因为这样的模式,每个用户可以任意把自己的 repository 当中的一个版本交换给其他用户,而不需要对自己手头正在工作的版本进行回退。下图是这样一个灵活的工作模式的演示。

在使用 Mercurial 的系统中每个改动隔离在各自的 repository 里,既避免把不相关的代码混杂起来, 又便于一个接一个的测试每一部分工作,用户做的每个改动称为一个 revision。一般会有一个所有用户都可以访问得到的 repository 保存了项目的“主要”版本,工作repository 是用户自己做事情的地方,实现新的特性,修改漏洞,重构,实验等,当完成改变后,你可以 push 到共用的 repositor y中,即完成了一个 revision。
一个或多个文件的改变集合在一起形成一个逻辑单元,称为 changeset。每一个 changeset由两部分内容描述,版本号和 changeset 标识,例如:
	changeset:   207:58e4906e69e3 
冒号前面的数字代表版本号,它用来标识本地 changeset。这个版本号只有在用户的本地repository 中才有意义。冒号后面的那个很长的十六进制串是 changeset标识, 它是确定changeset的全局唯一标识符, 在所有包含这个 changese 的 repository 中都相同。多个用户之间讨论changeset,一般使用这个 changeset 标识,而不是上面说的版本号,因为完全有可能每个用户的 repository 中同样的 changeset 版本号不同。
Head 表示 repository 中每个分支最新的 revision,通常在合并几个分支时会用到这个概念。
Tip 是最新的一个 changeset 的版本号的一个别名。在命令中任何使用版本号的地方都可以使用 tip 来代替最新的 changeset的版本号。Tip在各个repository中是不同的,同时一个repository 中只有一个 tip。
Log 命令按时间顺序从近到远的记录着在 repository 中发生的每一次事件。可以通过指定-v诊断输出选项来获得更多更详细的历史信息,或者指定—debug选项来获得历史信息中的一切细节。

以下是一些实际使用 Mercurial 中常用的例子。Mercurial 的原意是元素周期表当中的汞元素,但是 Mercurial 这样的单词显然不太适合日常使用。事实上 Mercurial 的命令取了元素周期表当中汞元素的化学符号:hg,所有的 Mercurial 命令都以 hg 开始。
    $ hg command [options]
其中 command 是 Mercurial 的命令。每个命令的具体的命令行选项可以使用:
    $ hg help command
来获得。
我们使用 clone 命令克隆一个 repository,它生成一个完整的 repository 复本,这样我们就有一个本地私有的 repository 来工作。
例如:
     $ hg clone  http://foo.com/hg/project1
如果所有都没问题,clone 命令输出:
    destination directory: project1
    requesting all changes
	adding changesets
	adding manifests
	adding file changes
	added 127 changesets with 448 changes to 143 files
	139 files updated, 0 files merged, 0 files removed, 0 files 	unresolved
此时,我们应该在当前目录下发现一个目录叫 project1,目录下的文件是我们刚克隆的 repository 的精确复本。在 Mercurial 中,每一个 repository 是自包含的。当你克隆一个repository 后,新 repository 变成克隆时它的精确复本,但是后续的两个 repository 当中任一方改变都不会在对方显示,除非用户使用 pull 或 push 命令明确地传递改变,这个将在后面介绍。
另外,每个用户可以使用 init 命令将本地的一个目录初始化为一个 Mercurial 的 repository,只需要在那个目录下运行:
   $ hg init
如果设置好了同步共享的发布方式,其他用户就可以来克隆该用户的 repository 了。
进入工作目录,使用我们喜欢的编辑软件修改,例如我们要修改 main.py 让它增加打印一行输出:
def main():
    print "I'm in the a function."
    print "Great joy of using Mercurial!"  #新加的一行
if __name__ == "__main__":
    sys.exit(main())
完成之后退出编辑器,任务完成。有了刚才的修改我们就可以创建一个changeset。
在创建 changeset 之前如果想确认一下哪些文件被改动了,可以使用 status 命令。
$ hg status
M main.py
使用 diff 命令可以检查实际文件内容的改变:
diff -r a58db6f0e482 main.py
--- a/main.py   Thu Nov 29 13:38:38 2007 +0800
+++ b/main.py   Thu Nov 29 13:46:10 2007 +0800
@@ -1,5 +1,6 @@ def main():
 def main():
     print "I'm in the a function."
+    print "Great joy of using Mercurial!" #新加的一行
 if __name__ == "__main__":
     sys.exit(main())
diff 命令的默认输出是通用的补丁格式,易于在各种系统之间交换和讨论。
创建一个 changeset 后我们就可以用 commit 命令提交了。
$ hg commit
这个命令把我们带到一个编辑器内,缺省的编辑器是 vi,同时给我们展示了几行如下的文字:
HG: user: Guolian Yun <yunguol@cn.ibm.com>
HG: changed main.py
第一行是空的,接下来的几行表明哪些文件将进入本 changeset。为了改变 changeset,我们必须描述它的原因,这通常称为 changeset注释。让我们输入一些:
I’m using Mercurial!
HG: user: Guolian Yun <yunguol@cn.ibm.com>
HG: changed main.py
接着,保存并退出编辑器,如果一切正常,commit 命令将没有任何提示地输出。
让我们看看status命令现在告诉我们什么?
$ hg status
什么也没有!我们的改动已经提交到changeset里了,那里没有修改的文件需要提交的。Repository 中内容和当前工作目录的内容一致了。
现在可以检查以下最新的改动是不是包含刚才所添加的 changeset 注释,使用 tip 命令就可以显示了:
$ hg tip:
changeset:   2:2874393e3d9c
tag:         tip
user:        Guolian Yun <yunguol@cn.ibm.com>
date:        Thu Nov 29 10:10:39 2007 +0800
summary:     I'm using Mercurial!
目前新的 changeset 只存在本地 repository 中,如果想和其它 repository 分享改动,我们需要使用 push。
$ hg push project2
project2 为你想要 push 的目标 repository 的名字。
想要得到所有在别的 repository中而在本地repository中没有的改动,可以采用 pull命令。
$ hg pull project3
project3为我们想要得到更新的目标repository的名字。
在 Pull 后,缺省情况下Mercurial不更新工作目录。这意味着虽然repository现在有changeset,但在工作目录中的 main.py 文件仍然是 pull 之前老的内容。
如果只想从hg clone的 repository 中更新当前 revision 到最新版,可以直接采用:
$ hg pull -u
上文当中的 push 和 pull 的操作,是处于不同位置的 repository 之间的同步。之前给出的两个例子是本地目录 repository 之间的同步。Mercurial 还支持以下形式的 repository 之间的同步:
file://local/filesystem/path
http://[user@]host[:port]/[path]
https://[user@]host[:port]/[path]
ssh://[user@]host[:port]/[path]
其中 file 协议和本地目录相同。在 http 和 https 协议上使用push命令,需要在远端的服务器上启用相关的属性。ssh协议是众多系统中支持的shell。
标准的 Mercurial 发行包中还附带一个 Python CGI 脚本 hgweb.cgi, 可以用来参考搭建一个多用户可以集中式的同步改动的界面,如下图所示:

 

 

Mercurial 系统中提供一种扩展机制来添加新的命令。通过扩展添加的命令可以在现有的Mercurial 系统的基础上添加新的功能,这些命令跟随 hg 后被调用时就像原生的命令一样。本文介绍两个常用的Mercurial扩展:Patchbomb和Mq。
Patchbomb是一个在Mercurial系统中利用发送邮件的方式来交换changeset的扩展。Patchbomb添加了一个新的email命令。通过调用 hg email 命令,changeset 提交时的信息的第一行将作为邮件的主题,信件的正文包括完整的 changese t提交信息,以 patch 的形式发布出来的 changeset 完整补丁。如果一次发送的是多个changeset,那么Patchbomb会提示输入本次 changeset 集的总提示信息,这部分信息将作为第一封信,信件主题以[PATCH 0 of N]开头,changeset 则会以[PATCH i of N]将的主题开头发出,其中i是 changeset在本地 repository 当中的顺序。多changeset 系列邮件中,每封信会在邮件头中包含合适的回复信息,这样在邮件客户端可以清晰的显示出系列 changeset 之间的层次关系。
Patchbomb 支持使用本地系统中的 sendmail 程序来发送邮件,同时支持使用 SMTP 邮件服务器。用户如果长期固定为某个项目工作,还可以将邮件的收件地址和发信地址提供给Patchbomb,免去每次手动输入的麻烦。这一切都可以在 Mercurial 统一的 .hgrc 当中设置,以下是一个完整的例子。
[extensions]
hgext.patchbomb =
[email]
method = smtp # 还可以在这里指定/usr/sbin/sendmail
from = Zhengang Li <lizg@cn.ibm.com>
to = groupmail@foo.com
[smtp]
host = smtp.foo.com
Patchbomb 作者为Bryan O’Sullivan,该扩展现在随同 Mercurial 系统一起发布,用户不需额外下载安装,只需如上例中一样启用即可。
Mq 扩展的全名是Mercurial Queues,顾名思义该扩展将用户本地的多个 changeset 排列到队列中。原先分布式版本控制系统中,changeset 一旦提交并不能修改。有了 Mq 扩展之后,用户可以将本地的任意数量的 changeset 存放至一个本地的队列当中,对这些 changeset 用户除了可以使用传统的 changeset 上的任何命令之外,还可以修改changeset,包括提交信息和版本补丁的改动。
启用 Mq 扩展的办法同其他扩展一样,在 .hgrc 当中添加如下信息:
[extensions]
hgext.mq=
Mq的命令是一系列以字母q打头的命令,qinitqnewqrefreshqdiffqpop和qpush等。Qinit 用来初始化用来存放补丁队列的目录,qnew创建一个新的补丁changeset,qrefresh 将改动刷新到当前的补丁当中去,qdiff 将当前的补丁打印到屏幕,qpop 和 qpush 用来移动当前存放在队列顶部的补丁。完整的 q 系列命令可以从 hg help给出的列表中获得。
Mq中所有的 changeset 补丁存放在项目顶层目录的.hg/patches下面,用户可以手动修改这些补丁当中的提交信息。Changeset 补丁的顺序存放在.hg/patches/series文件当中,同样的,用户可以修改这些补丁的顺序。
Mq的作者是Chris Mason,该扩展现在随同 Mercurial 系统一起发布,用户不需额外下载安装。
如果现有的扩展不能满足用户的要求,编写自己的扩展也不困难。Mercurial 使用 Python编写,编写一个新的扩展相当于在 Mercurial 系统的 hgext 包当中编写一个新的模块。具体的扩展实现还有些约定的规则,用户可以参考 Mercurial 所提供的文档。

分布式版本控制系统领域还有一些其他的系统,如GNU arch,monotone,Bazaar,git,darcs。
各类系统在各在一定的领域内长处,如GNU arch在GNU Savannah主机上应用,Bazaar 主要用于 Ubuntu Linux 系统的开发当中,git 源于Linux kernel 的开发,现在在多处和内核相关的项目中使用。他们大多数提供友好的Web界面和多种版本同步协议的支持。Git 和Gnu arch 由 C 和 shell 脚本语言编写实现,monotone 由C++语言实现,darcs 由 Haskell语言实现,Bazaar 和本文介绍的 Mercurial 由 Python 语言实现。从开放和扩展性方面来说,类似 Python 这样的脚本语言的更易于用户编写自己的扩展。
在众多的分布式版本控制系统中,Mercurial 是最年轻的,它的第一个版本发布于2005年4月。Mercurial 吸收了众多前辈的特性,被众多的项目采用。

Mercurial 基本的概念和操作熟悉之后,读者可以进一步阅读参考资料当中的文献,进一步了解Mercurial 的系统维护和实现细节。
 
 
KINGFO:其他指南 http://hgbook.red-bean.com/read/

 

每日一Tip_CSS_input

2010-4-29 9:39:49 开发者 抢沙发(0)

 

 对于input框,在定义高度后在ie下会出现各种很怪异的问题,所以我们一般不定义input框的高度,通过padding值来控制

--------来自团队的总结

[转]200行实现JavaScript OOP 框架

2010-4-2 9:31:55 开发者 抢沙发(0)
来自:http://corodidea.net/blog/index.php/archives/129
作者
infinte
关键词
JavaScript, OOP, 框架, 运行时覆写, 保护成员
摘要

本文主要实现了一个轻量的JavaScript OOP框架,并借此讨论了基于运行时覆写实现基类方法调用的技术以及实现保护成员的方法。
A lightweight JavaScript OOP framework was implemented in this text. Base-method invocation implementation technique, using runtime overwriting, and implementation of protected members was discussed here.

我终于这个优雅且几乎完美的JavaScript OOP 实现——轻便,简洁,并且有效。(暂且命名为emop)
Finally I've found the elegant implementation of almost-perfect OOP framework implementation in Javascript. It's light, clear, and, effective.(temporarily named emop)

  1. var Class; 
  2. void function() { 
  3. var mgBaseGen = function(bcl, original) { 
  4. return function(nname) { 
  5. var c = bcl, rv; 
  6. while (c && !c.prototype.hasOwnProperty(nname)) c = c.baseConstructor; 
  7. if (!c) throw new Error('Cannot find base class with specific methods'); 
  8. this.base = mgBaseGen(c.baseConstructor, this.base); 
  9. try { 
  10. rv = c.prototype[nname].apply(this, Array.prototype.slice.call(arguments, 1)); 
  11. finally { 
  12. this.base = original; 
  13. }; 
  14. return rv; 
  15. }; 
  16. }; 
  17. // Token 
  18. var CHECK = {}; 
  19. // Empty function 
  20. var NULLFUNC = function() {}; 
  21. // hasOwnProperty checker 
  22. var HOPCHECKER = {}; 
  23. // protected properties name 
  24. var protectedName = '..protected..'
  25.  
  26. var registerProtecteds = function(o) { 
  27. var protecteds = []; 
  28. var p = function(checker, name, value) { 
  29. if (checker !== CHECK) throw new Error('access denied'); 
  30. if (arguments.length > 2) return protecteds[name] = value; 
  31. else if (HOPCHECKER.hasOwnProperty.call(protecteds, name)) return protecteds[name]; 
  32. }; 
  33. p.length = 0; 
  34. p.toString = function() { 
  35. throw new Error('access denied'
  36. }; 
  37.  
  38. o[protectedName] = p; 
  39. }; 
  40.  
  41.  
  42.  
  43. var bound = null
  44. var priv = function(n, v) { 
  45. if (!HOPCHECKER.hasOwnProperty.call(bound, protectedName)) { 
  46. if (protectedName in bound) { 
  47. registerProtecteds(bound); 
  48. else { 
  49. return new Error('access denied'); 
  50. if (arguments.length > 1) return bound[protectedName](CHECK, n, v); 
  51. else return bound[protectedName](CHECK, n); 
  52. }; 
  53.  
  54. var wrapProtected = function(method) { 
  55. return function() { 
  56. var hold = bound; 
  57. bound = this
  58. try { 
  59. var ret = method.apply(this, arguments); 
  60. finally { 
  61. bound = hold; 
  62. return ret; 
  63.  
  64. // catalogs 
  65. var NClassBase = null
  66. var NAttachments = []; 
  67.  
  68. Class = function(Definition, proto) { 
  69.  
  70. // stack backup 
  71. var BClassBase = NClassBase; 
  72. var BAttachments = NAttachments; 
  73.  
  74. // clear attachments array 
  75. NAttachments = []; 
  76.  
  77. try { 
  78. // constructor 
  79. var ctor, def = new Definition(priv); 
  80. if (def.initalize instanceof Function) { 
  81. ctor = wrapProtected(def.initalize); 
  82. else { 
  83. ctor = NULLFUNC; 
  84. }; 
  85. delete def.initalize; 
  86.  
  87. // create class 
  88. var Clz = function() { 
  89. registerProtecteds(this); 
  90. return ctor.apply(this, arguments); 
  91. }; 
  92.  
  93. //bind prototype 
  94. if (proto instanceof Function || NClassBase instanceof Function) { 
  95. if (proto instanceof Function) { 
  96. Clz.prototype = new proto; 
  97. Clz.baseConstructor = proto; 
  98. else if (NClassBase instanceof Function) { 
  99. Clz.prototype = new NClassBase; 
  100. Clz.baseConstructor = NClassBase; 
  101. }; 
  102. Clz.prototype.base = function(name) { 
  103. var c = Clz.baseConstructor, rv, cler = arguments.callee.caller.caller; 
  104. var original = this.base; 
  105. this.base = mgBaseGen(c, original); 
  106. try { 
  107. rv = this.base.apply(this, arguments); 
  108. finally { 
  109. this.base = original; 
  110. }; 
  111. return rv; 
  112. }; 
  113. else { 
  114. if (proto) Clz.prototype = proto; 
  115. else Clz.prototype = {}; 
  116. Clz.baseConstructor = Object; // need to be fixed; 
  117. }; 
  118.  
  119. proto = Clz.prototype; 
  120.  
  121. //attachments 
  122. for (var i = 0; i < NAttachments.length; i++) { 
  123. var item = NAttachments[i][0]; 
  124. var args = [proto].concat(NAttachments[i][1]); 
  125. item.apply(proto, args); 
  126.  
  127. //reflect methods 
  128. for (var each in def) if (def.hasOwnProperty(each)) { 
  129. if (def[each] instanceof Function) { 
  130. var f = wrapProtected(def[each]); 
  131. f.toString = function() { 
  132. return def[each].toString() 
  133. }; 
  134. f.length = def[each].length; 
  135. proto[each] = f; 
  136. else { 
  137. proto[each] = def[each]; 
  138. }; 
  139.  
  140. finally { 
  141. NClassBase = BClassBase; 
  142. NAttachments = BAttachments; 
  143. return Clz; 
  144. }; 
  145. }; 
  146.  
  147. Class.base = function(f) { 
  148. NClassBase = f; 
  149. }; 
  150. Class.attach = function(attachment, _args_) { 
  151. NAttachments.push([attachment, Array.prototype.slice.call(arguments, 1)]); 
  152. }; 
  153.  
  154. Function.prototype.derive = function(definition) { 
  155. return Class(definition, this); 
  156. }; 
  157. Function.prototype.asClass = function(base) { 
  158. return Class(this, base); 
  159.  
  160. }(); 
  161.  
  162. //Usage 
  163. var Greeter = function() { 
  164. this.greet = function() { 
  165. alert('hello!'); 
  166.  
  167. var A = function(priv) { 
  168. Class.attach(Greeter); 
  169. this.f = function(t) { 
  170. priv('y', t); 
  171. alert(priv('y')); 
  172. }; 
  173. }.asClass(); 
  174.  
  175. var B = A.derive( function(priv) { 
  176. var Point = function(p) { 
  177. this.initalize = function(x, y) { 
  178. p('x', x); 
  179. p('y', y); 
  180. }; 
  181. this.abs = function() { 
  182. var x = p('x'); 
  183. var y = p('y'); 
  184. return Math.sqrt(x * x + y * y); 
  185. }; 
  186. }.asClass(); 
  187. this.g = function(p, q) { 
  188. alert((new Point(p, q)).abs()); 
  189. }; 
  190. this.f = function(t) { 
  191. this.base('f', t); 
  192. alert('-- ' + t); 
  193. this.inter = function(obj, t) { 
  194. obj.f(t) 
  195. }; 
  196. }); 
  197.  
  198. var foo = new B(); 
  199. foo.greet(); 
  200. foo.f(2); 
  201. var foo2 = new B(); 
  202. foo2.g(5, 5); 
  203. foo2.inter(foo, 3); 

 

这是什么?
What is it?

长期以来JavaScript程序员感到JavaScript的诸多掣肘之一就是它没有“完整的”OOP支持。尽管JavaScript利用原型系 统提供了OOP功能,但它没有提供这些特性:保护成员和基类方法调用。这段代码就是为了解决此问题。
For a long time, one of its limits which JavaScript programmers found is that it cannot provide "complete" OOP support. Although OOP was provided by the prototype system, the following charactics are absent: protected members and base method invocation. The target of this code is to solve this problem.

基本思想
Basic concepts

  • 使用闭包独立环境
    Isolate environments by using closures
  • 运行时覆写
    Runtime overwriting
  • 使用try-finally的异常处理
    Exception handling via try-catch

定义类
Defining class

定义类的是一个函数,和传统的JavaScript构造器类似:
What defines a class is a function, written in thetraditional way:

  1. function(priv) { 
  2. this.g = function(t) { 
  3. priv('x', t); 
  4. alert(priv('x')); 
  5. }; 
  6. this.f = function(t) { 
  7. this.base('f', t); 
  8. alert('-- ' + t); 

你可以看见类似传统“构造器”的东西——用this.methodName定义方法。不过,这里有一个很奇怪的priv参 数——它就是保护乘员的读写器。类定义将会传入Class函数,经过一系列复杂的转化后,返回我们需要的构造器。获得这个类只 要通过下面一句:
You can see something like traditional JavaScript constructors--defineing methods via "this.methodName". But, a strange method named "priv" appears here.It's the accessor of protected members. Such class definition will be sent into Class, and after a series of complicated transformation, the constructor we need yields.Getting this class can even use only one statement:

  1. A = Class(definition, base) 

第一个参数是定义,第二个是其基类。
The first argument is the definition, the second is its base class.

emop还有两个语法糖:一个是Function.prototype.asClass,另一个是Function.prototype.derive。 它们分别定义为:
There are also 2 syntax sugars in emop: one is Function.prototype.asClass, the other is Function.prototype.derive. Their definitions are:

  1. Function.prototype.derive = function(definition) { 
  2. return Class(definition, this); 
  3. }; 
  4. Function.prototype.asClass = function(base) { 
  5. return Class(this, base); 

细节
In depth

你已经看到了,一个类定义很像一个构造器,但事实上不是。类定义是个函数,而且真的会运行,但只会运行一次。事实上构造器是由其定义的initalize方 法描述:
You have seen that a class definition looks like a constructor, but it in't. Class definition is a function, and it will be invoked, but only once. Actually, the exact constructor is described by initalize method which the definition defines:

  1. var Point = Class(function(p) { 
  2. this.initalize = function(x, y) { 
  3. p('x', x); 
  4. p('y', y); 
  5. }; 
  6. this.abs = function() { 
  7. var x = p('x'); 
  8. var y = p('y'); 
  9. return Math.sqrt(x * x + y * y); 
  10. }; 
  11. }); 

上面的Point是一个极好的例子。当我们要构造一个点的时候,虽然代码像下面的这样:
The Point above is a good example. When we need to construction a point, the following code appears:

  1. var p1 = new Point(0, 0); 

特性
Charactics

你可能已经发现第一段代码中有几个很有趣的方法:
Maybe you've found some interesting methods in the first code paragraph:

  • Class.attach
  • Class.base
  • “priv”
  • (instance).base

上面几个“特殊”方法的用途分别是:
Usages of the "special" methods are:

  • 附加相关的“附件”(混入类)
    Attach specific "Attachments"(mixins)
  • 指定基类
    Define base class
  • 存取“保护成员”
    Access "protected members"
  • 调用“基类”方法
    Invoke base methods

priv

因为类定义是一个函数,它的第一个参数就是存取保护成员的读写器。在emop中一个对象拥有一般成员(和JavaScript中的一样)和保护成员 (只能用这个读写器访问)。因为方法定义总是包含在类定义中,所以priv可以被访问到,对象方法可以使用priv读 写保护成员:
Because class definition is a function, it's first paramater is the accessor of protected memners. Objects in emop contains normal members (like that in native JavaScript object) and protected members(only accessable by this accessor). Because the method definition is always contained by class definition, priv can be accessed and methods can use it to access protected members:

  1. this.m = function() { 
  2. priv('x'); // get 
  3. priv('x', 1); // set 

在这个例子中,m方法可以自由读写'x'保护成员。但priv在 类定义外就不能访问了,所以外界不能经过priv读写保护成员。
In this example, method m can access protected member 'x' freely. But priv is not accessable outside the class definiton, so outside program cannot access protected members via priv.

priv有两种调用方法:
There are two ways to invoke priv:

  1. priv('x'); // get 
  2. priv('x', 1); // set

 

传入一个参数意味着读取,传入两个参数就是写入。
Passing one argument means reading, two means writing.

因为是保护成员,所以基类定义的方法和派生类定义的方法都可以使用priv访问。
Because it is protected members, base methods and derived methods can both access it via priv.

  1. var A = Class( function(p) { 
  2. this.f = function() { 
  3. p('x', 1) 
  4. }); 
  5. var B = Class( function(p) { 
  6. this.g = function() { 
  7. return p('x'
  8. },A); 
  9.  
  10. var obj = new B(); 
  11. obj.f(); 
  12. obj.g(); // --> 1  

Class.base and Class.attach

emop规定一个类只有一个基类,Class.base就描述基类。而Class.attch则 描述了混入类。混入类定义类似类定义,但它是直接apply到某类的prototype上。显然, 混入类不能拿到保护成员读写器priv。上面的那段代码可以改写成:
emop declared that a class have only one base class, and it is defined by Class.base. Class.attach describes mixins. Mixin definition looks like class definition, but it is directly apply-ed to the prototype of some class. Obviously, mixins cannot get protected member acceessor "priv". Code above can be written like:

  1. var A = Class( function(p) { 
  2. this.f = function() { 
  3. p('x', 1) 
  4. }); 
  5. var B = Class( function(p) { 
  6. Class.base(A); 
  7. this.g = function() { 
  8. return p('x'
  9. }); 
  10.  
  11. var obj = new B(); 
  12. obj.f(); 
  13. obj.g(); // 1  

因此,定义派生类有两种方法:
So, derived class can be defined in two ways:

  1. Derived = Class( function() {...... 
  2. },Base) 
  3. Derived = Class( function() { 
  4. Class.base(Base);...... 
  5. }) 

下面的情况下,Base1Derived的基类。另一个被抛弃:
Under the following situation, Base1 is the base class of Derived, Not Base2

  1. Derived = Class( function() { 
  2. Class.base(Base2);...... 
  3. },Base1) 

混入类可以参数化——当调用Class.attach(mixin,a,b,c)时,传入mixin的 第一个参数是被定义类的prototype,接着是a,b,c。传入的this指 针也是那个prototype
Mixins can be parameterized -- When invoking Class.attach(mixin,a,b,c), First argument passed into mixin is the prototype of defined class, following arguments are a,b and c. this pointer passed is also that prototype.

  1. Mix = function(proto, a) { 
  2. this.f = function() { 
  3. return a 
  4. }; 
  5.  
  6. var Thing = Class( 
  7. function() { 
  8. Class.attach(Mix, 1); 
  9. }); 
  10.  
  11. var t = new Thing; 
  12. t.f() // --> 1 

(instance).base

每个用Class创建的类构造的对象都有一个base用于调用基类方法。类似这样:
Each object constructed by a Class-defined class contains the base as a base-method invoker. Like this:

  1. var A = Class( function(p) { 
  2. this.f = function(t) { 
  3. return t 
  4. }); 
  5. var B = Class( function(p) { 
  6. Class.base(A); 
  7. this.f = function(t) { 
  8. return 'x' + this.base('f', t) 
  9. }; 
  10. }); 
  11.  
  12. var obj = new B(); 
  13. obj.f(1); // --> x1 

传入给base的第一个参数是要调用的基方法名,接着是参数。base会搜索其基类,如果它定义了 指定的方法,就执行之,否则继续向上,直到确认在所有基类中都找不到为止。
First argument passed into base is the method name we want to invoke, then "actual" arguments. base will search its base class, invoke the specified method if the base class defined it, or continue going up until it confirmed the method is absent in all base class definitions.

  1. var A = Class( function(p) { 
  2. this.f = function(t) { 
  3. return t 
  4. }); 
  5. var B = Class( function(p) { 
  6. Class.base(A); 
  7. }); 
  8.  
  9. var C = Class( function(p) { 
  10. Class.base(B); 
  11. this.f = function(t) { 
  12. return 'x' + this.base('f', t) 
  13. this.g = function() { 
  14. return this.base('g'
  15. }); 
  16.  
  17. var obj = new C(); 
  18. obj.f(1); // --> x1 
  19. obj.g(); // --> Error: Method "g" absent 

实现技术
Implementation Techniques

运行时覆写
Runtime Overwriting

回溯法的例子
Examples in backtracking algorithms

想象一下我们在书写一个迷宫程序。迷宫由若干cell组成,我们的目的是看有没有去出口的路径。这种问题很适合用回溯 法,算法的框架大体如下:
Imagine that we're writing a maze program. A maze consists of some cell, and we're going to find out whether the way to the exit exists. This problem is suit for backtracking. The algorithm looks like this:

  1. var found = false, steps = 0; 
  2.  
  3. function searchCell(position) { 
  4. position.visited = true
  5. if (position == maze.exit) { 
  6. found = true
  7. return 
  8. }; 
  9. steps += 1; 
  10. var neighbors = position.getNeighbors(); 
  11. for (var each in neighbors) if (!neighbors[each].visited) { 
  12. searchCell(neighbors[each]); 
  13. if (found) return
  14. steps -= 1; 
  15.  
  16. searchCell(maze.start) 

steps很好地说明了运行时覆写的原理——当搜索一个未访问的cell时,把它加1,表示 “走一步”;而回溯的时候,则把它减1,表示“退回”。这样,如果你搜索地图无果,那么steps还是0。
steps explained the mechanism of runtime overwriting well -- when searching one unvisited cell, increase it, means "one step"; when backtracking, decrease it, means "go back". Therefore, if the way does not exist, steps equals to 0.

运行时覆写的基本思路就是——当进入方法,修改某个东西;当退出方法,则把它改回来。
Basic principles of Runtime overwritting is Change something when enter the method, and change it back when exit.

priv

你肯定很好奇调用priv的时候是怎么知道要取那个对象的保护成员的。要知道我们压根没有给privthis指 针。实际上,priv取的是一个叫bound的对象的保护成员——bound则 是在调用对象方法的时候覆写成传入那个方法的this指针的。
You will be curious about how priv knows which object's protected members will be accessed. We never pass this pointer to priv. Actually, priv accesses the protected members of an object named "bound". "bound" is overrited to this pointer when invoking a method.

emop中,每个类定义的方法都被包裹,秘密在wrapProtected中。它的代码是:

  1. var bound; 
  2.  
  3. // ........ 
  4. var wrapProtected = function(method) { 
  5. return function() { 
  6. var hold = bound; 
  7. bound = this
  8. try { 
  9. var ret = method.apply(this, arguments); 
  10. finally { 
  11. bound = hold; 
  12. return ret; 
  13. // ........ 
  14. Clz.prototype[each] = wrapProtected(def[each]); 

因为不能给priv直接传this(否则要他还有何意义),wrapProtected就 做了一个有趣的事情——在对象调用方法之前,把bound设置成传入的this指针,同时做备份; 而当退出时,则把bound再改回来。这么一来二去,method调用时priv可 以正确找到保护成员,而结束后则像什么都没发生一样。
Because we can't pass this to priv explicitly, wrapProtected did something interesting. Before the "real" method's invocation, change bound to passed this pointer, also backup it; After the method invoked, change bound back. With this process, when the "real" method is running, priv works correctly, and, when it ended, it seems nothing happened to priv and bound.

try ... finally保证了即使method发生异常,bound也 能改回来。当然,这会造成一点性能损失。
try ... finally ensured that even method throws an exception, bound will be changed back. Obviously, it will slow down the progeam a little.

Class.baseClass.attach的原理相似,这里就不深究了。各位可以看代 码研究。
Class.base and Class.attach works in the same mechanism. You can read the code above.

base

base中使用的技术最为复杂——因为这次覆写的就是它自己。this.base在它自己调 用时,基类方法调用前覆写,基类方法调用后改回。
Techniques used in base is the most complicated, becaust it will overwrite itself. this.base overwrites itself after it is invoked, and before base method's invocation; and change it back after base method exits.

base搜索基方法依赖在类上维护的baseConstructor属性,它直接指向基类。用于覆写的base由一个叫mgBaseGen的函数生 成,代码是:
base's searching depends on the baseConstructor property of classes, which points to it's class. bases used to overwrite is generated by function mgBaseGen:

  1. var mgBaseGen = function(bcl, original) { 
  2. return function(nname) { 
  3. var c = bcl, rv; 
  4. while (c && !c.prototype.hasOwnProperty(nname)) c = c.baseConstructor; 
  5. if (!c) throw new Error('Cannot find base class with specific method'); 
  6. this.base = mgBaseGen(c.baseConstructor, this.base); 
  7. try { 
  8. rv = c.prototype[nname].apply(this, Array.prototype.slice.call(arguments, 1)); 
  9. finally { 
  10. this.base = original; 
  11. }; 
  12. return rv; 
  13. }; 
  14. }; 

mgBaseGen接受两个参数,一个是搜索方法的起点bcl,另一个是备份original。 在3-5行,沿着baseConstructor链搜索某个合适的类c,包含nname指 定的方法;接着,把this.base覆写成以c的基类为起点的新base。 然后,调用基类方法。调用完毕,则把this.base改回original,清除痕迹;最后,返 回值。
mgBaseGen accepts two parameters, ont is the searching start, bcl, the other is the backup, original. In line 3-5, generated base method searches the class c defines specified method method; then overwrite the this.base method by a new base with c.baseConstructor as searching start. Then, invoke the found base method and change this.base back.

这样,this.base就以一种神奇的方式工作——如果基类方法还要调用更“基类”的方法的话,this.base就 会站在更深的地方搜索方法。而当基类方法调用完成时,this.base就像没改过一样。
In this way, this.base works magically --  if the base method needs to invoke a "baser" method, this.base will start searching the method at a "deeper" place. But when the base method is exited, this.base looks unchanged.

this.base加入到原型中使用下面的代码——这几乎不用解释:
Adding this.base into prototype uses following code -- explaining is needless:

  1. Clz.prototype.base = function(name) { 
  2. var c = Clz.baseConstructor, rv, cler = arguments.callee.caller.caller; 
  3. var original = this.base; 
  4. this.base = mgBaseGen(c, original); 
  5. try { 
  6. rv = this.base.apply(this, arguments); 
  7. finally { 
  8. this.base = original; 
  9. }; 
  10. return rv; 
  11. }; 

类定义的实现
Implementation of class definition

在类定义中我们用“this.method = function(){...}”定义方法,但是,这种定义法是如何变 成一个构造器的呢?事实上在Class中,我们把定义给new了一次:
In class definitions, we use "this.method = function(){...}" to define methods. But how did the class definition come into a constructor. In fact, we constructed the definition once in Class:

  1. Class = function(definition, proto) { 
  2. // ... 
  3. var ctor, def = new Definition(priv); 
  4. if (def.initalize instanceof Function) { 
  5. ctor = wrapProtected(def.initalize); 
  6. else { 
  7. ctor = NULLFUNC; 
  8. }; 
  9. delete def.initalize; 
  10.  
  11. // create class 
  12. var Clz = function() { 
  13. registerProtecteds(this); 
  14. return ctor.apply(this, arguments); 
  15. }; 
  16. // ... 

def对象是“new”定义的产物,接着,如果它包含initalize方 法,则把包裹它作为“初始化器”,否则,就用空函数。生成的构造器Clz实际上包含了两步,一个是注册保护成员(见下一节), 另一个则是执行初始器。
The def object is the result of constructing definition, and then, if def contains initalize method, wrap it by wrapProtected and set the initalizer (ctor) to it; if not, set it to the empty function. Generated constructor Clz contains 2 steps: registering protected menber (see the selection below), and executing ctor.

原型的绑定在下一步:
Prototype is bound in the next:

  1. //bind prototype 
  2. if (proto instanceof Function || NClassBase instanceof Function) { 
  3. if (proto instanceof Function) { 
  4. Clz.prototype = new proto; 
  5. Clz.baseConstructor = proto; 
  6. else if (NClassBase instanceof Function) { 
  7. Clz.prototype = new NClassBase; 
  8. Clz.baseConstructor = NClassBase; 
  9. }; 
  10. Clz.prototype.base = function(name) { 
  11. var c = Clz.baseConstructor, rv, cler = arguments.callee.caller.caller; 
  12. var original = this.base; 
  13. this.base = mgBaseGen(c, original); 
  14. try { 
  15. rv = this.base.apply(this, arguments); 
  16. finally { 
  17. this.base = original; 
  18. }; 
  19. return rv; 
  20. }; 
  21. else { 
  22. if (proto) Clz.prototype = proto; 
  23. else Clz.prototype = {}; 
  24. Clz.baseConstructor = Object; 
  25. }; 
  26.  
  27. proto = Clz.prototype; 

这里检查了两处,一个是显式传入的proto,另一个是用Class.base注册的基类NClassBase。 如果proto是函数,则构造一个对象做原型,NClassBase同理。如果proto是 一个对象,则把Clz的原型直接赋予它。
We checked explicitly passed argument proto and NClassBase registered by Class.base. If proto is a function, the prototype of Clz will be the object constructed by proto. NClassBase is processed in like manner. If proto is an object, but not a function, then directly set Clz.prototype to it.

保护成员的实现
Implementation of protected members

现在我们要深入priv,了解保护成员是如何实现的。
Now we will see the detail of priv, to know how protected members implemented.

  1. var priv = function(n, v) { 
  2. if (!bound.hasOwnProperty('__protected')) return new Error('access denied'); 
  3. if (arguments.length > 1) return bound.__protected(CHECK, n, v); 
  4. else return bound.__protected(CHECK, n); 
  5. }; 

priv的实现异常简单——它调用……对象的__protected方法。等等,这个方法会不会把 保护成员泄密?实际上不会,因为有CHECKCHECK是一个围在闭包里的“令牌”,外界几乎无 法获取。__protected方法是在这里注册的:
The implementation of priv looks very simple. It invokes ...... the __protected method of the object. But will this method betray its protected methods? Actually no, because there is a check. The constant CHECK is an token object contained in the outer closure and it's unaccessable outside. __protected was registered here:

  1. // Token 
  2. var CHECK = {}; 
  3. // Empty function 
  4. var NULLFUNC = function() {}; 
  5. // hasOwnProperty checker 
  6. var HOPCHECKER = {}; 
  7. var registerProtecteds = function(o) { 
  8. var protecteds = []; 
  9. var p = function(checker, name, value) { 
  10. if (checker !== CHECK) throw new Error('access denied'); 
  11. if (arguments.length > 2) return protecteds[name] = value; 
  12. else if (HOPCHECKER.hasOwnProperty.call(protecteds, name)) return protecteds[name]; 
  13. }; 
  14. p.length = 0; 
  15. p.toString = function() { 
  16. throw new Error('access denied'
  17. }; 
  18.  
  19. o.__protected = p; 
  20. }; 

函数p的第一行干的就是检查CHECK,如果“令牌”不对,对不起,access denied。接下来的内容就很简单了,基本的hash操作。这里使用了一个小技巧,用HOPCHECKERhasOwnProperty检 查protecteds的属性有无,防止覆写hasOwnProperty造成检测的错误。
What the first line of function p did is checking CHECK. if the token is wrong, then throw an access denied exception. Following contents are easy, basic hashtable operations.There is a small technique:  checking property existance of protecteds by HOPCHECKER's hasOwnProperty, in order to avoid mistakes made by overwriting hasOwnProperty.

那么,registerProtecteds是何时“注册”到对象上的呢?答案在这里:
So, when does registerProtecteds "registers" to the object? The answer is here:

  1. var Clz = function() { 
  2. registerProtecteds(this); 
  3. return ctor.apply(this, arguments); 
  4. }; 

优势
Advantages

  • 仍可以使用原生的Clz.prototype.method = function(){}定义方法
    Native Clz.prototype.method = function(){...} is still avliable
  • base在不用Class定义的派生类中仍可使用,只需维护baseConstructor
    base is still avaliable in derived classes not defined by Class, only keeping baseConstructor needed.
  • 支持嵌套类
    Nested classes supported

待改进
To do

  • 使用try ... finally造成调试不便
    try ... finally will make debugging unconvient
  • 性能损失
    Preformance lack
  • 不支持ECMA v5定义的读写器,稍加修改即可
    Accessor defined in ECMA v5 is not supported, but it can be supported after a little modification.

 

 

 

 

如何判断客户端是否安装Flash脚本

2010-3-30 14:00:22 开发者 抢沙发(0)

某人问我,这里写下。

基本方法如下:

  1. if (typeof navigator.plugins !="undefined" && typeof navigator.plugins["Shockwave Flash"] == "object") { 
  2. //非IE 已安装Flash 
  3. }else if (typeof window.ActiveXObject != "undefined") { 
  4. //IE   已安装Flash 

 

 

YSlow计算公式

2010-3-25 11:54:29 开发者 抢沙发(0)
F = 8×{100-[4×(JS文件数-3)+4×(CSS文件数-2)+3×(CSS背景图连接数-6) ] }
	+6×(100-10×未使用CDN的连接个数)
	+10×[100-11×(Expire时间小于172800秒的连接个数) ]
	+8×[100-11×(文件大小大于500字节且 未使用gzip的连接个数)]
	+4×[body中没有CSS连接 ? 100∶ (99-10×body中的CSS连接数) ]
	+4×(100-5×head中的JavaScript连接数)
	+3×[不存在CSS表达式 ?100: (90-2×CSS表达式数目) ]
	+3×[100-5×(域名数-4)]
	+4×(100-10×未精简的CSS与JS个数)
	+4×(100-10×Redirect个数)
	+4×[100-5×(重复的JavaScript个数+重复的CSS个数) ]
	+2×(100-11×未使用Etag的连接个数)
	+4×(100-5×未缓存或者缓存时间不足3600秒的AJAX请求个数)
	+3×(100-5×请求方式不是GET的AJAX个数)
	+3×{DOM元素数量<MaxDOM ?100∶ [99-10×⌈((DOM元素数量-900))/250⌉ ] }
	+4×(100-5×状态为404的连接个数)
	+3×[cookie大小不超过1000字节 ?100∶ (99-10×⌊页面cookie的大小/1000⌋ ) ]
	+3×(100-5×不满足cookie free要求的链接个数)
	+4×[100-(2×使用hack的filter个数+5×未使用hack的filter个数) ]
	+3×(100-5×在HTML代码中设定width和height的图片数)
	+2×[100-(favicon不可缓存或缓存时间小于3600秒 ? 5∶0)-(favicon大小超过2000字节? 5∶0)]

Score = F / 89