2008年4月17日

Linked CSE (Custom Search Engine)

Google的自訂搜尋引擎,讓使用者利用Google的資源打造自己的搜尋,設定檔可以放在Google上用代號連結,或是放在其他地方用url連結 (Linked CSE),使用Linked CSE可以在使用者端動態的改變設定檔及其連結,增加了很大的彈性。另外Google有提供一些工具,可以動態產生設定檔,例如makecse可以讀取HTML、RSS、ATOM、OPML格式的檔案,掃描其中的連結,作為設定檔中的網域限制,來限制CSE的查詢範圍,Blogger in draft中的search box的其中一部分,就是使用這個功能,提供範圍為blog中所有連結的查詢。

可以用這個線上Demo來測試,試試在URL欄輸入feed的位置,會有什麼效果。

Blogger Data API能產生符合多個標籤的文章的feed (使用summary post,而不是full post,以減少不必要的連結,max-results參數設成很大的數以取得所有符合的文章),加上Linked CSE、makecse (參數設定pattern=exact),雖然還有些缺陷,但是可以做出多標籤文章中的關鍵字查詢 (Multiple labels or tags keyword search),當文章越來越多,若是標籤有分類好,這個功能將來也許會有用處。

這個方法的缺陷在於:

  1. 因為makecse本身的功能,會多出一些網站 (feed中多餘的資訊,或是文章摘要中的連結),目前一般只會多一個,例如傳入http://happytemplate.blogspot.com/feeds/posts/summary/-/Javascript/Cross%20Domain這個feed,產生的設定檔會多出happytemplate.blogspot.com/search/label/Javascript。
    (非本網誌的連結可以用搜尋運算子site:來排除,使用Google AJAX Search API的話,可以用setQueryAddition()在幕後加入。若要完全解決多出來的一個網站,需要用其他方法動態產生設定檔。)
  2. 目前只有web search有此功能,blog search還沒有,差別在於文章本身之外的內容也可能會被搜尋到,例如在側邊欄中的東西。
  3. 因為設定檔是動態產生,所以會要多等一下,若是使用Google AJAX search control,通常第一次搜尋,會有提示訊息要你過幾秒再按一次。

使用Google AJAX Search API做了以上的功能放在側邊欄,介面使用其中的google.search.searchControl,沒有多作修改。

Read more

2008年4月14日

Label Cloud Online Test

標籤雲的基本原理很簡單,就是改變一下CSS而已。

Blogger中的標籤格式大致如下:
<div>
  <ul>
    <li><a>AAA</a><span>(2)</span></li>
    <li><a>BBB</a><span>(1)</span></li>
  </ul>
</div>
只要將<li>設成display:inline,就會變成橫向表示,IE上要使標籤換行,還要在外面的區塊加上word-wrap:break-word (不知道是否有其他方法)。文章數的部分可依需要改變,例如是否顯示及顏色大小等。接下來就是依照標籤的文章數將標籤的顏色大小加以改變,顏色加在<a>上,文字大小加在<li>上 (好像這樣line-height才會有用)。
依照文章數改變style一般使用以下的方法,以log(文章數)/log(最大文章數)來做加權:
weightStyle( min, max, count, maxCount ) {
  if (maxCount <= 1 || min === max ) {
    return max;
  } else if (min < max) {
    return min + Math.floor(Math.log(count) * (max-min) / Math.log(maxCount));
  } else {
    return min - Math.floor(Math.log(count) * (min-max) / Math.log(maxCount));
  }
}
加權的方式可以自己做合理的改變,例如想要靠近代表最小值的顏色多一點,可以依照比例縮小加權用的文章數:
if (count < maxCount*0.66) {
  count = (count-1)*0.3 + 1;
}
標籤雲的線上測試:用來選擇自己喜歡的顏色及大小範圍、加權方式,可以設定標籤數、最大文章數,按下Refresh按鈕重繪。繪圖使用jquery plugin:flot,顏色選擇器使用yui color picker,flot使用canvas繪圖,因為IE不支援,所以需要ExploreCanvas (動態載入ExploreCanvas的話,flot繪圖會有問題,目前原因不明),祝大家找到自己喜歡的標籤雲。
Read more

2008年1月15日

Google AJAX Feed API

Google.com serves as a proxy to the outside feeds。

Google AJAX Feed APIGoogle AJAX API的一個分支,簡單來說就是連到Google來取得feed,本質上仍是使用script tag的方式突破same-origin policy取得資料。

  • 說明上說要先申請一個Google API key,雖然要輸入使用的URL(說明上說在此URL下所有的子目錄都可以使用,且可以用不同的URL申請),但是我用部落格申請的key,在個人電腦上也可以用,好像並沒有限制,甚至沒有key也可以用。
  • 依照Developer's Guide的說明,使用起來非常直覺簡單。
  • 有提供JSON、XML、Conbined JSON/XML三種資料格式,JSON格式將RSS或Atom的部分常用元素轉成對應的欄位,並統一名稱,並沒有全部轉換,XML格式可以使用google.feeds.getElementsByTagNameNS來取得擴充的資料。
  • feed資料由crawler(Feedfetcher)取得,所以不一定是最新的。
  • 可以設定取得資料的數量,預設是4個。
  • 對於一般的資料顯示,有FeedControl可以直接用,另外還有提供一些成品可以套用(請參考ExampleBlog)。
  • 甚至可以做feed的查詢及探知

