易栈网-膘叔

浏览模式: 标准 | 列表 | 图文
莫让 KPI 成为"作恶"诱因

莫让 KPI 成为"作恶"诱因

KPI(Key Performance Indicators),目前大多用于公司中某一个部门的业绩考核,当然我们公司也不例外,还是从多方面进行了考核哦。记得发生过其中的一些笑话,KPI考核中有个员工满意度,有一次,KPI考核分好象还不错(因为公司业绩还行嘛),但是员工满意度很低,某些人就说了,员工满意度很低呀,那就少发点奖金了。我靠,这不是刺激别人将员工满意度等打满分吗?如果打满分他会不会又说,既然大家都满意,那我们也不需要发奖金了?

附上在DBA notes里的,就是本文标题的一篇博客:

年底了,受到的垃圾骚扰信息也多,到处都在搞年底业绩大跃进,什么中奖活动、折扣信息...... 眼看着元旦还有几天,最后的疯狂不能错过。

和朋友讨论类似的现象,都认为是 KPI (Key Performance Indicators) 惹得祸,完全以 KPI 为导向的团队,要想成员不作恶是不可能的。为完成小团体的业绩,管他什么企业价值观什么使命感,都是狗屁!赶紧把相关指标搞定,年底好分奖金才是真格的。

国内某些企业的败局,有些案例的导火索就是某些追求业绩的中层捅出的篓子。所谓"风起于青萍之末",未尝没有道理。

或许用企业价值观强行对 KPI 进行干预是一种办法 ? 可操作性还是个问题。

Don't be evil ! 与君共勉!

--EOF--

原文:http://www.dbanotes.net/review/kpi_evil.html

备注:该篇博客的下一篇中还有提到KPI和PV的关系 哦。。。黑黑,这点,我就不多谈了,不接触也没有什么好谈的。人嘛。。。懂的喽

感慨

时间过的真快,一转眼到XX网也已经有了一年多了。回顾这一年多来,却发现自己不知道在忙些什么,又在折腾些什么。
时间如流水般哗哗的漂过,而自己却依旧在希望的田野上徘徊着。迷惘,则是我一年多来最多的心情写照。
每个人都是希望自己能够做一些事情的,我也不例外。虽然在XX网的时间不长,但也经历了一些我认为比较大的事件:
1、年会:一年前,公司所有的员工欢聚一堂,举杯畅饮,还有抽奖活动,Boss开心的说,今年人人都有奖金;一年后,公司以为人均标准100元为目标,让各部门自由举行活动
2、班车:一年前,作为公司福利,我很幸运的乘着某辆班车天天上下班;一年后,公司说地铁通车了,大家乘地铁去。
3、辞退:一年前,公司说我们要发展,我们要扩张;一年后,公司在裁员,大多是老员工(网上可以查到:XX网裁员内幕)
当然这都是公司的一些变化,也让我们感受到了互联网寒冬的到来。现实情况就是寒风凛冽,而我们就站在这瑟瑟寒风中默默感受着。只要是有远见的人,都会在这段时间求变,以便在春天到来时可以积累下庞大的资源。XX网于是就在改版,而我的事情就准备从改版前开始说起。。。
话说。。。(改版的篇幅实在太长,下篇继续[毕竟我原来写这个就写了一小时],文章会加密处理,熟悉我的朋友可以MSN或者QQ上索取密码)
MYSQL中EXPLAIN的说明

MYSQL中EXPLAIN的说明

EXPLAIN列的解释:

  • table:显示这一行的数据是关于哪张表的
  • type:这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL
  • possible_keys:显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句
  • key:实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MYSQL会选择优化不足的索引。这种情况下,可以在SELECT语句 中使用USE INDEX(indexname)来强制使用一个索引或者用IGNORE INDEX(indexname)来强制MYSQL忽略索引
  • key_len:使用的索引的长度。在不损失精确性的情况下,长度越短越好
  • ref:显示索引的哪一列被使用了,如果可能的话,是一个常数
  • rows:MYSQL认为必须检查的用来返回请求数据的行数
  • Extra:关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢

extra列返回的描述的意义

  • Distinct:一旦MYSQL找到了与行相联合匹配的行,就不再搜索了
  • Not exists: MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了
  • Range checked for each Record(index map:#):没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一
  • Using filesort: 看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行
  • Using index: 列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候
  • Using temporary 看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上
  • Where used 使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题不同连接类型的解释(按照效率高低的顺序排序)
  • system 表只有一行:system表。这是const连接类型的特殊情况
  • const:表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索引)。因为只有一行,这个值实际就是常数,因为MYSQL先读这个值然后把它当做常数来对待
  • eq_ref:在连接中,MYSQL在查询时,从前面的表中,对每一个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用
  • ref:这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部分(比如,利用最左边前缀)时发生。对于之前的表的每一个行联合,全部记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好
  • range:这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西时发生的情况
  • index: 这个连接类型对前面的表中的每一个记录联合进行完全扫描(比ALL更好,因为索引一般小于表数据)
  • ALL:这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟糕,应该尽量避免

How JavaScript Timers Work[COPY]

转载这篇文章的原因是它解释了setTimeout和setInterval之间的区别,对于这篇文章,博客园有位朋友进行了翻译,网址如下:http://www.cnblogs.com/rainman/archive/2008/12/26/1363321.html

翻译内容为:

How JavaScript Timers Work

从基础的层面来讲,理解JavaScript的定时器是如何工作的是非常重要的。计时器的执行常常和我们的直观想象不同,那是因为JavaScript引擎是单线程的。我们先来认识一下下面三个函数是如何控制计时器的。

  • var id = setTimeout(fn, delay); - 初始化一个计时器,然后在指定的时间间隔后执行。该函数返回一个唯一的标志ID(Number类型),我们可以使用它来取消计时器。
  • var id = setInterval(fn, delay); - 和setTimeout有些类似,但它是连续调用一个函数(时间间隔是delay参数)直到它被取消。
  • clearInterval(id);, clearTimeout(id); - 使用计时器ID(setTimeout 和 setInterval的返回值)来取消计时器回调的发生

为了理解计时器的内在执行原理,有一个重要的概念需要加以探讨:计时器的延迟(delay)是无法得到保障的。由于所有JavaScript代码是在一个线程里执行的,所有异步事件(例如,鼠标点击和计时器)只有拥有执行机会时才会执行。用一个很好的图表加以说明:


(点击查看大图)

在这个图表中有许多信息需要理解,如果完全理解了它们,你会对JavaScript引擎如何实现异步事件有一个很好的认识。这是一个一维的图标:垂 直方向表示时间,蓝色的区块表示JavaScript代码执行块。例如第一个JavaScript代码执行块需要大约18ms,鼠标点击所触发的代码执行 块需要11ms,等等。

