Archive for the ‘DOM | Scripting’ Category.

下一代 JavaScript 开发方向?

更新: Mozilla Labs 新出一篇 Next Generation Javascripting 也总结了近来出现的一些有趣的东西,包括 Tamarin, SqurirrelFish, Processing.js, ContextFree.jsParchment

其中 Tamarin 和 SqurirrelFish 代表了浏览器 JavaScript 引擎性能的提高 — Tamarin 虽然现在还不明显,因为它的 JIT 还在 tracing 优化阶段,SqurirrelFish 甚至还完全没用 JIT。不过这两者正式进入市场至少要到 2009 年 Mozilla 3 平台的初步发布和 Safari 4 随 Snow Leopard 一起发布时。

Processing.js 和 ContextFree.js 则代表了 Web-based Processing 创作的新方向,ProcessingNodeBox 的成功充分说明了在简单技术上提供更有效的表达方式会给图形生成带来多大的改进,现在能在 Web 上直接实时呈现这些优美的图形,毫无疑问 Flash, Silverlight 等将更不受欢迎了。

Parchment 则是一个非常漂亮的 Z-Machine 在 Web 上的呈现,适合 Text Adventure 爱好者们。


近来,两个新出现的 JavaScript 框架得到了许多关注: SproutCoreObjective-J。表面上,它们共同的特点就是大量使用了 HTML5 特性来改善用户体验,达到类似桌面应用的效果,然而从底层分析,它们其实代表了两种对富 JavaScript Web 应用的开发方式的革命。

为什么说是“两种”呢?虽然 SproutCore 是一套几乎完全由 Apple 工程师开发的框架 — 其设计目的就是给 MobileMe 提供类似桌面应用的效果 — 但其设计思维是 Web 化的,是和现下流行的服务器端开发框架一脉相承的。从我的观点看来,SproutCore 是受 Ruby on Rails 影响严重的一套框架,其提供的工具大多用 ruby 编写,创建的目录结构都带着 Rails 的痕迹 — 甚至开发人员也是典型的 TextMate 用户。

而 Objective-J 虽然也主要是由前 Apple 工程师领导开发,可是这些工程师却是不折不扣的 Objective-C/Cocoa 狂热爱好者,他们在 280slides 表现出来的是典型的 Keynote 风格,其代码组织则是典型的 NeXTSTEP 风格。

所以,可以想见的是,SproutCore 的设计思维更容易为大众接受,而 Objective-J 很可能始终保持小众 — 甚至我都很怀疑它到底会不会发布…

Pro JavaScript Techniques 的中文版上市

相信熟悉 Web 设计的朋友已经了解,这本书是一本关于 JavaScript 的较有深度的书籍,中文版是由贤安 (realazy) 和我一起翻译的,中文译名是《精通 JavaScript》。

原书的价值,对于熟悉的朋友应该毋庸置疑,我们两人在翻译时也颇费了一番功夫,大致分工是我主要负责和语言特性相关的,贤安主要负责和 Web 应用相关的 (包括与 CSS 的配合),如果有任何意见建议,欢迎致信 projsch@gmail.com,我们会将所有的更正与改进放在勘误页面上。

用 Python 维护 iTunes Library

Mac OS X 10.5 中,由于 Scripting Bridge 的引入,用 Ruby 或 Python 程序完成原来 AppleScript 才能完成的任务变得非常简单,而因为这两门语言自身的强大,无形中,可以完成的工作也多了不少。比如我们原来可能要用 ID3Mod 这样的软件进行 iTunes Music Library 的歌曲乱码转换,现在写一段不到十行的 Python 脚本就能完成 (当然,界面没有那么方便)。

一个小例子

这里先用 Python 简单的展示一点可以完成的操作:

# 导入必要的模块
from Foundation import *
from ScriptingBridge import *

# 找到 iTunes 这个应用程序
iTunes = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")

# 打印出当前正在播放的音乐名称
print iTunes.currentTrack().name()

这段代码在 Leopard 下,既可以保存为 .py 文件,用系统自带的 python 解释器 (/usr/bin/python) 执行,也可以直接在命令行下调用 Python 解释器,查看它的输出,比如我这里是:

$ python
Python 2.5.1 (r251:54863, Oct  5 2007, 21:08:09) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type “help”, “copyright”, “credits” or “license” for more information.
>>> from Foundation import *
>>> from ScriptingBridge import *
>>> iTunes = SBApplication.applicationWithBundleIdentifier_(”com.apple.iTunes”)
>>> print iTunes.currentTrack().name()
Le Festin
>>> 

注意,执行之前最好先在 iTunes 里开始播放一首曲目。

了解更多

见识了 Scripting Bridge 的威力之后,你很自然的想知道这段代码为何可以工作,应该按什么方式来调用它,除了查询当前播放的歌曲名称以外,还有什么其他的接口可以使用。这时候,我们就需要对我们希望用脚本操纵的程序所提供的 Scripting 接口有个清晰的了解。所以可以用 sdef 这个命令获得一个程序的脚本接口定义:

$ sdef /Applications/iTunes.app
xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary><suite name="Standard Suite" code="****" description="Common terms for most applications">
...

出现了一长串的怪异信息,太混乱了,看不懂,怎么办呢?这时应该用 sdp 命令过滤一下,生成一个比较可读的格式,Objective-C 头文件:

$ sdef /Applications/iTunes.app | sdp -fh --basename iTunes
$ ls
iTunes.h
$ more iTunes.h
/*
 * iTunes.h
 */

#import <AppKit/AppKit.h>
#import <ScriptingBridge/ScriptingBridge.h>
...

这里就是一个对 iTunes 提供的脚本编程接口的详尽描述了。从这里我们其实很容易看出,原来 iTunes = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes") 获得的,是一个 iTunesApplication 对象,以这个对象为出发点,我们可以做到任何 API 允许的事情。下面就是一个例子。

#!/usr/bin/python

from Foundation import *
from ScriptingBridge import *

iTunes = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")

for track in iTunes.sources()[0].playlists()[0].tracks():
    print track.name(), track.artist()

简单的观察一下,iTunes 的脚本编程 API 组织是这样的:在 iTunesApplication 下,可以找到多个来源 (iTunesSource),来源有许多种,比如 music library,比如 CD,比如 iPod 等等,而每个来源里,又按照播放列表 (iTunesPlaylist) 来组织,而每个播放列表中,显而易见地有多个曲目 (iTunesTrack),在上面这个例子里,我们要找的第一个来源 (sources()[0]) 的第一个播放列表 (playlists()[0]),正是你的 iTunes Music Library。

值得注意的一点是,在 iTunes.h 中用 SBElementArray * 表示的数据类型,一眼可以看出它是数组,在 Python 中处理起来也很简单,直接当成 list 类型遍历即可。

修改数据

那么,查询我们知道了,该怎么把信息写回去,也就是说,修改原来 Music Library 的内容呢?

先来看看 iTunes.h 中的出现的一段:

@interface iTunesTrack : iTunesItem

- (SBElementArray *) artworks;

@property (copy) NSString *album;
@property (copy) NSString *albumArtist;
@property NSInteger albumRating;
@property (readonly) iTunesERtK albumRatingKind;

这里描述的是一个曲目 (它继承了 iTunesItem 类型,所以也继承了它的 name 等属性),@property 这种写法,是 Objective-C 2.0 中新出现的,也很好理解,(copy) 的意思是你的赋值会被复制一份保存,(readonly) 当然是只读的,而 NSInteger 这种简单的变量也是可以写的。

那么,查询我们知道了,比如这里有个 album 属性,我们就去调用 track 的 album() 方法,但究竟如何赋值呢?还是按照 Cocoa 的 Key-value coding 的老规矩,改成 setAlbum(<参数>),比如:

>>> iTunes.sources()[0].playlists()[0].tracks()[0].name()
u’Le Festin - Performed by Camille’
>>> iTunes.sources()[0].playlists()[0].tracks()[0].setName_(u’Le Festin’)

这里把一首原来叫做 Le Festin - Performed by Camille 的歌曲名称修正为 Le Festin,回到 iTunes 里一看,果然改了。

系统自带与 macports