範例

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("feeds", "1");

function initialize() {
  var feed = new google.feeds.Feed("http://api.flickr.com/services/feeds/photos_public.gne?tags=羅平,油菜花,sunset&tagmode=all");
  feed.setResultFormat(google.feeds.Feed.MIXED_FORMAT);
  feed.load(function(result) {
    if (!result.error) {
      var div = document.getElementById("flickr photos");
      for (var i = 0; i < result.feed.entries.length; i++) {
        var entry = result.feed.entries[i];
        var img = document.createElement("img");
        var imgLink = google.feeds.getElementsByTagNameNS(entry.xmlNode, "http://www.w3.org/2005/Atom", "link")[2];
        img.setAttribute("src", imgLink.getAttribute("href"));
        img.setAttribute("title", entry.title);
        img.setAttribute("width", "200");
        img.setAttribute("height", "116");
        var a = document.createElement("a");
        a.setAttribute("href", entry.link);
        a.setAttribute("target","_blank");
        a.appendChild(img);
        div.appendChild(a);
      }
    }
  });
}

google.setOnLoadCallback(initialize);
</script>

動態載入updated on 2008-04-18

基本上使用Google AJAX API loader載入所需的模組分為兩個階段:

  1. <script type="text/javascript" src="http://www.google.com/jsapi"></script> 建立google、google.loader模組及相關函式
  2. 使用google.load(moduleName, moduleVersion, optionalSettings)載入所需要的AJAX API模組,例如google.load("feeds","1")載入google.feeds模組

Google對這兩個階段都提供了動態載入的支援 (參考:blogdoc):

  1. 加上callback參數,動態插入<script type="text/javascript" src="http://www.google.com/jsapi&callback=YOUR_CALLBACK"></script>,載入完成後會執行所指定的回呼函式。
  2. 傳入callback參數,google.load(..., ..., {"callback": YOUR_CALLBACK})會使用動態插入script tag的方式載入模組,完成後會執行所指定的回呼函式。
Read more

2008年1月13日

Multiple labels or tags search

目前很多網站及部落格都有提供使用標籤來分類文章的功能,對於以網頁提供服務的方式,這是傳統目錄分類方式的合理替代品。在瀏覽部落格時,常會想要找出符合多個標籤的文章,但網站上通常都沒有明確的說明如何做到,只把標籤當作類似單層的目錄來使用。

以下是我目前知道的多標籤查詢方法:

1. Technorati