由于JavaScript引擎同一时间只执行一条代码(这是由于JavaScript单线程的性质),所以每一个JavaScript代码执行块会 “阻塞”其它异步事件的执行。这就意味着当一个异步事件发生(例如,鼠标点击,计时器被触发,或者Ajax异步请求)后,这些事件的回调函数将排在执行队 列的最后等待执行(实际上,排队的方式根据浏览器的不同而不同,所以这里只是一个简化);

从第一个JavaScript执行块开始研究,在第一个执行块中两个计时器被初始化:一个10ms的setTimeout()和一个10ms的setInterval()。 依据何时何地计时器被初始化(计时器初始化完毕后就会开始计时),计时器实际上会在第一个代码块执行完毕前被触发。但是,计时器上绑定的函数不会立即执行 (不被立即执行的原因是JavaScript是单线程的)。实际上,被延迟的函数将依次排在执行队列的最后,等待下一次恰当的时间再执行。

此外,在第一个JavaScript执行块中我们看到了一个“鼠标点击”事件发生了。一个JavaScript回调函数绑定在这个异步事件上了(我 们从来不知道用户什么时候执行这个(点击)事件,因此认为它是异步的),这个函数不会被立即执行,和上面的计时器一样,它将排在执行队列的最后,等待下一 次恰当的时候执行。

当第一个JavaScript执行块执行完毕后,浏览器会立即问一个问题:哪个函数(语句)在等待被执行?在这时,一个“鼠标点击事件处理函数”和 一个“计时器回调函数”都在等待执行。浏览器会选择一个(实际上选择了“鼠标点击事件的处理函数”,因为由图可知它是先进队的)立即执行。而“计时器回调 函数”将等待下次适合的时间执行。

注意,当“鼠标点击事件处理函数”执行的时候,setInterval的回调函数第一次被触发了。和setTimeout的回调函数一样,它将排到执行队列的最后等待执行。但是,一定要注意这一点:当setInterval回调函数第二次被触发时(此时setTimeout函数仍在执行)setTimeout的第一次触发将被抛弃掉。当一个很长的代码块在执行时,可能把所有的setInterval回调函数都排在执行队列的后面,代码块执行完之后,结果便会是一大串的setInterval回调函数等待执行,并且这些函数之间没有间隔,直到全部完成。所以,浏览器倾向于的当没有更多interval的处理函数在排队时再将下一个处理函数排到队尾(这是由于间隔的问题)。

我们能够发现,当第三个setInterval回调函数被触发时,之前的setInterval回调函数仍在执行。这就说明了一个很重要的事实:setInterval不会考虑当前正在执行什么,而把所有的堵塞的函数排到队列尾部。这意味着两次setInterval回调函数之间的时间间隔会被牺牲掉(缩减)。

最后,当第二个setInterval回调函数执行完毕后,我们可以看到没有任何程序等待JavaScript引擎执行了。这就意味着浏览器现在在等待一个新的异步事件的发生。在50ms时一个新的setInterval回调函数再次被触发,这时,没有任何的执行块阻塞它的执行了。所以它会立刻被执行。

让我们用一个例子来阐明setTimeout和setInterval之间的区别:

setTimeout(function(){
/* Some long block of code... */
setTimeout(arguments.callee, 10);
}, 10);

setInterval(function(){
/* Some long block of code... */
}, 10);

这两句代码乍一看没什么差别,但是它们是不同的。setTimeout回调函数的执行和上一次执行之间的间隔至少有10ms(可能会更多,但不会少于10ms),而setInterval的回调函数将尝试每隔10ms执行一次,不论上次是否执行完毕。

在这里我们学到了很多知识,总结一下:

  • JavaScript引擎是单线程的,强制所有的异步事件排队等待执行
  • setTimeout 和 setInterval 在执行异步代码的时候有着根本的不同
  • 如果一个计时器被阻塞而不能立即执行,它将延迟执行直到下一次可能执行的时间点才被执行(比期望的时间间隔要长些)
  • 如果setInterval回调函数的执行时间将足够长(比指定的时间间隔长),它们将连续执行并且彼此之间没有时间间隔。

上述这些知识点都是非常重要的。了解了JavaScript引擎是如何工作的,尤其是大量的异步事件(连续)发生时,才能为构建高级应用程序打好基础。



英文原文如下:

At a fundamental level it's important to understand how JavaScript timers work. Often times they behave unintuitively because of the single thread which they are in. Let's start by examining the three functions to which we have access that can construct and manipulate timers.

  • var id = setTimeout(fn, delay); - Initiates a single timer which will call the specified function after the delay. The function returns a unique ID with which the timer can be canceled at a later time.
  • var id = setInterval(fn, delay); - Similar to setTimeout but continually calls the function (with a delay every time) until it is canceled.
  • clearInterval(id);, clearTimeout(id); - Accepts a timer ID (returned by either of the aforementioned functions) and stops the timer callback from occurring.

In order to understand how the timers work internally there's one important concept that needs to be explored: timer delay is not guaranteed. Since all JavaScript in a browser executes on a single thread asynchronous events (such as mouse clicks and timers) are only run when there's been an opening in the execution. This is best demonstrated with a diagram, like in the following:

aab87e80b3d460b7be414cc9cda00be7

There's a lot of information in this figure to digest but understanding it completely will give you a better realization of how asynchronous JavaScript execution works. This diagram is one dimensional: vertically we have the (wall clock) time, in milliseconds. The blue boxes represent portions of JavaScript being executed. For example the first block of JavaScript executes for approximately 18ms, the mouse click block for approximately 11ms, and so on.

Since JavaScript can only ever execute one piece of code at a time (due to its single-threaded nature) each of these blocks of code are "blocking" the progress of other asynchronous events. This means that when an asynchronous event occurs (like a mouse click, a timer firing, or an XMLHttpRequest completing) it gets queued up to be executed later (how this queueing actually occurs surely varies from browser-to-browser, so consider this to be a simplification).

To start with, within the first block of JavaScript, two timers are initiated: a 10ms setTimeout and a 10ms setInterval. Due to where and when the timer was started it actually fires before we actually complete the first block of code. Note, however, that it does not execute immediately (it is incapable of doing that, because of the threading). Instead that delayed function is queued in order to be executed at the next available moment.

