在 Dashboard 中显示 GMT 时间

widget-world-clock-gmt

工作中需要知道美国旧金山时间和 GMT 时间,但 Mac 的 Dashboard 中的 World Clock Widget 没有对应 GMT 的城市。虽然 Greenwich 就在 London 边上,但 London 要加一个小时的偏移。还好,就着此文指引,自己动手加了个 GMT,如上图所示。

仅需修改此文件,先备份:

$ sudo cp /Library/Widgets/World Clock.wdgt/WorldClock.js{,.bak}

然后用下面的补丁:

233a234,236
> var GMT = [
> 	{city:'GMT', offset:0, timezone:'GMT', id:"9999999"} 
> ];
243c246,247
< 	{name:"South America", array:SouthAmerica}
---
> 	{name:"South America", array:SouthAmerica},
> 	{name:"GMT", array:GMT}

===

第一次看到 JavaScript 里面三个等号的比较运算符。竟然这么多年来从未见过,要么我寡闻,要么我术业不精。原来 === 是说除了数据相同外,数据类型也要相同。对应的还有 !== 作不等同比较。

Google Ads

目标驱动抉择

人们普遍喜欢带有圆角的设计,Apple 的产品充分体现了这一点。网页设计也不例外。Google 很多产品的简洁风格都在应用了圆角设计后儒雅很多。

至于圆角设计的实现,不一而足。最原始的是使用设计好的背景图片,带来显而易见的缺点就是不够灵活,无法自适应内容大小的变化。最轻松的是使用样式表定义,不过目前并非所有的浏览器都支持。再接下来就是下面两大类的实现技巧了。

其一,使用 Javascript 处理特定的容器对象。在 DOM 树上动态增加若干辅助的 div 然后通过 CSS 定位等技巧按像素级画出圆角的效果。优点是,保持 xHTML 的语义整洁;缺点是需要等待页面完全加载后才能渲染,页面数据大时可以见到延迟渲染的效果,圆角不够光滑,没有次像素辅助,圆角大时比较生硬。

其二,使用圆角小图片。有些配合表格,有些直接使用额外的 div。然后配合 CSS 在角落处应用定位好的背景图片。优点是无须 JavaScript 介入,所以一旦 CSS 加载完成(通常都比较快),页面上马上出效果,圆角可以较平滑;缺点是 xHTML 不再纯洁,冗余元素众多,并需要事先准备若干小图片。

如何选择?这看设计以何为目标。通常,开发人员(比如说我)倾向于第一种,符合了完美主义,优雅的行事风格。维护起来也轻松,还能顺便展示一下与时俱进的工作态度。这是以设计人员为导向。Google 怎么做的?很遗憾,Google 并没有遵循严格的 xHTML 设计,到处使用 table。当然不是滥用,而是用心良苦地用。因为它的设计导向是访问者。从访问者的角度看到的都是第一优先级,保持语义和高维护性处于次优先级。所以 Google 使用了第二种,并且作了很好的折衷和平衡。

这样的抉择思想同样适用于程序开发。

极限编程的敏捷思想,就是以快速实现最小系统为导向(这符合利益最大化的商业原则)。敏捷的目的并不是要赶最终项目的进度,而是要及时得到客户的反馈信息,以便减少后续开发走弯路的可能性。没有反馈,敏捷是没有意义的。很多项目开始的时候,客户都不清楚最终应该需要什么样子,所以我不同意这种情况下在一开始就使用测试驱动开发。只有反馈和需求明确的部分,使用 TDD 来确保质量才是有意义的。具体操作的时候,作为项目的主体,开发人员常常会不自觉地陷入某些细节纠缠不清,似乎不满足自己的美学需求就不算通过,无法继续工作。还会经常替客户考虑长远的更高更大的问题,以便将来到这一步时,现在的“优良设计”可以优雅地适应,自己可以减少很多工作量。可实际问题是,那一天会到来吗?有多远?这之前还有什么更重要的需要做?有相当一部分情况是,没有那一天,或者局势早就变得面目全非。这样说来,当初的铺垫所花费的时间和精力都是白费的。再好的代码实现,没有用处就等同于垃圾,并且搭上成本,连垃圾还不如。客户和用户不明白这些,也没必要关心这些。所以每个阶段,只需要关心和思考客户要什么就 ok 了。这么说来,很多开发人员会抱怨,那这一阶段完成后,客户要求改动,可能会牵涉到底层的数据,需要修改很多连带的东西,那我不是死定了?没错,很遗憾,开发人员在这个产业链中充当的只是生产劳动力的角色,这个角色有多辛苦没人关心,人们只关心这个项目是否好用有用,是否可以带来现金。敏捷开发的终极目标就是节约成本,扩大利润空间,赚钱是第一导向。所以生产者可能出现的弯路和复杂劳动都是在此导向下次优先级的,是可以屈就的。当然任何有人情味的人都不希望看到开发人员如此受苦,但没有办法,将来的需求就是风险,要规避风险必须有代价。敏捷开发就是用来拯救开发者的 -- 必要的开发不能少,但绝不浪费多余的。在每一个阶段使用最小系统实现,在当前阶段就确保了最低陈本,风险最低。如果各种原因导致项目流产也就不至于损失更多。下一个阶段时,重新评估,并在此阶段下最小实现。结合对减少每个阶段的时间长度的控制,通过得到的反馈来调整阶段目标,就可以时刻保持风险最小化。因为即便哪一天有重大的需求变更而导致大量的代码重写,也可以向客户有理有据地要求额外的报酬,开发者也能相对平衡些。当然在开始时没有全面完整地了解需求则另当别论。

