最近想在一个小应用中采用浏览器端的xinclude。找了一下,居然没有找到现成的实现。所以就打算自己写一个。
完整的xinclude实现也需要用到一些其它技术。最基本的就是xml base。
在html中你的链接如果不是absolute url形式,就需要相对于文档位置或者文档中用base元素定义的base url来resolve。
xml base和html的base有点类似,但是更加灵活,通过xml:base属性而不是单独一个属性来设置,所以可以有层次。例如:
<?xml version="1.0"?> <doc xml:base="http://example.org/today/" xmlns:xlink="http://www.w3.org/1999/xlink"> <head> <title>Virtual Library</title> </head> <body> <paragraph>See <link xlink:type="simple" xlink:href="new.xml">what's new</link>!</paragraph> <paragraph>Check out the hot picks of the day!</paragraph> <olist xml:base="/hotpicks/"> <item> <link xlink:type="simple" xlink:href="pick1.xml">Hot Pick #1</link> </item> <item> <link xlink:type="simple" xlink:href="pick2.xml">Hot Pick #2</link> </item> <item> <link xlink:type="simple" xlink:href="pick3.xml">Hot Pick #3</link> </item> </olist> </body> </doc>
这个示例代码抄自于xml base规范。其中的xlink解析后如下:
* "what's new" resolves to the URI "http://example.org/today/new.xml"
* "Hot Pick #1" resolves to the URI "http://example.org/hotpicks/pick1.xml"
* "Hot Pick #2" resolves to the URI "http://example.org/hotpicks/pick2.xml"
* "Hot Pick #3" resolves to the URI "http://example.org/hotpicks/pick3.xml"
原理还是一目了然的。但是要做出一个完全符合标准,并且鲁棒的实现也不是很容易的。
首先是确定我们的API。
API决定了我们如何利用xml base。
本身xml base是很简单的,你可以直接在xml文档中加入xml:base属性即可。然后就是application的问题了。比如你使用xlink,或者xinclude,或者其它属性(比如各种xml格式中常见的链接)――关键是应用是否能利用它来算出每个链接(有可能是relative URI)的最后的absolute形式。
所以API就呼之欲出了。
对于xml文档中的一个uri,其要么是在文本(text node或cdata中),要么是在属性中(实际上还有其它,如entity,PI等,我们暂时忽略)。那么我们的参数就是uri,和uri所在的node,我们希望得到的,就是最后的absolute uri。如下:
resolveURI(node, uri)
从node开始,我们可以根据xml base算出其 base URI。所谓base URI,是URI规范(RFC2369)中的术语,relative URL是根据base URI进行resolve的。xml base中得到一个node的base URI的大体算法如下:
如果这个node是text node或cdata,其base URI就是该node的parentNode,也就是一个element的base URI。
如果这个node是attr,其base URI就是该attr所属的element。
如果这个node是element,那么如果它有xml:base属性,那base URI就是xml:base属性所表达的URI――注意xml:base属性也可以是relative URI,所以它自身也要根据更上一级的base URI来resolve。如果没有xml:base属性,那其base URI就是父元素(或者最上层的document节点)的base URI。注意,有时候可能没有父节点,比如这个node刚创建出来还没有插入DOM树,或者已经被从DOM树上remove了,这个时候base URI就是null。
如果这个node是document,则其值为documentURI,通常是document的资源地址,但是有时候是null,例如你是从内存中产生的,或者从字符串parse出来的。
这个算法其实比较简单,就是递归向上找。
实际上,DOM中已经有这样一个baseURI属性了。
DOM Level 3增加了Node.baseURI属性,用来返回一个节点的baseURI。所谓baseURI,是由XML infoset所规定的一个xml属性,其计算方式就是根据xml base规范来的(你可以看到,w3c的规范是一个规范集合,互相独立但是又紧密关联),也就是我上面描述的过程。
Firefox 2.0虽然没有完全支持DOM Level 3,但是它是支持xml base规范和baseURI属性的。Safari 3也是支持的(虽然其支持有些问题,后述)。
显然,你料到了,IE 6不支持baseURI。
其它浏览器,如IE 7,Opera等,我暂时未及测试。有兴趣的同志可以测一下。
为了兼容IE 6和其它不支持baseURI属性的,我又定义了
baseURI(node)
其含义等价于DOM Level 3的 Node.baseURI。
API好了之后,我们就可以实现它了(当然其实应该先写测试用例,呵呵)。
代码明天再发。