四月 2007


随心所欲04 Apr 2007 09:46 pm

        冲动了~~一夜没睡~~手写ajax rss2.0 reader
        最近比较容易冲动,这不昨天一冲动,研究了半天,晚上没睡觉,用js写了ajax的rss2的未化妆版的阅读器。本来是因为想找一个合适的yahoo widget用来read rss的,但是在网上找了很久都不如意,nnd,于是算了自己做一个吧。
        关键还是使用ajax的过程吧,后来才发现yahoo widget他有他自己实现fetch的过程,所以最后写出来的代码,还必须好好改造一下才能用在yahoo widget上面。
        正当发愁之际,想看看人家的yahoo widget是怎么写的,然后打开默认的my widget文件夹,找找自己感兴趣的widget研究一下,没想到竟然被我看到了rss reader。我的天啊!我怎么一开始没看到呢。适用了一下虽然只能订阅一个rss,但是还是挺漂亮的。于是放弃了把ajax迁移过来的想法。

       总结一下用ajax写rss reader
       下面就是样图,很朴素吧,呵呵 , 非常之土了,但是功能到了,如果一天以前的就会显示为以前的源,一天之内的会显示多少分钟之前的,自己可以设定多长时间重新获取。下面写的临时更新是我让他更新的时候打印出来给我看的,没有什么实际意义。

先是html页面

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>ajax测试</title>
<link href=”a.css” rel=”stylesheet” type=”text/css”/>
<script type=”text/javascript” src=”js/ajax.js”></script>
<script type=”text/javascript” src=”js/rss2reader.js”></script>
<script type=”text/javascript” src=”js/rssClass.js”></script>
<script type=”text/javascript” src=”js/display.js”></script>
<script type=”text/javascript”>
var requestUrl = “http://dongpingyuwen.blog.sohu.com/rss”
function iniPage(){
display.iniHolder(”holder”);
display.setTime(”time”);
if(initXMLHTTPRequest()!==null){
sendRequest(requestUrl,’1′,’GET’);
}
}
</script>
<meta http-equiv=”keywords” content=”keyword1,keyword2,keyword3″>
<meta http-equiv=”description” content=”this is my page”>
<meta http-equiv=”content-type” content=”text/html; charset=UTF-8″>
</head>
<body onload=”iniPage()”>
<div id=”time”></div>
<br/>
<div id=”holder”></div>
<br/>
<input type=”hidden” name=”hd” id=”hd”/>
</body>
</html>

//然后是负责异步的ajax核心代码,一般都是大同小异

var req = null;
var console = null;
var ie = false;
var READY_STATE_UNINITIALIZED = 0;
var READY_STATE_LOADING = 1;
var READY_STATE_LOADED = 2;
var READY_STATE_INTERACTIVE = 3;
var READY_STATE_COMPLETE = 4;

function sendRequest(url,params,HttpMethod){
if(!HttpMethod){
HttpMethod = “GET”
}
req = initXMLHTTPRequest();
if(req){
req.onreadystatechange = onReadyState;
try{
req.open(HttpMethod,url,true);
}catch(error){
alert(error);
}
req.setRequestHeader(”Content-Type”,”application/x-www-form-urlencoded”);
req.send(null);
}
}
function initXMLHTTPRequest(){
var xRequest = null;
try {
ie=true;
xRequest = new ActiveXObject(”Msxml2.XMLHTTP”);
return xRequest;
} catch (othermicrosoft) {
try {
xRequest = new ActiveXObject(”Microsoft.XMLHTTP”);
return xRequest;
} catch (failed) {
ie = false;
try{
xRequest = new XMLHttpRequest();
return xRequest;
}catch(error){
alert(error);
}
}
}
}
function onReadyState(){
var ready = req.readyState;
var xml = null;
if(ready==READY_STATE_COMPLETE){
var result = document.getElementById(’result’);
if(req.status==200){
xml = req.responseXML;
rss2Reader.fetchRss(xml,10000,true);
}
else{
result.value = req.status;
}
}
else{
data = “loading…[”+ready+”]”
}
}