Additionally, within this first JavaScript block we see a mouse click occur. The JavaScript callbacks associated with this asynchronous event (we never know when a user may perform an action, thus it's consider to be asynchronous) are unable to be executed immediately thus, like the initial timer, it is queued to be executed later.

After the initial block of JavaScript finishes executing the browser immediately asks the question: What is waiting to be executed? In this case both a mouse click handler and a timer callback are waiting. The browser then picks one (the mouse click callback) and executes it immediately. The timer will wait until the next possible time, in order to execute.

Note that while mouse click handler is executing the first interval callback executes. As with the timer its handler is queued for later execution. However, note that when the interval is fired again (when the timer handler is executing) this time that handler execution is dropped. If you were to queue up all interval callbacks when a large block of code is executing the result would be a bunch of intervals executing with no delay between them, upon completion. Instead browsers tend to simply wait until no more interval handlers are queued (for the interval in question) before queuing more.

We can, in fact, see that this is the case when a third interval callback fires while the interval, itself, is executing. This shows us an important fact: Intervals don't care about what is currently executing, they will queue indiscriminately, even if it means that the time between callbacks will be sacrificed.

Finally, after the second interval callback is finished executing, we can see that there's nothing left for the JavaScript engine to execute. This means that the browser now waits for a new asynchronous event to occur. We get this at the 50ms mark when the interval fires again. This time, however, there is nothing blocking its execution, so it fires immediately.

Let's take a look at an example to better illustrate the differences between setTimeout and setInterval.

setTimeout(function(){
/* Some long block of code... */
setTimeout(arguments.callee, 10);
}, 10);

setInterval(function(){
/* Some long block of code... */
}, 10);

These two pieces of code may appear to be functionally equivalent, at first glance, but they are not. Notably the setTimeout code will always have at least a 10ms delay after the previous callback execution (it may end up being more, but never less) whereas the setInterval will attempt to execute a callback every 10ms regardless of when the last callback was executed.

There's a lot that we've learned here, let's recap:

  • JavaScript engines only have a single thread, forcing asynchronous events to queue waiting for execution.
  • setTimeout and setInterval are fundamentally different in how they execute asynchronous code.
  • If a timer is blocked from immediately executing it will be delayed until the next possible point of execution (which will be longer than the desired delay).
  • Intervals may execute back-to-back with no delay if they take long enough to execute (longer than the specified delay).

All of this is incredibly important knowledge to build off of. Knowing how a JavaScript engine works, especially with the large number of asynchronous events that typically occur, makes for a great foundation when building an advanced piece of application code.

使用开源软件设计、开发和部署协作型 Web 站点

使用开源软件设计、开发和部署协作型 Web 站点

一直以来,IBM developworks都是我比较关注的网站,它上面有很多内容是IBM公司内部人员所写,也有一部分内容是业界领先人士根据自身经验写出的教程,因此,它一直是我的订阅对象之一。本文就是其中一篇比较旧的文章,最初发表于2007年3月15日,但是从今天的眼光来看,它还是有一定的学习价值,所以我贴出来了。

简介如下:(原文我就不贴了,大家可以点其中的链接我自己也会点过去看的)

现在,Web 站点已经成了业务的重要部分,而用来创建和部署 Web 站点的工具也变得更加灵活和容易使用。但是,复杂 Web 应用程序的开发并不轻松,它们需要的不只是标准的交互和更新方法(比如 blog)。组织中的每个应用程序常常还需要进行定制。

开放源码社区提供了各种工具,结合使用这些工具可以为复杂的 Web 应用程序创建一个有用的开发和生产环境。这个系列文章来自 IBM Internet Technology Group 团队,他们将展示如何把开放源码软件作为基础,并提供一种方法和一些改进来帮助简化 Web 站点的开发过程。尽管定制仍然是有必要的,但是这个系列讲解了如何使用开放源码工具和技术快速建立和运行复杂的 Web 站点。

在这个系列中,Internet Technology Group 团队通过一个虚构的组织,International Business Council(IBC),来展示如何更有效地尽可能地扩展 Web 站点的功能,这些功能包括文档存储、讨论组、专门的工作组、研讨会日程安排、日程议题描述、会话过期和其他任务。他们举例说明了创建这个 Web 站点需要用到下列开放源码工具:

  • Drupal - 开放源码的内容管理系统
  • MySQL - 开放源码的数据库
  • PHP - 可以使用 PHPMyAdmin 和 SQLBrowse 创建动态 Web 内容的开发语言
  • Apache - 开放源码的 Web 服务器
  • Eclipse - 开放源码的开发环境
  • CVS - 用于跟踪代码变更的代码管理系统

Internet Technology Group 团队会首先介绍业务场景以及选择开源工具的决定因素,他们还通过描述一个灵活的开发方法来讲解了应用程序的设计流程。这个流程可以用来设计 Web 站点或者应用程序的用户体验。接着,他们会一步一步地指导如何安装和使用前面所提到的开发工具套件。这些步骤包括:

  • 建立开发环境
  • Drupal 入门
  • 着重介绍 Drupal 与其它软件工具的交互(如 MySQL, Ajax 和 PHP)
  • 构建 Drupal 定制模块
  • 部署和调整安装

沿着这条道路,Internet Technology Group 团队同其他可选方案进行了对比,并讨论了如何通过集成其它软件组件来尽可能地增强这些工具。

现在就链接到 项目实现:

EditPlus的一些其他技巧

EditPlus的一些其他技巧

昨天我转载了别人的一篇editpllus的技巧,现在我也将我常用的一些技巧放出来,算是和大家共享吧。
1、选中一列内容

选中一列内容在从excel等其他文件格式导成Txt的时候特别有用,比如我们不要当中某列数据的时候,确实有点痛苦,如果有规律,我们还能写正则进行替换,如果没有规则怎么办?
1.先关闭自动换行(菜单栏上有一个W,或者按快捷键ctrl+shift+w)
2.按住 alt键,用鼠标进行选择,你就会发现你选择的内容是屏幕中的某一块区域,而不是平时那种按照行来进行选择的了

2、autocomplete
对于我们写PHP代码的人来说,自动完成这个功能,很重要,有的变量经常用,但是打起来很繁,比如:$_POST;$_GET;$_REQUEST等,象这些变量,在写的时候要按着shift键才有用,如果经常写,我当然是希望有快捷的方式了。
1.点击菜单Tools->Preferences然后选中Files->setting&syntax,选择PHP
2.下面有两个选择的地方哦:setting&syntax和auto completion,这两个,一个是语法文件,一个是自动完成文件,有了这两个文件,你的代码就会被自动着色,函数和class等的着色就会和平常不一样,syntax文件官方有下载,但autocomplteion官方就没有了,不过你可以自己写,我就是自己写的。

格式如下:
#TITLE=PHP
#CASE=y

#T=h_401
header( 'HTTP/1.0 401 Unauthorized' );
exit();
#T=h_404
header( 'HTTP/1.0 404 Not Found' );
exit();

#T代表了你要输入的字符,下面的内容就是你输完后,在屏幕上输出的内容
比如,我输入h_401,在我按空格后,屏幕上就会立即跳出:

header( 'HTTP/1.0 401 Unauthorized' );
exit();

多方便呀。

顺便透露一下,htmlbar.acp其实就是一些快捷键的自定义哦,你也可以通过修改这个文件,达到对菜单的自定义。

3、页面函数显示
editplus太轻量级了,没有办法象IDE那样在打开某个文件后还能显示文件中的class和function,但也有一个简单的替代方法,那就是快捷键:ctrl+F11,你就可以快速查看页面中的function了,好象。。。。对public function aaa()这样的无效。这样的话也就是说PHP5文件中的function是看不到的。唉。没办法了。。。

先介绍这一点。功能有太多。慢慢来喽

PHP负载均衡

PHP负载均衡

文章的内容写的不错,所以转载一下。
原文:http://xinsync.xju.edu.cn/index.php/archives/2946
内容如下:

过去当运行一个大的web应用时候意味着运行一个大型的web服务器。因为你的应用吸引了大量的用户,你将不得不在你的服务器里增加更多的内存和处理器。

今天,’大型服务器’模式已经过去,取而代之的是大量的小服务器,使用各种各样的负载均衡技术。这是一种更可行的方法,将使硬件成本降至最低。

‘更多小服务器’的优势超过过去的’大型服务器’模式体现在两个方面:

1. 如果服务器宕机,那么负载均衡系统将停止请求到宕机的服务器,转而分发负载到其他正常运行的服务器上。
2. 扩展你的服务器更加容易。你要做的仅仅是加入新的服务器到负载均衡系统。不需要中断你的应用运行。

所以,把握住这个机会:). 当然,代价就是这要求你的应用开发时增加一点复杂度。这就是本文要覆盖的内容。