在 Mac OS X 10.4 中,由于系统自带的 Python 是老旧的 2.3 版本,所以对 Python 爱好者来说,都往往不得不自己编译一份或者使用 fink/macports 中提供的 Python 2.4/2.5 版本。而在 Mac OS X 10.5 中,终于把自带的 Python 升级到了最新的 2.5.1 版本,对爱好者来说无疑是省事了,可惜这个自带的版本一个令中文用户郁闷的 bug 是:在提示符模式下无法用输入法输入任何中文字符 (相信其他 CJK 字符也是如此),比如我用输入法打一段:你好 hello 世界 world,等我一确认,Python 解释器只收到了 hello world,中文字符被自动滤掉了!然而,通过 macports 安装的 Python 此功能却是正常的。

这使得我们这里如果要修改中文的歌曲、作家、专辑名等等都变得很不方便,怎么办呢?

系统自带的 Python 的特别之处,其实在于它自带了许多 Apple 添加的模块,比如我们在代码最开始导入的 FoundationScriptingBridge 模块,这些模块有些不是开源的,有些则还没来得及把代码公开,所以我们机器上只安装有编译好的 Python 模块,然而幸好这些模块是 2.5 版本的,只要我们用 macports 也安装一个 2.5 版本,把系统自带模块的路径给加进去,就能找到并使用它们了。让我们还是用开头那个例子做个简单的示范:

#!/opt/local/bin/python2.5

import sys

sys.path.append('/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python')
sys.path.append('/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python/PyObjC')

from Foundation import *
from ScriptingBridge import *

iTunes = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")

print iTunes.currentTrack().name()

有了上面这些介绍,相信要把这几个工具组合在一起成为一套 iTunes Music Library 的管理利器也不是难事,各位不妨发挥想象力,我就不多说了。

现有的 JavaScript 实现概览

严格来说,JavaScript 其实指的是 Netscape/Mozilla 对 ECMAScript 标准的实现,但考虑到习惯因素,这里就不咬文嚼字了。

目前仍然在开发中的 JavaScript (ECMAScript) 开放源代码的实现包括:

JavaScriptCore

Apple 开发的 JavaScript 引擎,以 Mac OS X Framework 的形式提供,与 WebCore 一同结合而成 WebKit Framework。JavaScript 是基于 KDE 计划的 KJS 库和 PCRE 正则表达式库开发的。

特点:

  • 强大的垃圾回收器
  • 使用 C++ 开发
  • 基于标准 C/C++ 库和 ICU (IBM 的 Unicode 库)
  • Mac OS X 程序调用它比较方便
  • 采用创建语法树并执行的形式,而不是生成 bytecode 再执行
  • 提供 C, Java (JNI), Objective-C, Qt 的 binding
  • 跨平台,可在 win32 下使用

网站:

KDE 提供了 kjsembed 库,也是基于 KJS 的,但依赖 Qt (和 KDE)。

SEE

是 Simple ECMAScript Engine 的缩写,提供了 ECMAScript 的解析器和运行时环境。提供到 JavaScript 1.5 (ECMAScript 第 3 版) 的兼容性。

特点:

  • 不直接提供 DOM,但 API 设计时是考虑到了支持 DOM 绑定的
  • 关注正确性与可移植性

网站:

  • SEE
  • 提供代码的 snapshot, Subversion, 邮件列表和 Bugzilla

文档:

Using SEE

DMDScript

Digital Mars 的 ECMAScript 实现,作者是 D 语言的发明者。

特点:

  • 声称要比其他实现都快
  • 有 D 语言实现也有 C++ 语言实现
  • 混合 License (开源软件可以使用 GPL)
  • 加上了 Microsoft Jscript 扩展

网站:

SpiderMonkey

Mozilla 的 JavaScript 参考实现,支持到 JavaScript 1.7。

特点:

  • 使用者数量众多,对 JavaScript 和语言扩展支持最快
  • 专门为单独 (standalone) 编译考虑,不需要依赖任何其他的 mozilla 软件/库
  • 要保证线程安全则需要 NSPR 库
  • 向 Adobe 贡献的 tamarin 引擎转移的步骤进行很慢

网站:

Canvas

Canvas 这个有趣的东西,随着 Firefox 1.5 的发布,终于可以亲自来尝试一下了。

Dojo

Dojo Toolkit 是一套方便你构建 JavaScript 应用的工具集。

Standards-compliant WYSIWYG HTML editor