//下面是自定义的保存rss节点信息的一些“类”和处理方法var rssClass = {};
rssClass.getChannelInfoInstance = function(channel){
var info = new Object();
var titleNodes = channel.getElementsByTagName(”title”);
var linkNodes = channel.getElementsByTagName(”link”);
var descriptionNodes = channel.getElementsByTagName(”description”);
var pubDateNodes = channel.getElementsByTagName(”pubDate”);
var generatorNodes = channel.getElementsByTagName(”generator”);
try{
if(titleNodes.length>0){
info.title = channel.getElementsByTagName(”title”)[0].firstChild.nodeValue;
}
if(linkNodes.length>0){
info.link = channel.getElementsByTagName(”link”)[0].firstChild.nodeValue;
}
if(descriptionNodes.length>0){
info.description = channel.getElementsByTagName(”description”)[0].firstChild.nodeValue;
}
if(pubDateNodes.length>0){
info.pubDate = channel.getElementsByTagName(”pubDate”)[0].firstChild.nodeValue;
}
if(generatorNodes.length>0){
info.generator = channel.getElementsByTagName(”generator”)[0].firstChild.nodeValue;
}
return info;
}catch(error){
alert(error);
return null;
}
};
rssClass.getItemInfoInstance=function(item){
var info = new Object();
var titleNodes = item.getElementsByTagName(”title”);
var linkNodes = item.getElementsByTagName(”link”);
var descriptionNodes = item.getElementsByTagName(”description”);
var pubDateNodes = item.getElementsByTagName(”pubDate”);
var commentsNodes = item.getElementsByTagName(”comments”);
var creatorNodes = item.getElementsByTagName(”dc:creator”);
var guidNodes = item.getElementsByTagName(”guid”);
try{
if(titleNodes.length>0){
info.title = item.getElementsByTagName(”title”)[0].firstChild.nodeValue;
}
if(linkNodes.length>0){
info.link = item.getElementsByTagName(”link”)[0].firstChild.nodeValue;
}
if(descriptionNodes.length>0){
info.description = item.getElementsByTagName(”description”)[0].firstChild.nodeValue;
}
if(pubDateNodes.length>0){
info.pubDate = item.getElementsByTagName(”pubDate”)[0].firstChild.nodeValue;
}
if(commentsNodes.length>0){
info.comments = item.getElementsByTagName(”comments”)[0].firstChild.nodeValue;
}
if(pubDateNodes.length>0){
info.creator = item.getElementsByTagName(”dc:creator”)[0].firstChild.nodeValue;
}
if(commentsNodes.length>0){
info.guid = item.getElementsByTagName(”guid”)[0].firstChild.nodeValue;
}
return info;
}catch(error){
alert(error);
return null;
}
};
rssClass.getTimeInstance = function(time){
var date = new Date(time);
var instance = new Object();
if(!ie){
instance.year = date.getYear()+1900;
}
else{
instance.year = date.getYear();
}
instance.month = date.getMonth()+1;
instance.date = date.getDate();
instance.day = date.getDay();
instance.hour = date.getHours();
instance.minute = date.getMinutes();
instance.second = date.getSeconds();
instance.zone = date.getTimezoneOffset();
return instance;
};
rssClass.getOffsetMinuteDescription = function(time){
var offset = rssClass.getMinutesByNow(time);
switch(offset){
case -1:return “无效时间:”break;
case -2:return “以前的:”break;
case 0:return “最新的:”break;
default:return offset+”分钟之前:”break;
}
}
rssClass.getCustomDate = function(time){
var t = rssClass.getTimeInstance(time);
return t.year+”年”+t.month+”月”+t.date+”日”+” “+t.hour+”时”+t.minute+”分”
}
rssClass.getMinutesByNow = function(time){
var date = new Date(time);
var current = new Date();
var result = 0;
var deltaYear = current.getYear() - date.getYear();
var deltaMonth = current.getMonth() - date.getMonth();
var deltaDay = current.getDate() - date.getDate();
var deltaHour = current.getHours() - date.getHours();
var deltaMinute = current.getMinutes() - date.getMinutes();
if(deltaYear < 0){
return -1;
}
else if(deltaYear==0){
if(deltaMonth<0){
return -1
}
else if(deltaMonth==0){
if(deltaDay<0){
return -1
}
else if(deltaDay==0){
if(deltaHour<0){
return -1;
}
else if(deltaHour==0){
if(deltaMinute <0){
return -1;
}
else{
result = deltaMinute + result;
}
}
else{
result = deltaHour*60+result;
}
}
else{
return -2;
}
}
else{
return -2;
}
}
else{
return -2;
}
return result;
}