这时你可能对自己说: ‘但是我怎么知道我正在使用负载均衡呢?’。最诚实的回答是,如果你正在问这个问题,那么答案是你多半没有在使用负载均衡系统并且你的系统不需要考虑这个问题。大多数情况,当应用成长足够大的规模时,负载均衡就需要明确提出和设置了。然而,我也偶尔看见虚拟主机公司为客户的应用做这个负载均衡,或者像下面描述的那样要自己来做。

在继续下面的内容之前,我要指出本文主要描述PHP的负载均衡。将来我可能会写有关数据负载均衡的文字,但是现在你必须等待。

注意,我一直提“web应用”而不是website,这是想区分’web应用’是那些复杂的站点往往涉及服务器端编程和数据库,而不是website那样只显示简单的静态内容。
1. PHP文件

第一个问题是,如果你有大量的小型服务器,你怎么把你的php文件上传到所有的服务器上?有如下的方法供你参考:

1. 分别上传所有的文件到每一个服务器 , 这种方法带来的问题是:想像一下你有20个服务器,那么上传过程中这将很容易导致错误,并且更新时极有可能导致不同服务器上有不同版本的文件。
2. 使用 ‘rsync ‘ (或类似的软件) . 这样的工具能同步本地目录和多个远程主机目录上的文件。
3. 使用版本控制软件(如subversion ) . 这是我最喜欢的方法。用它可以很好地维护我得代码,当发布我的应用时,可以在每一个服务器上运行svn update命令同步。这种方法也使切换服务器得代码到过去的某一个版本更加容易。
4. 使用一个文件服务器(你可能发现NFS 非常适合做这件事情). 这种方式是使用一个文件服务器来存放你的web应用. 当然,如果你的文件服务器宕机,那么多所有你的站点将不能使用。这时,你就需要花费更多的开支来恢复它。

选择哪种方式依赖于你的需求和你掌握的技能。如果你使用版本控制系统,那么你可能得计划一个方法如果同时执行一个更新命令更新所有服务器上的代码。然而,如果使用文件服务器,你就要实现一些失败恢复机制,防止万一服务器宕机导致请求失败。
2. 文件上传

当只有一台服务器时,文件上传不是一个问题。但是当我们有多台服务器时,那么上传的文件应该怎么存放呢?上传文件的问题和跨服务器php文件存储是类似的。下面是几种可能的方案:

1. 把文件存储到数据库中 。大多数数据允许存储二进制数据。当你请求文件下载时,访问数据把二进制数据和相应的文件名和类型输出给用户。在使用这种方案前应该考虑数据库怎样存储你的文件。该方法的问题在于如果数据库服务器宕机将使文件不可用。
2. 在一个文件服务器上存储上传的文件 . 与前面的介绍一样,你要安装一个文件服务器让所有web服务器共享,把所有上传的文件上传到这里,上传后所有的web服务器就都可以使用它。但是,如果文件服务器宕机,那么可能发生图像文件下载中断。
3. 设计你自己的上传机制传输文件到服务器到每一个服务器 . 这个方法没有单个文件服务器或者数据库方案的缺陷,但是将增加你代码的复杂度。例如,如果上传到多个服务器过程中,服务器宕机,你要怎么处理?

用数据库存储上传文件但是设计一个文件缓存机制是一个不错的方案。当服务器接收一个文件下载请求时,首先检查缓存系统中是否有该文件,如果发现那么从缓存系统下载,否则从数据库读取并把它缓存到文件系统中。
3. 会话(Sessions)

如果你熟悉php的session处理,你将可能知道默认情况下,它存储session数据在服务器的临时文件里。而且,这个文件仅仅在你请求处理的那个服务器上,但是接下来的请求可能被另外一个服务器处理,这将在另一个服务器上生成新的session。这导致session频繁地不被识别,如登录用户总是要求重新登录。

我推荐的方案是,要么重新php内建的session处理机制存储session数据到数据库,或者实现你自己的机制保证发送一个用户的请求到同一台服务器。
4. 配置(Configuration)

尽管这个话题不是和php特别相关,我感觉还是有必要提及。当运行集群服务器时,用某种方法保持服务器之间的配置文件同步是一个好主意。如果配置文件不一致,可能导致一些非常奇怪的断断续续的行为导致很难排查这些问题。

我推荐使用版本控制系统单独管理他们。这样你可以为不同的项目安装存储不同的php配置文件,也可以保持所有服务器配置文件同步。
5. 日志(Logging)

像配置问题一样,logging不是仅仅和php相关。但是对于保持服务器健康运行它仍然是非常重要的。没有正确的logging系统,你怎么知道如果PHP代码开始产生错误(在系统正式运行时,你总是关闭display_errors 设置,不是吗?)

有几种方法你可以实现logging:

1. 在每一个服务器上记录日志。 这是最简单的方法。每一个机器仅仅记录一个文件。好处是简单,可能只要很少的配置。但是,随着服务器数量的增多,监控每台服务器上的日志文件将变得非常困难。
2. 记录日志到一个共享 这种方法每一个服务器仍然有这个日志文件,但是他们通过共享机制被存储在一个中央文件服务器上,这将使监控日志变得更简单。该方案的问题在于,如果文件服务器不可用将导致一个简单的日志不能写入问题最终导致整个应用崩溃。
3. 记录日志到logging服务器 你可以使用一个logging软件,如syslog 来把所有的日志写到一个中央服务器。尽管这个方法要求更多的配置,但是他也提供了最健壮的方案。