总的来说,就是目标驱动抉择。目标是什么?优先级就该怎么分。如果要赚大钱,就去做生意;如果要在技术领域成为专家,就可以不计成本死命钻研;如果只是糊口,就去找一份符合自己兴趣的工作,完成老板交付的任务。不要考虑任务是否合理和公平,只管去敏捷实现,这才是你的价值。如果你坚决认为任务的荒谬或者老板的不可理喻,那就到了该去考虑换一份工作的时间了。

Google 希望用户开心,所以宁可代码稍显丑陋。敏捷开发希望及早收钱,所以宁可将来再投入更多。

IE 下 JavaScript 调试

本来在 FireFox 下用 FireBug 是一件很美好的事情。可绝大多数的项目都还需要在 IE 下面调试。开始的结果总是惨不忍睹。除了 CSS 要加很多 trick 之外,如果 JS 报错,就麻烦了。IE 只是很笼统地给出一些错误信息,给出一个内部的行号,字符位置和抛出的异常名称。很难在此基础上发现并解决实际的问题。就算用了 FireBug 的 Lite 版本,也好不到哪里去。只能靠经验摸索,恼人得很。

今看到 gugod 介绍 Jash 貌似不错的样子,有机会在接下来的工作中用用看。

jQuery.js 和 Prototype.js

实在忍不住要写点什么了。我被这两个 JavaScript 搞死了。它们都让我又爱又恨。

Prototype.js 出来得早,随着 Rails 的开发而逐渐扩大影响力,成为 Rails 框架下御用 JS 框架。所以秉承了 Ruby 语言的一系列概念,非常清晰地用组织良好的类,对象扩展的方式,增加了各种常用的 handy 函数或者方法。所以叫做原型么。学习曲线低,顺手拈来。

不过这种循规蹈矩的做法很快让书写它们的人感到厌倦。很多操作都要加上类名,繁复、生硬、罗嗦。于是 jQuery.js 得以进化为,用最自然的,类似伪代码的形式,完成相同的功能,并具有更好的可读性。比如:

$(".rborder").corner("5px").parent().css('padding', '1px').corner("round 5px");

一气呵成,痛快极了。想象不出还有什么写法可以比这个更简洁达意。而 jQuery 的哲学就是:写最少的代码,做最多的事情。

而这种写法,像极了 shell 下面使用管道符串联起来的一系列命令,简单、优雅、强大、灵活。喜欢用 Perl 写代码的一个原因,也是因为其中的 map 和 grep 组合所构造出来的类似效果,清晰、流畅、自然。所以 jQuery 是很讨开发者的欢心的,用起来舒服。

用起来舒服,这件事本身没什么伟大的,可一舒服,写程序的人就兴奋起来了,就会很高兴地去发明些什么,这种内在激发的驱动力一爆发,就造成了现在 jQuery 插件遍天下的格局。这和 Rails 本身敏捷而富有艺术性的气质所带来的全球影响力的爆发是一致的。

不过这带来了一个问题。插件满天飞,怎么保证质量和兼容?自由带来的多样性是件好事,太多了就分不清楚谁是谁了。虽然 jQuery 提供了统一的 svn 仓库,不过各式的插件的文档,测试,演示等等,各自为政,颇为凌乱。或许需要一个类似 CPAN 一样的统一管理介质?

jQuery 对 DOM 对象的操作很容易--主要是很舒服,可要处理数据的时候,就不那么舒服了。我总是对 prototype.js 中的 inspect(), join() 念念不忘,可 jQuery 没有,好不容易看到一个 Array 的插件,提供的功能也是捉襟见肘。所以我目前的结论是,对页面元素的控制上,用 jQuery,对数据的处理上,用 Prototype.js 。

还好,jQuery 只是在自己的命名空间里面东奔西跑,按照标准的方式,两者可以共存。不过 jQuery 为了 write less,默认情况下,将自己的类捆绑到 $ 函数上,于是,这一强大的内激效应让一些插件的作者忘掉了这只是一个快捷方式,只要在插件内部也使用这种写法,就会和 prototype 冲突。比如说:ThickBox 3.1

开会去了…

prototype.js 和 Ruby