//下面是负责解析rss的代码

 

var channels;
var first=true;
var rss2Reader={};
rss2Reader.fetchRss = function (xmlDoc,delay,timeEnabled){
if(delay>0){
if(timeEnabled){
display.setTime(”time”,”default”);
}
var root = xmlDoc.getElementsByTagName(”rss”)[0];
channels = root.getElementsByTagName(”channel”);
var oldItemDate = document.getElementById(”hd”).value;
var newItemDate = rss2Reader.getLatestItemDate(channels[0]);
if(oldItemDate!==newItemDate){
display.removeAll();
for(var i=0;i<channels.length;i++){
rss2Reader.decode(channels[i]);
}
}
else{
var div = display.appendDiv(”default”);
display.appendTextNode(div,”没有更新”,”default”);
}
window.setTimeout(”sendRequest(’”+requestUrl+”‘,’1′,’GET’)”,delay);
}
else{
var root = xmlDoc.getElementsByTagName(”rss”)[0];
channels = root.getElementsByTagName(”channel”);
for(var i=0;i<channels.length;i++){
rss2Reader.decode(channels[i]);
}
}
};
rss2Reader.getLatestItemDate = function(channel){
var channelInfo = rssClass.getChannelInfoInstance(channel);
if(channelInfo!==null){
return channelInfo.pubDate;
}
return null;
};
rss2Reader.decode = function(channel){
var channelInfo = rssClass.getChannelInfoInstance(channel);
if(channelInfo!==null){
display.setHiddenValue(channelInfo.pubDate);
var items = channel.getElementsByTagName(”item”);
for(var i = 0 ; i< items.length ; i++){
itemInfo = rssClass.getItemInfoInstance(items[i]);
if(itemInfo!==null){
var div = display.appendDiv(”defaultdiv”);
display.appendTextNode(div,rssClass.getOffsetMinuteDescription(itemInfo.pubDate),”default”);
display.appendLink(div,itemInfo.title,itemInfo.link,”default”);
}
}
}
};

//下面是用来显示在页面上的方法

var holder;
var display={};
display.iniHolder = function (holderid){
holder = document.getElementById(holderid);
if(holder===null){
alert(holderid);
window.alert(”Error while getting the holder!”);
}
};
display.removeAll = function(node,style){
var length = holder.childNodes.length;
for(var i = 0; i<length;i++){
holder.removeChild(holder.firstChild);
}
};
display.appendDiv = function (style){
if(holder===null){
window.alert(”Must you first initialize the holder!”);
}
var newDiv = document.createElement(”div”);
newDiv.className = style;
holder.appendChild(newDiv);
return newDiv;
};
display.appendTextNode = function (parentNode , text , style){
var newTextNode = document.createTextNode(text);
parentNode.appendChild(newTextNode);
return newTextNode;
};
display.appendLink = function (parentNode , text , link , style){
var newlink = document.createElement(”a”);
newlink.setAttribute(”href”,link);
newlink.setAttribute(”class”,style);
var newTextNode = document.createTextNode(text);
newlink.appendChild(newTextNode);
parentNode.appendChild(newlink);
return newlink;
};
display.setTime = function(holderid,style){
var holder = document.getElementById(holderid);
if(holder===null){
window.alert(”Error while getting the holder!”);
}
else{
if(holder.hasChildNodes()){
holder.removeChild(holder.firstChild);
}
var time = document.createTextNode(rssClass.getCustomDate(new Date))
holder.appendChild(time);
holder.setAttribute(”class”,style);
}
};
display.setHiddenValue = function(value){
var hd = document.getElementById(”hd”);
hd.value = value;
};