SVN的机制确实是很多人现在所考虑的,一来这样保证了代码的同步,二来也不需要担心开发版和上线版的区别,更重要的是,每次的update你肯定不会有错。

文件上传其实才是一个大头,当你的服务器过多的时候,你如何保证每一台服务器的上传内容同步?如果你同步了,那么这么多的冗余文件是否是一个浪费?如果你不同步,而采用同一个NFS服务器来存储,那么就象文中说的如果NFS宕机了怎么办?给NFS也来一个负载均衡?

总之,当服务器越来越多的时候,你考虑的就不仅仅是代码的问题,而是架构的问题

新闻一则:雨林木风工作室宣布解散

新闻一则:雨林木风工作室宣布解散

内容来自cnbeta,未作删减。原文网址:http://www.cnbeta.com/articles/73351.htm

内容如下:

据中国站长站报道,国内知名操作系统DIY商雨林木风宣布解散,雨林木风工作室负责人罗先生表示消息属实,并发来正式声明.
罗先生表示,雨林木风工作室选择此举也属无奈,随着国内版权意识的加强、以及早些时候的番茄花园事件.雨林木风的关闭也属大势所趋.而随着雨林木风的关 闭,雨林木风所提供的系统也将成为历史.所有版本仅供爱好者收集纪念,禁止非法用途及传播.而关于雨林木风工作室解散以后的动向问题,罗先生并未回应.

以下为声明全文:

亲爱的会员朋友们:

基于多方面的原因,我们不得不很遗憾地告诉大家,经过雨林木风工作室管理层慎重考虑,决定于2008年12月31日起正式解散.今后,雨林木风将以自主研 发软件产品为主,继续为网民提供服务(不再进行未开源操作系统技术研究),所有成员与工作室脱离一切隶属关系,而雨林木风论坛将继续独立运营,不受本工作 室解散之影响.

为了铭记这些曾经为我们提供了精品资源的技术精英们,同时,也为众多的论坛会员留下一份美好的回忆,我们特别奉献一套经典收藏版[包括:雨林木风 Ghost XP SP3 装机版 YN9.9;雨林木风 Ghost XP SP3 纯净版 YN6.0;雨林木风 WinXP SP3 安装版 YS8.0;雨林木风 Windows Vista ULTIMATE With SP1 精简版V2.1]以供大家收藏留念,禁止非法用途及传播.

历经三载,我们风雨同行,一路走来,雨林木风工作室由衷地感谢各界朋友们对我们的支持与厚爱!雨林木风工作室也将成为一段历史!再次感谢3年来一直支持着本工作室作品的雨林木风迷们!希望大家能继续支持雨林木风旗下软件部门推出的其他软件产品.

特此声明!
雨林木风工作室
二〇〇八年十二月二十六日

让Editplus自动格式化css和js

让Editplus自动格式化css和js

对于我们使用PHP进行网页开发的人员来说,EditPlus应该是我们的常用工具了吧。emeditor我是用不顺手的,UltraEdit的着色也是我不喜欢的(或许是因为我用EP的时间太长了吧)。

经常用啊用,也会有一些技巧的积累,不过今天我是看到了别人的技巧,先贴上来(主要是看上他那个工具了,呵呵)

原文网址:http://www.cnblogs.com/darkangle/archive/2008/12/25/editplus_auto_format_js_css.html

作者是:沙加

原文内容:

受阿一同学的启发俺也来发个小东西.

虽然很喜欢editplus的小巧快速,但是很遗憾它不支持代码的自动格式化,现在写php,css,html都是用的Aptana,这个感觉不错。

今天正好看了一下editplus 的用户工具,它允许通过命令行调用和标准输出输出与其它程序进行交互,可以传入参数可以手动指定,也可以是当前选定内容,当前本件所有文本等,即然这样我 们就利用它的filter模式,即把所有文本内容传过去,再使用回传内容替换当前文本。那么我们还需要一个外部工具,为了方便起见,脚本类语言当然是首 选,而在windows平台下面可以使用vbs和JScript, 那么就用JScript吧,去网上找一个 http://github.com/einars/js-beautify/tree/master 的东西,它提供在线的js格式化功能,正好是js写的, 拿它回来稍做改造一下,使用WScript的Std接口接收和发送内容
var input = "";
while(!WScript.StdIn.AtEndOfStream)
{
input += WScript.StdIn.ReadAll();
}
从输入端读入数据.
WScript.Echo(formated_code); 发送数据.
那么js的格式化就搞定了, 再来看看css的,很怀念VS里面可以将一个CSS定义都放到一行上面去,这样看起来比较方便,否则代码太长了要拖半天. 这样的话看起来也不是太难, 就自己动手写一个了. 两个文件我已经打包. 下载完可以解压到任意目录,接着就配置Editplus.