在 Advanced Serch 中,Tag Search 好像可以做多個標籤邏輯組合的搜尋(使用AND、OR、NOT,空白似乎代表AND,標籤中有空白的話可用雙引號「"」將標籤包起來),但是介面上缺少只搜尋某個網站文章的功能,但是上面的 Keyword Search 卻有這項功能,使用的參數名稱是from,於是試著自己加看看,結果竟然成功了。

例如要搜尋 Ajaxian.com 上包含 jQuery 和 Prototype 標籤的文章(標籤的大小寫好像無關,這點跟一些部落格上的查詢好像有差),可以在網址列上輸入 http://technorati.com/tag/jQuery+AND+prototype?authority=n&language=n&from=http://ajaxian.com/ (中間用 jQuery+prototype 也可以)。

可惜的是查詢結果的 feed 及網站所提供的 Tag Query API 無法用上面的參數得到限制網站的結果,不過或許還有隱藏秘技沒被發現,有了這些資訊,要自己做一個多標籤限制網站的Technorati搜尋介面應該不難。

另外有的部落格較舊的文章在 Technorati 上找不到。(ps. 關於 Technorati 有一些問題,例如如何與部落格同步增減標籤及文章,或著如何以人工方式做到,如果有人知道希望能分享一下)

2. Blogger Data API(for Blogger.com 的 blog )

Blogger Data API 是 Google Data API 的一個分支,提供以 feed 的格式存取 blog 資料的功能,其中包括查詢多個標籤文章的功能,例如要查詢本站包含 Javascript 和 Cross Domain 標籤的文章,可以輸入 http://happytemplate.blogspot.com/feeds/posts/summary/-/Javascript/Cross%20Domain

要注意的是標籤的大小寫要一致,並且要經過 UTF-8 編碼,目前只支援 AND 的功能,用「/」表示。另外網頁上所列的查詢參數有一些錯誤,orderby 的值可以是 published(發佈)或 updated(更新),updated-min 和 updated-max 要與 orderby=updated 一起用才會有效,日期時間的值似乎不能加後面的時區(與 Google Data API 上說的不一樣),以上這些可在討論群組中找到。

利用這些資訊,練習寫了一個 Multiple labels search,放在側邊欄:

  • 多個標籤的 AND 以「/」表示,標籤與/之間不能有空白,例如:Javascript/Cross Domain
  • 本站查詢可以從下方標籤雲以拖拉的方式輸入
  • 可以查詢其他 Blogger.com 的 blog
  • 日期格式為YYYY-MM-DD,會跳出月曆方便選擇,不接受時間
  • 為了方便,日期範圍統一依照排序方式為發佈日期或更新日期
  • 顯示的日期時間以查詢目標網站的時區為主,不做轉換

程式方面的感想:

  • 使用 dynamic script sag 及 JSONP 方式取得資料,若是查詢失敗或產生的網址有錯,script tag 的 onload 事件好像不會觸發,所以使用 timer 來刪除失敗時的 script tag,不知道是否有更好的方法,另外不知是否有辦法取得 script tag 所載入的內容。
  • IE 真的很難搞,連簡單的產生並設定 DOM 物件的屬性的都會有問題,根據網路上的討論,似乎短時間內還不會改變。
  • 放在 Google project hosting 的 download 中的 css 檔似乎無法在網頁中直接連結,還不知道原因。
  • 會再用 Google AJAX Feed API 試試看。
  • Yahoo PipesFilter operator 也可以用來過濾標籤,但是對於 Atom 格式的 feed 會有問題,未深入研究,不知怎樣將參數導入 Filter operator。
Read more

2007年11月14日

JSONP

The script tag method for cross-domain data fetching: JSON with Padding, or simply JSONP. - Bob Ippolito

緣由

為了安全,瀏覽器中的javascript不能存取位在不同domain的資料,這稱做same-origin policySame-Origin Policy Part 1: Why we’re stuck with things like XSS and XSRF/CSRF中說的很清楚:

Put simply, same-origin prevents a document or script loaded from one site of origin from manipulating properties of or communicating with a document loaded from another site of origin. In this case the term origin refers to the domain name, port, and protocol of the site hosting the document. The following list covers the major actions that cause the browser to check access against the same-origin policy:
  • manipulating browser windows
  • URLs requested via the XmlHttpRequest
  • manipulating frames (including inline frames)
  • manipulating documents (included using the object tag)
  • manipulating cookie

但是HTML本身仍可以加入很多其他domain的資源。

The above restrictions don’t limit all interaction, however. There is no limitation on including documents from other sources in HTML tag elements. It’s fairly common for images, style sheets, and scripts to be included from other domains. In fact, the only time same-origin explicitly restricts document retrieval is when the XmlHttpRequest method is used.

若是需要cross domain取得資料時,例如使用其他網站提供的web service,就需要一些額外的輔助,例如local proxy,但是一般的使用者,無法在server上放proxy。 於是Bob Ippolito提出了以下的建議:

  • 使用script tag載入javascript檔的方式,可以cross domain取得資料。
  • server端傳回的資料採JSON(Javascript object notation)格式。
  • server端提供一項服務,接受回呼函式(callback function)名稱為參數,將傳回的資料當成回呼函式的參數,傳回對此回呼函式的呼叫(padding 所指的就是此加上回呼函式的填充動作)。當此javascript傳輸完成,client端會自動執行,呼叫此回呼函式,因此在資料傳輸完畢時可以得知。

這裡需要的是server端的幫助,目前GoogleYahoo等的服務都有提供JSONP格式的回傳。

範例

Flickr提供的服務查詢標籤包含羅平、油菜花、sunset的公開照片,並加入參數format=json、jsoncallback=doPhoto,表示傳回資料為JSON格式,並將其填入回呼函式doPhoto的呼叫中作為參數:

<script type="text/javascript" src="http://api.flickr.com/services/feeds/photos_public.gne?tags=羅平,油菜花,sunset&tagmode=all&format=json&jsoncallback=doPhoto">

會得到「回呼函式名稱(JSON格式資料)」這種格式的回傳:

doPhoto({
"title": "每個人的相片 已標籤 羅平, 油菜花 和 sunset",
"link": "http://www.flickr.com/photos/",
"description": "",
"modified": "2007-04-22T14:19:09Z",
"generator": "http://www.flickr.com/",
"items": [
{
"title": "羅平金雞嶺夕陽",
"link": "http://www.flickr.com/photos/38362352@N00/468370409/",
"media": {"m":"http://farm1.static.flickr.com/211/468370409_319f383a24_m.jpg"},
"date_taken": "2007-04-22T22:19:09-08:00",
"description": "...",
"published": "2007-04-22T14:19:09Z",
"author": "nobody@flickr.com (Nature RGB)",
"author_id": "38362352@N00",
"tags": "sunset yunnan 油菜花 羅平"
}
]
})

當回傳載入完畢後,client端執行上面這段javascript,便會呼叫之前定義好的doPhoto,這裡做的是秀出查詢的圖片(請原諒我憋腳的javascript):

function doPhoto(result) {
var div = document.getElementById("flickr photos");
for(var i=0; i < result.items.length; i++) {
var img = document.createElement("img");
img.setAttribute("src",result.items[i].media.m);
img.setAttribute("alt",result.items[i].title);
var a = document.createElement("a");
a.setAttribute("href",result.items[i].link);
a.setAttribute("target","_blank");
a.appendChild(img);
div.appendChild(a);
}
}
Read more