基本的逻辑是

1 在html里调用ajax的异步获取

2 将异步获取到的xml交给rssreader解析

3 rssreader将具体的每一个item以及channel信息,用具体的“类”rssClass来解析,并保存

4 rssreader将获得的数据调用display中的方法显示到页面上

 

由于是兴起而至,所以没有加注释 呵呵 敬请原谅

 

目前有一点碰到的问题

ajax有着很严格的安全性,是不能跨域的,比如说我在自己本机的tomcat里面架了一个网站,我的html是这个网站的一部分,我用localhost去访问时,如果这个时候xml源也是tomcat里面的另一个网站里的xml,那么没问题一切正常,但是如果我fetch的是internet上的源就不行了,无论是ie还是firefox都没有权限。

 

如果我们只是通过本地访问html文件,而不是通过网站访问,那么ie是可以的,但是firefox不可以。

这是很郁闷的一件事情不是吗?

网上提供了一个解决方法,就是在internet自己的站点上架一个servlet,然后去掉,自己对自己域内的有访问权限,然后由internet上的servlet对信息做中转,访问internet其他的信息源。

这种解决方案实在是哎 ~~ 欺负我们这种穷人啊。

 

自己写着玩的,首先健壮性肯定很弱的,呵呵,欢迎提出一些意见建议

随心所欲03 Apr 2007 07:00 pm

RSS是“Rich Site Summary”或“Really Simple Syndication”的英文首字母缩写,中文称作“简易信息聚合”。RSS是一种基于XML标准,在互联网上被广泛采用的内容包装和投递协议。

RSS技术诞生于1999年的网景公司(Netscape)。当时网景公司定义了一套描述新闻频道的语言,RSS,用于将网站内容投递到Netscape Navigator互联网浏览器中。但由于公司内部商务决策、当时互联网内容匮乏等诸多原因,网景最终只发布了一个0.9版本的规范。微软当时也推出了类 似的数据规格,与RSS非常接近,试图利用新闻频道的架构把“推”(Push)技术变成一个应用主流,捆绑在IE浏览器中与Netscape Navigator抗衡。不过无奈的是,由于当时互联网访问速度慢、内容缺乏、用户不熟悉等原因,这个“推”技术自始至终没有得到市场的广泛支持。

但是随着时间的推移,RSS技术随着XML技术的发展和博客群体的快速增长,逐渐被人们广泛地接受,其应用范围也已经跳出单纯的博客圈,成为新闻传媒、电 子商务、企业知识管理等众多领域的不可缺少的新技术。2001年,RSS技术标准的发展工作被戴夫·温那(Dave Winer)的公司UserLand所接手,继续开发新的版本,以适应新的网络应用需要。通过戴夫·温那的努力,RSS升级到了0.91版,然后达到了 0.92版,并随后被众多的专业新闻站点所接受和支持。在广泛的应用过程中,众多的专业人士认识到需要组织起来,把RSS发展成为一个通用的规范,并进一 步标准化。一个联合小组根据W3C新一代的语义网技术RDF对RSS进行了重新定义,发布了RSS 1.0版,并把RSS定义为“RDF Site Summary”。这项工作并没有与戴夫·温那进行有效的沟通,而戴夫则坚持在自己设想的方向上进一步开发RSS的后续版本2.0,同时也并不承认RSS 1.0的有效性。RSS由此开始分化形成了RSS 0.9x/2.0和RSS 1.0两个阵营。

戴夫·温那在2002年9月独自把RSS升级到了2.0版本,并交由哈佛大学法学院Berkman互联网和社会学中心进行维护。而RSS 1.0版则仍然由W3C联合小组维护。

