你的下一个Web应用程序是Google Gadget吗?
by Aiessandro Lacava
翻 译:郭世龙
学习怎样驾驭Google Gadget API使你的Web应用程序更可行。作为例子,我们来看看怎样打造一个实用的gadget,它能够取回并显示DevX的feeds。
自从AJAX首次出现以来,对JavaScriot感兴趣的开发者数量快速增加——用JavaScript打造功用的工具集也迅速增长。Google Gadget 是JavaSctript开发相关的最新工具集之一。许多网站已经提供了用Google工具打造的gadget,你也可以。这篇文章就是想你展示怎样开发一个可以取回DevX RSS feed并显示给用户的Google Gadget。
Google Gadget 剖析
开发一个Google Gadget(简单的gadget从现在开始)确实是一个简单活。毕竟,一个gadget由XML、HTML、JavaScript,可选的有CSS。其作用如下:
XML描述gadget的结构;
HTML和CSS提供了表示层;
JavaScript提供了gadget的逻辑层。
下面的XML展示了基本的gadget结构:
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="A title" />
<Content type="html">
<![CDATA[
<!-- Here the CSS, JavaScript and HTML code -->
]]>
</Content>
</Module>
这是XML元素的breakdown。根元素是Module。ModulePrefs元素持有关于gadget的信息(标题、高度等等)和作者的信息——稍后作更多介绍。Content元素一般包含“实际”的内容——
CSS、HTML和JavaScript代码。像你稍后看到的,有其他元素需要考虑,但是这些是你会在你开发的每个gadeget使用的基本元素。
一个“Hello World” Gadget
开始学习一个新的编程语言或技术最简单的方法是猛啃简单的实例。下面是一个在框框里打印出传统的“Hello,World”消息的gadget的代码。
<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="Hello world example" />
<Content type="html">
<![CDATA[
<div id="content" style="color: red;">
hello, world!
</div>
]]>
</Content>
</Module>
用hello-world.xml作为保存前面的XML文件。现在你还不会用这文件——下一部分我将向你介绍怎样配置它——但是现在来看一下图1,它展示了运行的HelloWorld gadget的样子。
图1
看过了上面的例子,你可能注意到:
你在XML文件中完整的定义了一个gadget.
<Module>标签指示了这个XML文件包含了一个gadget.
用ModulePrefs属性你指明了这个gadget的标题.
<Content type="html">行指示gadget的内容类型是HTML(它可能也包含CSS和/或JavaScript代码)。也有其他的内容类型但是这一个最具柔性和通用性。你可以找到关于内容类型的更多信息 。
CDATA部分包含HTML(可选的CSS和JavaScript)代码,这些代码用来提交和激活gadget。你不必使用CSS,你可以在你的HTML用简单的内联类型属性,像前面指明颜色(红色)的实例代码。
用standard<style>标签包含CSS代码块,例如:
<style type="text/css">
.your-class
{
color: red;
}
</style>
你可以像在标准HTML页中一样在<script>包括JavaScript代码:
<script type="text/css">
function init()
{
alert("hello, world");
}
</script>
稍后你将看到关于开发DevX RSS阅读器gadget中更多CSS和JavaScript的内容。
Gadget部署
当然,开发了gadget之后你要部署它。例如,为了运行“hello,world”gadget你需要一下步骤:
1.上传gadget到你的web服务器。
2.去 http://www.google.com/ig。
3.为了加入gadget你必须有一个个性化主页。如果你还没有,你必须通过点击“开始(get started)”链接来创建一个。然后用已存在的google帐户登录,或创建一个新的帐户。
4.在有了个性化首页之后,你可以通过点击在右上角的“增加内容(add stuff)”链接来增加gadget。这将转入到内容目录(content directory)。你可以用内容目录搜寻gadget并将它们加入到你的主页。
5.点击“通过URL增加(Add by URL)”链接(临近“搜索主页内容(Search Homepage Content)”按钮。
6.在“通过URL增加(Add by URL)”域中输入URL(你的服务器或我的)获得hello-world.xml gadget,并且点击"增加(Add)"按钮。
7.点击“返回主页(Back to homepage)”链接,在你的主页上会看到新的gadget。
作者提示:我在我的web服务器上上传了这篇文章的实例,即使你没有自己的服务器你也可试试它们。你可以在http://www.alessandrolacava.com/google/gadgets/路径下找到所有的实例。例如,与“hello,world”相关的URL是http://www.alessandrolacava.com/google/gadgets/hello-world.xml。
你也可以在Google 内容目录发布你的gadget(publish your gadgets to the Google Content Directory)。那个链接也讨论了怎样发布你的gadget到另一个目标,像你自己的web网站通过聚合。
既然你已经看到了怎样写和部署一个简单的gadget,你准备要继续更有趣的内容了。
用户偏好
一些gadget需要给用户一个提供用户指定信息的方式。例如,一个RSS—feed 阅读器gadget可能要求用户提供提取的条目数或用户感兴趣的feed的URL。XML文件中的用户偏好部分描述用户输入域,但gadget运行时,这个域变成了用户接口控制。为了在你的gadget中包含用户偏好,你需要包含<UserPref></UserPref>部分到你的XML文件中。返回到HelloWorld例子,你可能让用户通过UserPref元素指定这个gadget的标题:
<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="__UP_title__" />
<UserPref name="title" display_name="Gadget Title"
default_value="Hello world example"
datatype="string" />
<Content type="html">
<![CDATA[
<div id="content" style="color: red;">
hello, world!
</div>
]]>
</Content>
</Module>
图2显示了前面的用户偏好怎样呈现(rendered)——作位一个简单的文本框。
前面的实例中UserPref元素属性有如下意义:
name是偏好(preference)的名字。就是通过名字你能在你的代码中引用这个偏好。这是唯一的必须属性。
display_name是除了呈现(rendered)对象——文本框、选择框等等之外,被显示色标签。
default_value是默认情况下选择的值。
datatype是这个用户偏好的数据类型。选项是string、bool、enum、hidden、list或location。如果你没有指定datatype值,默认的是string。偏好的提交依赖这个属性。例如,一个dedataype定义为bool的偏好会呈现为一个选择框。稍后你会看到更多关于datatype的内容。
这些属性是最常用的的一些,除此之外还有还有其他的属性你可能在你的gadget需要的。参考 官方文档(official documentation)查找更多关于这个和其他的关于gadget API话题。
注意前面的代码是怎样指向用户偏好的。它使用哪个了双下划线,其后是“UP”,它指明的“用户偏好(User Prefenrence)”,另一个下划线,偏好名,用另一个双下划线结束:
__UP_prefName__
要更改的唯一的事情是prefName,它必须与你在定义偏好的时候提供给name属性的值匹配。这一个变量被称作“替换变量(substitution variable)”。例如,你可以使用__UP_title__引用title偏好。接下来你会看到怎样在JavaScript段中程序性的应用用户偏好。
核心JavaScript库
使用JavaScript管理gadget逻辑。使用下面的模板在你的gadget中包含JavaScript代码:
<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="Hello world example" />
<UserPref name="title" display_name="Gadget Title"
default_value=" Hello world example"
datatype="string" />
<Content type="html">
<![CDATA[
<script type="text/javascript">
// Your JavaScript code here...
</script>
]]>
</Content>
</Module>
你将会经常使用哪个Google Gadget API 核心JavaScript库(Google Gadget API core JavaScript library)。在这个库中最重要的功能可能是_IG_RegisterOnloadHandler,它是事件句柄函数,当gadget加载时会被调用调用。它只有一个参数——函数,页面加载时它会被调用。下面是一个实例:
<Content type="html">
<![CDATA[
<script type="text/javascript">
function init()
{
alert("hello, world!");
}
// Call the init function on page load
_IG_RegisterOnloadHandler(init);
</script>
]]>
</Content>
页面一加载时,这个gadget用 _IG_RegisterOnloadHandler调用init函数显示“hello,world”。
其他有用的Google Gadget API是_IG_Prefs类,你可以用它程序性的提取用户偏好:
var prefs = new _IG_Prefs();
var title = prefs.getString("title");
alert(title);
前面的代码提取用户偏好的“title”并用JavaScript alert方法显示。另外getstring,你可以分别用getInt和getBool提取整型和布尔型用户偏好。你也可以像提取一样设置偏好,例如:
var prefs = new _IG_Prefs();
prefs.set("title", "The New Title Here");
使用设置器方法,你需要在你的gadget中包含setprefs库。使用Require XML元素——ModulePrefs的子元素来实现,例如:
<ModulePrefs title="The title">
<Require feature="setprefs"/>
</ModulePrefs>
你可以发现还有许多其他有用的JavaScript库。他们都在名为“Feature-Specific JavaScript Libraries”的库里。我不再在这篇文章中更多的涉及这个话题和其他具体特性的JavaScript库了,因为那需要专门的文章来讨论。
但是,我将简单的讨论核心JavaScript库中一些有用的特性。例如,如果你想要在URL中提取内容作为文本,HTML或JSON(关于JSON更多的内容),你可以用如下使用_IG_FetchContent函数:
<Content type="html">
<![CDATA[
<script type="text/javascript">
function init()
{
_IG_FetchContent(
"http://www.some-content.to-display.com",
callbackFunc);
}
function callbackFunc(responseText)
{
_gel("mainContainer").innerHTML = responseText;
}
// Call the init function on page load
_IG_RegisterOnloadHandler(init);
</script>
<div id="mainContainer"></div>
]]>
</Content>
你可能注意到了 _IG_FetchContent需要两个参数。第一个是去取回内容的URL。第二个是当内容取回时调用的回调函数。这是必要的,因为_IG_FetchContent是异步的,因此调用之后立即返回。在前面的实例中,回调函数在mainContainer div标签内显示HTML代码,这些HTML代码提取自那个假想的URL.
当然,你可以随意地按照你喜欢的方式处理提取的内容。例如,无论gadget在何时候加载,你可以使用中方式显示一个随机的有趣的引语。注意,另一个核函数 _gel的使用,它只是一个"get-element"的快捷方式,它封装了关于冗长的 document.getElementById JavaScript 函数。还有其他的有用的封装函数。稍后你将会看到。其他的你可以点击查看Official API Reference.
另外, _IG_FetchContent,这个核API提供了两个其他用作同样目的的函数:
_IG_FetchXmlContent(URL, func):取回指定的URL中的XML内容。参数指定的函数。像 _IG_FetchContent(),它是异步的
_IG_FetchFeedAsJSON(URL, func, num_entries, get_summaries): 异步方式取回指定的URL中的RSS/Atom feed并且将它作为一个JSON对象返回。当它准备好时,调用func。通
过num_entries取回指定的feed实体的数目(默认是3,可能值是1-100),可选的是为每个实体取回概括(summaries),这取决于get_summaries的值——默认的是false。
我使用了 _IG_FetchFeedAsJSON方法开发了DevX RSS Feed阅读器gadget。
打造一个DevX RSS Feed阅读器Gadget
列表1包括了打造DevX feed阅读器的完整代码。尽管乍看,可能看起来像是一堆代码,但是当你考虑到表示和业务逻辑时那就不是只是这样了。
如果你没有服务器,你可以用URLhttp://www.alessandrolacava.com/google/gadgets/devx-feeds.xml来测试这个gadget。
尽管列表1是你迄今为止所见到的相当大的一个实例,但是它由你已经看到过那些元素构成,从Module根元素开始,它包含了下面的子元素:
ModulePrefs
UserPref 元素集
Content元素
但是一些你尚未见过的方面值得解释一下。下面是ModulePrefs元素:
<ModulePrefs title="__UP_title__"
directory_title="DevX Feeds"
description="Aggregate 1-10 entries of DevX Feeds.
You can choose the feed to read by editing settings"
author="Alessandro Lacava"
author_affiliation="DevX"
author_location="Milan, ITALY"
height="250"
scrolling="true"
singleton="false"
author_link="http://www.alessandrolacava.com">
</ModulePrefs>
像你看到的,ModulePrefs元素包含一般关于gadget信息——作者信息、gadget的高度等等。因为内容可能超出高度,gadget通过scrolling="true"属性支持卷屏。注意我怎样写我的e-mail地
址:[email protected]
我这样做是因为垃圾邮件,Gmail丢弃+号后面的内容。
我前面告诉过你你可以为UserPref部分指定其他的数据类型。控件对偏好的呈现强烈的取决与你选择的数据类型。如果你没有指定数据类型,默认的是string,它呈现位一个简单的文本框。title偏好使用了默认呈现:
<UserPref name="title"
display_name="Gadget Title"
default_value="DevX Feeds"/>
图3展现了这个样例gadget的title偏好和其他偏好项。
图3
DevX gadget使用了bool、enum和默认的string偏好类型。像你从图3中看到的一样,gadget呈现bool型为选择框,而enum则呈现位复选框(下拉列表)。例如,在这feedUrl枚举偏好定义了让用户选择哪几个DevX feeds被取回。
<UserPref name="feedUrl"
display_name="Feed" datatype="enum"
default_value="http://services.devx.com/outgoing/devxfeed.xml">
<EnumValue value=http://services.devx.com/outgoing/devxfeed.xml
display_value="Latest Articles"/>
<EnumValue value=http://services.devx.com/outgoing/javafeed.xml
display_value="Java"/>
...
</UserPref>
注意你可通过UserPref元素的display_value属性指示默认值,显示值可以不同于这个条目的值(value)。你可以使用display_value属性指明一个不同的显示值。如果你没有指明具体的显示值,控件则显示value属性——这种情况下,对于终端用户来说不方便。为了完整性,下面是更详细的在DevX gadget中使用的bool偏好中的一个的考察:
<UserPref name="newPageTarget"
display_name="Open Link In New Page"
datatype="bool"
default_value="true"/>
前面的偏好定义让用户选择是否在同一个页面——这里是在Google 主页——或是不同的页面打开feed链接。我选择true作为这个偏好的默认值,意思时说选择框默认情况下是被勾选的并且所有
的feed链结将在新的浏览器实例中加载。
像你可能猜到的那样,一个gadget最重要的部分放在内容元素中,这就是你为gadget应用程序定义表示层(CSS和HTML)和业务逻辑(JavaScript)的地方。
我没有深入的探究CSS部分,因为像其他CSS一样它只是样式。HTML部分限制在下面的代码行中:
<div id="feedContainer"></div>
这个div元素的内容将包含完整的gadget体——即,DevX RSS feeds。
剩下的JavaScript部分由三个函数组成:fillGlobals, init和parseFeed加上一个前面讨论的_IG_RegisterOnloadHandler,它注册init函数作为页面加载句柄。下面是init函数的代码:
function init()
{
fillGlobals();
// Display loading message before fetching feed.
container.innerHTML =
'<div class="loadingLabel">Loading...</div>';
// Fetch feed and return it as a JSON object.
// parseFeed is the callback function.
_IG_FetchFeedAsJSON(feedUrl, parseFeed, numOfEntries);
}
函数init调用fillGlobals,它只是赋值给一些整个应用程序中使用的变量。通过提取用户偏好和获得一个指向包含feeds的feedContainer div标签的句柄来实现。下面是fillGlobals函数的代码:
function fillGlobals()
{
var prefs = new _IG_Prefs(__MODULE_ID__);
feedUrl = prefs.getString("feedUrl");
numOfEntries = prefs.getInt("numOfEntries");
container = _gel("feedContainer");
showFeedDate = prefs.getBool("showFeedDate");
openInNewPage = prefs.getBool("newPageTarget");
}
调用fillGlobals之后,init在主容器中显示“Loading...”消息,然后调用内嵌函数 _IG_FetchFeedAsJSON,它用来提取RSS作为一个JSON对象(关于JSON参看文章)。有三个参数传递
给 _IG_FetchFeedAsJSON,它们是:
feedUrl:取回RSS feed的URL。fillGlobals方法从用户偏好提取指定偏好的feed值。
parseFeed:这是一个回调函数,当feed被提取之后这个函数被出发。记住, _IG_FetchFeedAsJSON异步取回feed,完成之后,传递JSON对象——代表feed————给指定的回调函数,即parseFeed。
numOfEntries:提取实体的最大数。这个变量也被在用户偏好中的fillGlobals中初始化的。
这就是你需要知道的打造聪明的Google gadget的全部基本信息。图4显示了在我的Google主页上的完整gadget截屏。
我不会深入探究parseFeed函数的细节,因为它只是基本的JavaScript代码,用来循环提取feed实体并把它们加入到gadget的主容器中(feedContainer div)。唯一你需要更多了解的是传递
给parseFeed的JSON对象的结构。更多的信息请浏览官方文档(official documentation)。
关于安全
最后,我希望多几句话他一下安全性。事实上,你可能想知道:“是什么阻止我的gadget访问属于页面上别的gadget的数据?”
从gadget,它的类型是HTML——通过<Content type="html">行声明——你不能访问其他gadget的数据。原因是每一个HTML类型的gadget被提交给iframe。下面是来自Google Gadget开发守
则(Development Fundamentals)的一段话:
“Gadget内容被包装在一个iframe中。一个iframe不尽准确的说是一个运行在父业面之上的分离页面。iframe不知道并且没有能力与父页面交互。这样的独立性帮助保护用户免于遭受尝试的恶意gadget的
伤害,例如,盗取或修改cookies。但是,iframe确实是通过拒绝gadget之间或去其他页面组件的交互施加了一定的限制。”
你可以通过声明你的gadget位一个HTML内联(HTML-inline)gadget来逾越这些限制,通过改变它的content type设置成<Content type="html-inline">。当然,这各gadget的类型有其他的限制;例如,你不能用有其他的Google属性的HTML内联的gadget,并且内联的HTMLgadget不能被在内容目录中。内联的也也有其他主要的缺点,但它们超出了本文讨论的范围。关于gadget更多的信息,我强烈的建议你下载学习相关的文档(related documentation)。
源码
Original Text