- 前天买了本《Ruby Programming》,正儿八经开始学习 Ruby 了。
- 这两天在上班路上看,看完了前五章。
- 突然发现 prototype.js 像极了 Ruby 的一些写法。each, succ, inject 等等。包括从 Enumerable 类 Mixin 过来的 Array 也是如此。
- 万物归一的感觉, 美伦美奂。

Javascript 实现 ORM

最近又开始看以前订阅的 RSS,享受了很多发现的乐趣。而今天让我诧异的是这个,Simon Cozens 介绍了他自己写的 class-dbi.js

奇怪,怎么 JavaScript 也可以调用数据库?顺藤摸瓜,看这里, Mozilla 为了让扩展可以使用持续数据,提供这样的一个 API ,后端使用 SQLite 存储数据。目前这个接口尚未定型,Simon 却已经自己使用类似 Class::DBI 的语法写了 JavaScript 的类,所以可以先从接口定义数据库文件和相关的连接:

var file = Components.classes["@mozilla.org/file/directory_service;1"]
                     .getService(Components.interfaces.nsIProperties)
                     .get("ProfD", Components.interfaces.nsIFile);
file.append("asdf.sqlite");
var storageService = Components.classes["@mozilla.org/storage/service;1"]
                     .getService(Components.interfaces.mozIStorageService);
var mDBConn = storageService.openDatabase(file);

然后定义数据和类的映射关系:

function Mail () {}; databaseclass(Mail, "mail");
function Attachment () {}; databaseclass(Attachment, "attachment");
function Entity () {}; databaseclass(Entity, "entity");

于是,就可以用这样的语法操作数据了:

var mail = Mail.retrieve(msgid);
if (!mail) mail = Mail.create({ message_id: msgid, message: body });
// ...
mail.last_read((new Date).toUTCString());
// ...
mail.drop();

酷毙了。

keyCode 和 charCode

为了提取垃圾邮件中的中文字串,并自动转化为正则表达式需要的形式,我写了一个 Web 应用程序。只要鼠标选中字串,然后按下某个键,即可通过 Ajax 提交到后端转化和测试。自然我用的是 Catalyst 和 prototype.js。

原本我设定的是 Insert 键关联提交操作。而现在,我需要在提交的同时指定被选字串的类型,比如说邮件地址用 e 键,或者手机号码用 m 键,等等。这在当时开发的时候就发现, FireFox 下, prototype.js 只能返回功能键的 KeyCode,如果是字母,则一律是 0。我自然是不愿意因此屈就去用 IE 的,更何况我现在 Ubuntu 下面也没有 IE。而我有不想用 Mochikit,虽然实验过它没问题。我怀疑是 prototype.js 的问题,不过本着 Agile 开发的精神,没有深究下去。

这次,我决定解决这个问题。当然还是看了番源代码,做了些测试,未果。于是 Google,仔细找了下,发现枫岩的这篇文章,点醒我梦中人。简单的说,在 FireFox 中,KeyCode 返回 0 就用 charCode。于是我猜想 Mochikit 也是这样实现的:在 Signal.js 里第 135 行起定义的 key 函数非但给出了健壮的封装,还有一堆注解。

澳大利亚 vs 日本

我不太看球的。不过偶尔也会看看。这次世界杯,和朋友一起看了第一场的上半场,看到了三粒进球。今天澳大利亚和日本的比赛,洗完澡从第11分钟开始看,到上半场结束,澳大利亚很郁闷的输了一个球,矮个子日本人横着冲了一下守门员,进得有些侥幸。然后正好《透明人II》CD2 下载好了,于是我继续去看电影。等看完了,打开电视机,呵呵,他们还在踢。刚坐下来不到一分钟,就进了一球,我心想,tmd 我运气真好。然后坐下来看,没想到乱人堆里射门,正好穿过两个裤裆,直接又进一个。然后突然想起来看看 google 首页上世界杯实时比赛的分数是否及时更新,结果报告 javascript 出错,呵呵,放弃。再出来看球,没多久,又进了一个,老栾,本来 0:1 落后,我看好电影出来看他们球赛了,就在短短的最后十多分钟内连进三球。好像澳大利亚的运气很好,我的运气也很好。按理说我是个很倒霉的人(很多地方都可以印证),难得有这样的运气,也就莫名的开心一小阵。

好了,在磨蹭磨蹭,拉灯,睡觉!

Comet

这是一个新名词,类同于 Ajax ,Comet 用于指代一种技术:服务器段保持长连接,任何时候都可以向客户端发送数据,反过来客户端也可以任何时候发送数据给服务端。典型的应用比如基于 web 的聊天工具,或者实时的股票行情现实。已经有一些商业的产品,比如 lightstreamer ,它的几个演示都非常专业。dojo 的作者 Alex 在他的 blog 上提到了这些想法,并打算用简单的术语称呼这种技术实现,并给出了和 Ajax 原理类同的原理说明图:

让人兴奋的是,他还提到 dojo 早就已经考虑到了 Comet 的实现,其中的 dojo.io.bind() 就是由此而生,并声称在未来的几周内提供演示。