最近,著名的互联网搜索引擎公司GOOGLE收购了美国大型的博客服务网站www.blogger.com, 使这个网站一直采用的一种近似于RSS的技术衍生版Atom一夜之间成为RSS领域标准之争的新的有力竞争对手。目前,这三个技术标准阵营(RSS 0.9x/2.0,RSS 1.0,Atom 0.3) 正在展开相互兼容的对话,相信在不久的将来会有积极的结果。

RSS 1.0和 2.0 格式所包含的核心信息相同,但其结构不一样。

RSS 1.0 的根元素是 rdf:RDF,而 RSS 2.0 的根元素是 rss。rss 还包含一个强制版本属性用以表示所用的RSS的准确格式(可能的值包括:0.91, 0.94 等)。另一个主要差别是 RSS 1.0 文档有名字空间限定,RSS 2.0 的文档就没有。不管怎样,包含在两个文档中的信息本质上是一样的。

两个版本都包含 channel 元素,而 channel 元素又包含三个必须的元素:title、description 和 link,其代码如下:

<channel>
<title><!– channel 的标题 –></title>
<description><!– 简要描述 –></description>
<link><!– channel 的 URL –></link>
<!– 可选/可扩展元素 –>
</channel>
除了这些必须的元素外,RSS 1.0 还定义了三个附加元素:image、items 和 textinput,其中,image 和 textinput 是可选的。另一方面,RSS 2.0 提供了 16 个附加元素,其中也包括 image、items 和 textinput,此外还有 language、copyright、managingEditor、pubDate 和 category。RSS 1.0 允许通过定义在单独的 XML 名字空间中的可扩展元素来创建这种类型的元数据。
这两种格式在结构上的主要区别必须要看其 item、image 和 textinput 节点的表示形式。RSS 1.0 中,channel 元素包含对 item、image 和 textinput 节点的引用,这些节点存在于 channel 节点本身之外。这样在 channel 和 所引用的节点之间建立了一种 RDF 关联。如 Figure 1 所示,channel 元素与一个 image 元素以及两个 item 元素关联。RSS 2.0 中,item 元素只是在 channel 元素中连续排放(如 Figure 2 所示)。item 元素包含实际的新闻项信息。item 的结构在两个版本中是相同的。item 元素通常包含 title、link 和 description 元素,如下代码所示:
<item>
<title><!– 项标题 –></title>
<link><!– 项 URL –></link>
<description><!– 简要描述 –></description>
<!– 可选的/可扩展的元素 –>
</item>
在 RSS 1.0 中,title 和 link 是必须的,description 是可选的。而在 RSS 2.0 中,title 或 description 必须提供其中的一个;其它均可选。这些只是定义在 RSS 1.0 中的 item 元素。RSS 2.0 提供几个其它可选元素,其中有 author、category、comments、enclosure、guid、pubDate 和 source。RSS 1.0 获取这样的元数据是通过定义在单独的 XML 名字空间中称为 RSS 模块的可扩展元素来实现的。例如,在 Figure 1 中,item 的日期是用 Dublic Core 模块的 <dc:date> 元素表示的。

有关不同格式的完整信息请参考 RSS 1.0 和 2.0 规范。

那么,何为 Atom?
Atom 乃一项目的名字,主要是开发一个新的网志摘要格式以解决目前 RSS 存在的问题(混乱的版本号,不是一个真正的开放标准,表示方法的不一致,定义贫乏等等)。Atom 希望提供一个清晰的版本以解决每个人的需要,其设计完全不依赖于供货商,任何人都可以对之进行自由扩展,完整详细说明。
当今许多 Blog 引擎已经支持当前的摘要格式。Figure 3 是一个Atom 0.3 提要例子,它与前述 Figure 1 及 Figure 2 RSS 提要等同。注意 Atom 提要用名字空间限定的,但它不使用 RDF。这使得 Atom 和 RSS 1.0 及 RSS 2.0 在某些地方有相似之处。Atom 在未来是否能被接受,人们拭目以待。
除了定义新的摘要格式之外,Atom 还希望定义一个标准的档案文件格式和一个标准的网志编辑 API(Atom API)。有关 Atom 详细规范以及其它 Atom 资源请访问 The Atom Project。