打开Tools->ConfigUserTools, 工具名称可以随意,比如jsFormatter, 在Command处写上 Cscript.exe /nologo e:\jsFormatter.js 后面的路径就是js的引用路径, 并钩选下面的run as text fileter,让它作为文本过滤器使用,OK,现在打开一个待格式化的js 文件, 按下Ctrl+1(默认的第一个用户工具快捷键), 可以看到文本已经格式化好了, 不过等等, 上面怎么有MS的版权申明? 看来这个CScript也不是白用滴;( , 不过没关系,俺们还是有办法,按下Ctrl+Q, 存到Alt+1里面, 开始录制动作, 依次按下Ctrl+Home 回到文档开始处,Alt+Shift+Del 三次, 删除三行Ctrl+Q退出记录,这样就完成了, 以后在格式化之后播放一次这个键盘操作记录就行了. 感谢 键客 提醒.


后记:
要是可以格式化html和php那就更好了, js-beautify 里面也可格式化html,可惜效果不太好,我就没放进去,要是能有Aptana编辑器那么出色的效果就好了, 它可以完美处理js脚本, css, html的混合格式化, 想了一下似乎可以建一个java的控制台程序引用Aptana的插件 jar 包就能实现, 通过ICodeFormatter可以完成对各种类型代码的格式化, 留待以后研究.

不会造工具的猩猩永远是猩猩,会自己制造工具的程序员可以有效提高自己的生产力,windows下面的VBScript, JScript和Python, Perl 等脚本语言都是很好的助手. 在这个应用中先查看Editplus帮助,再搜索MSDN脚本技术参考,顺便复习一下正则表达式应用, 既能解决问题又能提高技能水平, 希望大家都 行动起来自己动手DIY, good luck!

两个文件见附件: darkangle_js_css_Formatter.rar


我自己以使用 PHP beautiful 插件比较多,但JS写的也不少,所以,看到这个工具,我感觉确实有用,所以复制下来了。

本站备份:6e57c31ecc3faefe1fa47c0ee576bba6


[转载]一步一步学习Firefox扩展的开发[据说这将是一个系列]

Firefox的插件也曾经研究过,对于XUL感觉也有点痛苦,当初想研究它是为了给自己的博客增加插件,可以方便的让自己在找到好的资料的时候,可以直接通过插件发送到博客上。但最终我还是放弃了。没有什么理由。。。。

看到有人在贴一步一步学习的教程,又有点拾起了回忆,如果有可能,这个系列,我会在这个作者写的同时,也一点点的转载。
希望作者不要见怪,转载完了,我也贴点我在开发的时候遇到的问题之类。当然一切都到元旦后。。。
原文网址:http://www.cnblogs.com/uubox/archive/2008/12/25/1361278.html
作者:DOMS
内容:Firefox提供开放、强大且灵活的扩展机制,因此衍生出了大量功能丰富的扩展组件,这些扩展组件甚至可以说是某些用户爱上firefox的主要因素。
比较可惜的是firefox扩展开发的中文资料相对比较少,因此在这里我会从简单到深入介绍开发firefox扩展的一些技术及常遇到的问题。

本文主要内容:

  • 制作一个包含工具栏按钮及右键菜单的简单组件
  • 解决扩展安装后工具栏按钮却不显示的问题


因为这个组件不需要设计交互窗口,因此开发过程相对简单,下面是这个组件运行的效果图:
firefox_addons_demo1_view.jpg

在开始之前先介绍一下几个网址:

  1. https://developer.mozilla.org/en/Extensions 官方开发网,类似MSDN,是开发时的主要参考资料;
  2. https://developer.mozilla.org/en/Building_an_Extension,官方的一个基础教程,笔者通过此篇文章而入门;
  3. http://ted.mielczarek.org/code/mozilla/extensionwiz/,一个在线的创建扩展的向导,如果你想立即创建一个属于自己的扩展,可以试试用这个向导制作一个雏形,然后再自己慢慢添砖添瓦。本文所讲的例子也是用这个向导制作出来的。

另外还有本文例子的源代码:http://www.uushare.com/user/csprogram/file/1139797。

好了,下面就开始讲解本文的示例组件:uusharedemo1的制作过程。

1、了解扩展组件的构成

Firefox 扩展组件是以一个xpi文件的形式发布的,而xpi文件实际上是zip压缩包文件,我们只要把Firefox扩展组件下载回来(用右键另存为即可下载), 把文件扩展名改为zip即可解压并看到所有的源代码,有时可能还会在压缩包里面看到jar格式文件,这个也是zip压缩包,改名解压即可看到源代码。因此 多下载优秀的扩展回来慢慢研究其源代码也不失为一种快速的学习方法。
一般xpi文件内部有(但不一定都有)如下目录及文件:
xpi_filetree.png
其中 uusharedemo1 为本文示例组件的名称,可以看到压缩包首层有:install.rdf及chrome.manifest两个文件以及一个chrome目录。

  • install.rdf:是该组件的安装信息,例如组件的名称、版本、作者等信息,XML格式;
  • chrome.manifest:是用于描述该组件由哪些文件组成的,即一个资源列表;


在chrome目录里就是该组件的主要内容了,一般有:content、defaults、locale、skin等目录,它们的作用如下:

  • content:这个是最主要的目录,包含组件的界面定义以及程序代码,其中xul文件是界面定义文件,类似SilverLight的XAML文件,同样是XML格式;js文件是组件功能实现的代码,全部用javascript实现的。
  • skin:存放css样式表文件及资源位图,css是用来控制组件界面外观的。
  • locale:如果想让组件能够在不同语言的firefox里显示正确的文本,则将不用的语言列表放到此目录,firefox会自动加载合适的语言文件。这点有些类似Win32PE可执行程序中的资源文件。
  • defaults:存放一些默认值,一般不要此目录也可以;


我 们可以把Firefox程序认为是一个平台,而我们看到firefox浏览器外观本身也是用跟扩展组件类似的结构搭建起来的,感兴趣的朋友可以解压 “C:\Program Files\Mozilla Firefox\chrome”目录下的jar文件观摩一下firefox浏览器外观的源代码,所以我们制作的扩展组件也会成为firefox浏览器自身 的一部分,可以实现非常强大而灵活的功能(例如Firebug这个神奇的组件)。相对来说,开发ie扩展程序显得困难多了,除非只是想做工具栏按钮和右键 菜单(用注册表即搞定),否则需要使用VC,VB,Delphi这些工具开发COM组件才能完成。

如果想了解firefox浏览器本身各个元素的定义,但又不想阅读chrome目录下的脚本代码,可以安装 DOM Inspector 这个组件,这个组件有如开发web时的FireBug,是开发具有界面的扩展组件时的神兵利器!

提醒:自己编写的组件代码文件最好用“UTF-8”格式保存,一般可以减少很多麻烦的问题。

2、认识install.rdf

install.rdf包含组件安装信息,本文例子中它的内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>uusharedemo1@uushare.com</em:id>
<em:name>Uushare书签(示例)</em:name>
<em:version>1.0</em:version>
<em:creator>KwanhongYoung</em:creator>
<em:description>这是一个模仿Uushare书签插件的示例</em:description>
<em:homepageURL>http://www.uushare.com</em:homepageURL>
<em:iconURL>chrome://uusharedemo1/content/uusharedemo1.png</em:iconURL>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- firefox -->
<em:minVersion>1.5</em:minVersion>
<em:maxVersion>3.0.*</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>



文件里有很多是固定的写法,这里就不一一讲解了。
第 5行的id是组件的一个标识符号,类似Windows里COM组件的CLSID,用于区别其他组件。可以使用 xxx@yyy.zzz 这样类似email地址的格式组成,而这个“email地址”不要求是真实或有效的。也可以用的GUID来标识,例如“{c45c406e- ab73-11d8-be73-000a95be3b12}”是WebDevelop组件的标识符。

name、version、creator、description、homepageURL分别是组件的名称、版本、创建者的姓名、组件描述、组件官方网站地址。
iconURL用于显示组件的图标,格式一般用32x32pixel的png图片。
这里出现了chrome://这样的地址,跟http://和file://协议类似,chrome://也是用来定位资源的。格式是:
chrome://组件名称/目录路径/文件名
需要注意的是,这里的组件名称可以跟em:name属性中所指定的名称(用于显示的)不一样的,并且这个组件名称需要在chrome.manifest里定义。
这些信息在安装完组件之后能够在firefox的组件管理列表显示,如下图:

firefox_addons_demo1_item.jpg

第14行比较重要,用于标识当前组件是用于哪个平台的(firefox/Thunderbird/SeaMonkey等),如果开发的组件是给firefox用的,则id={ec8030f7-c20a-464f-9b0e-13a3a9e97384}。
第 15、16行用于指定当前组件适用于firefox的哪些版本,因为firefox从1.5到3.0有些地方有所改变,所以如果你不知道自己的组件在哪些 版本下可运行的话,最好先测试一下,然后准确地指定一个范围值,上例的值表示该组件适用于firefox1.5到3.0及3.0所有修订版。版本号码不能 自己随便造,这里有一个完整的列表:
https://addons.mozilla.org/en-US/firefox/pages/appversions

3、认识chrome.manifest

这是资源列表文件,内容如下:

content uusharedemo1 chrome/content/
locale uusharedemo1 en-US chrome/locale/en-US/
skin uusharedemo1 classic/1.0 chrome/skin/
overlay chrome://browser/content/browser.xul chrome://uusharedemo1/content/firefoxOverlay.xul
style chrome://global/content/customizeToolbar.xul chrome://uusharedemo1/skin/overlay.css



前3行每一行表示一种类型的资源的地址,格式是:
资源名称<Tab>组件名称<Tab>参数<Tab>目录路径
其中组件名称是自定义的,只要跟其他组件名不一样并且使用全小写字母即可。定义好之后,就可以使用chrome://地址来定位资源了。例如,假设当前开发文件路径是“c:/dev/uusharedemo1/”,那么:
chrome://uusharedemo1/content/uusharedemo1.png 实际指的是 c:/dev/uusharedemo1/chrome/content/uusharedemo1.png 这个文件。

第4行及第5行表示将当前组件的界面结合到系统本身的界面,因为当前组件会修改firefox本身的工具栏及右键菜单,所以需要加上这两行。

4、default/、skin/、locale/

这几个目录下的文件比较简单,下载本文的源代码查看一般能明白其中的意思,为了节约篇幅这里就不讲解了,等以后我写后续的章节时再详细介绍。

5、认识 content/firefoxOverlay.xul

Firefox的所有窗口都是使用xul文件定义的,例如在地址栏输入:
chrome://browser/content/aboutDialog.xul
(这个地址对应的源文件是:“C:\Program Files\Mozilla Firefox\chrome\browser.jar/content/aboutDialog.xul”
就可以看到firefox的“关于”对话框,在这个例子里我们用firefoxOverlay.xul定义一个工具栏按钮、右键菜单以及一个工具菜单项,源代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 下面一行指明当前窗口控件所引用的样式表文件 -->
<?xml-stylesheet href="chrome://uusharedemo1/skin/overlay.css" type="text/css"?>

<!-- 这行指明当前窗口控件所使用的语言文件,注意这里不需指定语言的名称(即zh-CN或en-US等),只需指明文件名即可
有了语言文件之后,所有控件的文本(即text/label等属性)都统一写到语言文件里。 -->
<!DOCTYPE overlay SYSTEM "chrome://uusharedemo1/locale/uusharedemo1.dtd">

<overlay id="uusharedemo1-overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<!-- 这行用于指定当前窗口控件的事件(如单击事件)触发后所执行的代码所在的文件 -->
<script src="overlay.js"/>
<stringbundleset id="stringbundleset">
<stringbundle id="uusharedemo1-strings" src="chrome://uusharedemo1/locale/uusharedemo1.properties"/>
</stringbundleset>

<menupopup id="menu_ToolsPopup">
<menuitem id="uusharedemo1-hello" label="&uusharedemo1.label;"
oncommand="uusharedemo1.onMenuItemCommand(event);"/>
</menupopup>

<popup id="contentAreaContextMenu">
<menuitem id="context-uusharedemo1" label="&uusharedemo1Context.label;"
accesskey="&uusharedemo1Context.accesskey;"
insertafter="context-stop"
oncommand="uusharedemo1.onMenuItemCommand(event)"/>
</popup>

<toolbarpalette id="BrowserToolbarPalette">
<toolbarbutton id="uusharedemo1-toolbar-button"
label="&uusharedemo1Toolbar.label;"
tooltiptext="&uusharedemo1Toolbar.tooltip;"
oncommand="uusharedemo1.onToolbarButtonCommand()"
class="toolbarbutton-1 chromeclass-toolbar-additional"/>
</toolbarpalette>
</overlay>

menupopup 段定义了一个位于“工具”菜单栏下的项目,其中id是菜单项的名称,我们自己添加的菜单项、按钮等结合到firefox本身的控件的id必须惟 一;label属性是菜单项显示的文本,这里因为引用了语言文件,所以真正显示的文本内容位于语言文件(uusharedemo1.dtd)里标有 uusharedemo1.label 这行定义的后面;oncommand属性定义菜单被点击后执行 overlay.js 里面的那一个方法过程。

popup 段定义了一个右键菜单项,accesskey为菜单项的快捷键(即带下划线的字母),insertafter为新菜单项所出现的位置位于id为 “context-stop”(即“停止”)这项的后面,如何得知某一项菜单项的id呢?出动Dom Inspector 吧。包括firefox工具栏上面的所有按钮的id,都可以用Dom Inspector查看得知。

toolbarpalette段定义了一个工具栏按钮,其中tooltiptext为鼠标停留在这个新按钮上面时所显示的提示文本。

上面一段代码并没有定义自己的窗口,我会在下一章再讲解。

6、认识content/overlay.js

overlay.js 文件是专门为上面的firefoxOverlay.xul服务的,(怎么知道的呢?在firefoxOverlay.xul文件的第13行有引用啊)。在 xul文件里可以看到菜单被点击之后会调用一个名叫“onMenuItemCommand”的方法过程,而工具栏按钮被点击之后会调用 “onToolbarButtonCommand”方法过程。我们想在他们被点击之后做什么事情呢?为了演示如何在js里编写调用firefox对象的代 码,这里模仿uushare书签的功能,让菜单和按钮点击之后调用Uushare.com网站上的书签服务的添加书签的接口,接口是这样的:

http://www.uushare.com/bookmark/save?v=1&client=ff10&noui=yes&jump=close&url=XXXX&title=YYYY&content=ZZZZ

其中XXXX为待收藏的网页地址,YYYY是网页的标题,ZZZZ是网页中选中的文本。所以我们需要在js里获取当前网页的URL、Title以及被选中的文本。
下面是例子中的代码:

var uusharedemo1 = {
onLoad: function() {
// initialization code
this.initialized = true;
this.strings = document.getElementById("uusharedemo1-strings");
document.getElementById("contentAreaContextMenu")
.addEventListener("popupshowing", function(e) { this.showContextMenu(e); }, false);
},

showContextMenu: function(event) {
// 根据上下文决定显示/隐藏右键菜单项
// see http://kb.mozillazine.org/Adding_items_to_menus
document.getElementById("context-uusharedemo1").hidden = gContextMenu.onImage;
},

onMenuItemCommand: function(e) {
uusharedemo1.addToUushareBookmark();
},

addToUushareBookmark: function() {
//这里调用 uushare.com 书签API的接口
var location, title, desc;
var browser = window.getBrowser();
var webNav = browser.webNavigation;

if (webNav.currentURI)
location = webNav.currentURI.spec;
else
location = gURLBar.value;

if (webNav.document.title) {
title = webNav.document.title;
}
else {
title = location;
}

var focusedWindow = document.commandDispatcher.focusedWindow;
var winWrapper = new XPCNativeWrapper(focusedWindow, 'getSelection()');
desc = winWrapper.getSelection().toString();

var serverUrl = "http://www.uushare.com/bookmark/save?v=1&client=ff10";
var saveUrl = serverUrl + "&noui=yes&jump=close&url=" + encodeURIComponent(location) + "&title=" + encodeURIComponent(title) + "&content=" + encodeURIComponent(desc);
window.open(saveUrl, 'uusharev1', 'location=yes,links=no,scrollbars=yes,toolbar=no,width=550,height=550');
},

onToolbarButtonCommand: function(e) {
uusharedemo1.addToUushareBookmark();
}

};
window.addEventListener("load", function(e) { uusharedemo1.onLoad(e); }, false);



相信对于熟悉js的朋友上面的代码并不难。大部分代码跟在普通html页面里写的差不多,只是firefox提供了内置的一些对象需要翻查官方的文档才能知道它的作用及其成员。

7、打包、测试

至 此我们的扩展组件就编写完毕了,下面开始做安装测试。首先用压缩工具将install.rdf、chrome.manifest以及chrome目录打包 成一个zip格式的压缩包,要注意的是这2个文件及chrome目录必须在压缩包的顶层目录,否则安装会失败。然后将压缩包文件的扩展名更改了 “xpi”,并把这个文件拖到firefox窗口当中,firefox应该会提示安装并要求重启,重启完之后若无问题即可看到我们制作的“工具”栏菜单 项、右键菜单等。如果组件的源代码有错误,例如xul文件或js脚本文件有错误,一般什么预期的效果都看不到,这时需要重复卸载->修复错误代码 ->重新打包->安装这几个步骤。
开发firefox扩展组件最烦人的事情可能就是调试问题了,幸好firefox提供了一些工具,例如 Extension Developer's Extension 等可以减少一些麻烦。

8、工具栏按钮怎么看不到?如何解决

刚 才我们测试的时候会发现虽然右键菜单、“工具”菜单项都显示出来了,不过怎么工具栏上面的按钮没有显示呢?估计这是很多扩展组件开发者都会遇到的共同问 题,原来firefox将非系统默认的按钮都放到按钮箱里,如果想显示这些按钮,需要对着工具栏右键鼠标,选择“定制”,然后将需要的按钮拖出来才行。这 个特性某种程度上可以给用户最大的选择权,不过另一方面这特性给初级的用户带来困惑。所以我们最好能将工具栏按钮自动显示出来。下面是目前“民间”常用的 方法:
大致思路跟我们平时修改windows注册表类似。firefox工具栏上面的按钮布局被储存在firefox设置表中 navigator-toolbox/nav-bar/currentset 键下面,键的值为当前各个工具栏按钮id的以逗号分隔而连在一起的字符串。我们必须读出这个字符串,并把自己的按钮的id(本示例中按钮id为uusharedemo1-toolbar-button,见xul文件)掺和在其中,再写入新的字符串到设置表。
那 么在什么执行此连串操作呢?很明显不能在每次firefox启动时都执行,因为这样会耗时并影响firefox的启动速度,“民间”的方法是先在设置表中 检查某个自定义的键(例如extensions.uusharedemo1)的值,如果这个值为空的或者不等于当前组件的版本,则执行上述的连串操作,并 写入当前组件的版本号。所以完整的过程是:

  1. 读取某个自定义键(extensions.uusharedemo1)的值;
  2. 如果键的值为空或者不等于当前组件版本号,则下一步,否则退出过程;
  3. 读取navigator-toolbox/nav-bar/currentset键的字符串;
  4. 寻找自己按钮的id是否在字符串当中,不存在则将id连接到地址栏id(urlbar-container)的前面;
  5. 将新字符串写入键;
  6. 写当前组件的版本号到自定义键。

下面是上面过程的js代码:

var uusharedemo1 = {
strVersion : '1.0',

getUBKCharPref: function(strPrefName) {
this.prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
try {
return(this.prefManager.getCharPref("extensions.uusharedemo1." + strPrefName));
} catch(e) {
return(false);
}
},

setUBKCharPref: function(strPrefName, strValue) {
this.prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
this.prefManager.setCharPref("extensions.uusharedemo1." + strPrefName, strValue);
},

onLoadEvent: function()
{
//first load
var strPrefVersion = this.getUBKCharPref('version');

if(strPrefVersion != this.strVersion) {

var bolWongPostButtonExists = document.getElementById('uusharedemo1-toolbar-button');

if(!bolWongPostButtonExists) {
var objToolbox = document.getElementById("navigator-toolbox");
var strChild = '';
var strSet = '';
var objNavBar = false;

if(objToolbox) {
for (var i = 0; i < objToolbox.childNodes.length; ++i) {
if(objToolbox.childNodes[i].id == "nav-bar" && objToolbox.childNodes[i].getAttribute("customizable") == "true") {
objNavBar = objToolbox.childNodes[i];
break;
}
}
}

if(objNavBar) {
for (var i = 0; i < objNavBar.childNodes.length; i++) {
if(objNavBar.childNodes[i].id == "urlbar-container") {
if(!bolWongPostButtonExists) {
strSet += "uusharedemo1-toolbar-button,"
}
}
strSet += objNavBar.childNodes[i].id + ",";
}

strSet = strSet.substring(0, strSet.length-1);
objNavBar.currentSet = strSet;
objNavBar.setAttribute("currentset", strSet);
objNavBar.ownerDocument.persist(objNavBar.id, "currentset");
BrowserToolboxCustomizeDone(true);
}

strSet = strSet.substring(0, strSet.length-1);
toolbar.currentSet = strSet;

if(toolbar.setAttribute) {
toolbar.setAttribute("currentset", strSet);
}

toolboxDocument.persist(toolbar.id, "currentset");
BrowserToolboxCustomizeDone(true);
}

this.setUBKCharPref('version', this.strVersion);

}//end if first load
}
}

window.addEventListener("load", function() { uusharedemo1.onLoadEvent(); }, false);


为了简单起见,这段代码并没有整合到示例的压缩包当中,如果想查看整合后的结果,可以参考uushare书签组件的源代码:http://www.uushare.com/help/bookmark/toolbar_firefox。

本文完。