The Man in Blue 那儿出现了一个新的所见即所得 HTML 编辑器:widgEditor

尽管已经有很多类似的东西,——其中最著名的一个大概是 HTMLArea——widgEditor 的特点在于灵便,简单,只需要在 head 中加入一行包含 JavaScript 的代码,然后给你希望使用这个编辑器的那个 textarea 添加一个 class 即可。同时还可以通过 CSS 对界面的外观进行定制。

另外它的代码是完全面向对象的,易于扩展,这也是为什么本身保持足够简单,只提供基本的 CMS 需要的编辑功能的原因。

你可以在这里试试

Recent News

临近半个学期一度的考期,blog 的频率不得不降下来,好几天没上网,确实也出现了不少新的东西。

定位总是一个热门话题,近来,Tommy 的两篇文章:Relatively AbsoluteFloat Layouts 相信能给你一点启发。

Marek 的 CSS Workshop,介绍关于 CSS 的有趣实践,比如纯粹用 CSS 做一个 W3C 的验证标志,给水平线 <hr /> 设定样式等。

Roger 的 Quotations and citations: quoting text 是关于如何正确地在 (X)HTML 中引用别人的文本的一篇解释性文章,而他的 The alt and title attributes 则是就 alttitle 属性的正确使用这个老话题而写的。Dave Shea 说得不错,Roger 完全可以就如何正确使用标记写本书了。

最近一期 ALA 的 Invasion of the Body Switchers 是先前流行的 Paul Sowden 版本样式表切换器的一个更新版本,更关心与亲和力方面的应用。

来自 Mozilla 方面的消息,则是 ThunderbirdNvu 分别发布了 1.0 版本和 0.6 版本,不过似乎 nvu 还没有成为 mozilla 的 HTML 编辑器的下一代版本,或许它更愿意作为一个独立软件提供?

时间有限,不得不在这里打住。

Flash Embedding

deconcept 有一篇关于支持 Web 标准的 Flash 检测及嵌入的文章。这种新方法支持:

  • 能够检测用户代理的 Flash Player 版本,显示相应的内容
  • 可以显示几种替换内容:纯文本 (HTML),重定向、或者是图片。
  • 应该符合 XHTML 1.0 transitional 及以上
  • 支持所有 Flash 嵌入的参数和变量 (通过 Flashvars 参数)。
  • 非技术性的设计师们也容易掌握

看起来值得一试,哪位朋友尝试过的话,不妨写点意见。

Browser Test VS. Object Detection

广为使用的浏览器测试实际上是一种很不好的脚本编写方式——Peter-Paul Koch 告诉我们

所谓“浏览器测试”,是指在 JavaScript 脚本中通过 navigator.userAgent 来判断当前用户使用的是什么浏览器,并根据这个浏览器能够支持的情况提供相应的功能。乍一看似乎没有什么不妥,但 Peter 给出了为什么永远不要使用浏览器测试的原因:

  1. 它很有可能忽略了你没有注意到的浏览器,比如新出现的,或者特别罕见的。
  2. userAgent 这个属性在浏览器里多数是可以修改的,用户可能会把它定制为一些其他的。
  3. 有些浏览器,只有在特定平台下支持某种属性,或者只在特定平台下不支持,这些情况浏览器测试都不好用。

那么检测 JavaScript 的版本如何呢?也不行,因为许多浏览器对同一个版本的支持是不一致的,比如 Netscape 4 和 IE 4 都声称支持 JavaScript 1.2,但你真的相信他们支持的是同一个 1.2 么?

对象检测才是正确的方法。要知道一个浏览器是否支持 document.images 对象,只需要:if (document.images) 即可。而要知道 window 对象是否支持 focus() 函数,if (window.focus) 就可以判断了,但千万不要写成 if (window.focus()) 哦,那可是调用那个函数了。

Peter 还给出了其他的一些判断规则。

  • 如何判断是否支持 DHTML?if (document.getElementById || document.all || document.layers)
  • 如何判断是否支持 W3C DOM?if (document.getElementById && document.createElement)

另外,Peter 的网站 QuirksMode.org 实在是一个学习 JavaScript 和 DOM 的绝佳站点,如果你希望学习 DOM/脚本编程,建议从那里起步。