webui-aria2/js/libs/dojox/data/GoogleSearchStore.js.uncompressed.js
2012-05-01 19:52:07 +08:00

658 lines
23 KiB
JavaScript

//>>built
define("dojox/data/GoogleSearchStore", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/declare", "dojo/_base/window", "dojo/_base/query",
"dojo/dom-construct","dojo/io/script"],
function(dojo, lang, declare, winUtil, domQuery, domConstruct, scriptIO) {
dojo.experimental("dojox.data.GoogleSearchStore");
var SearchStore = declare("dojox.data.GoogleSearchStore",null,{
// summary:
// A data store for retrieving search results from Google.
// This data store acts as a base class for Google searches,
// and has a number of child data stores that implement different
// searches. This store defaults to searching the web, and is functionally
// identical to the dojox.data.GoogleWebSearchStore object.
// The following attributes are supported on each item:
// <ul>
// <li>url - The URL for the item</li>
// <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
// <li>visibleUrl - The URL with no protocol specified.
// <li>cacheUrl - The URL to the copy of the document cached by Google
// <li>title - The page title in HTML format.</li>
// <li>titleNoFormatting - The page title in plain text</li>
// <li>content - A snippet of information about the page</li>
// </ul>
// The query accepts one parameter: text - The string to search for
constructor: function(/*Object*/args){
// summary:
// Initializer for the GoogleSearchStore store.
// description:
// The GoogleSearchStore is a Datastore interface to
// the Google search service. The constructor accepts the following arguments:
// <ul>
// <li>label - the label attribute to use. Defaults to titleNoFormatting</li>
// <li>key - The API key to use. This is optional</li>
// <li>lang - The language locale to use. Defaults to the browser locale</li>
// </ul>
if(args){
if(args.label){
this.label = args.label;
}
if(args.key){
this._key = args.key;
}
if(args.lang){
this._lang = args.lang;
}
if("urlPreventCache" in args){
this.urlPreventCache = args.urlPreventCache?true:false;
}
}
this._id = dojox.data.GoogleSearchStore.prototype._id++;
},
// _id: Integer
// A unique identifier for this store.
_id: 0,
// _requestCount: Integer
// A counter for the number of requests made. This is used to define
// the callback function that GoogleSearchStore will use.
_requestCount: 0,
// _googleUrl: String
// The URL to Googles search web service.
_googleUrl: "http://ajax.googleapis.com/ajax/services/search/",
// _storeRef: String
// The internal reference added to each item pointing at the store which owns it.
_storeRef: "_S",
// _attributes: Array
// The list of attributes that this store supports
_attributes: [ "unescapedUrl", "url", "visibleUrl", "cacheUrl", "title",
"titleNoFormatting", "content", "estimatedResultCount"],
// _aggregtedAttributes: Hash
// Maps per-query aggregated attributes that this store supports to the result keys that they come from.
_aggregatedAttributes: {
estimatedResultCount: "cursor.estimatedResultCount"
},
// label: String
// The default attribute which acts as a label for each item.
label: "titleNoFormatting",
// type: String
// The type of search. Valid values are "web", "local", "video", "blogs", "news", "books", "images".
// This should not be set directly. Instead use one of the child classes.
_type: "web",
// urlPreventCache: boolean
// Sets whether or not to pass preventCache to dojo.io.script.
urlPreventCache: true,
// _queryAttrs: Hash
// Maps query hash keys to Google query parameters.
_queryAttrs: {
text: 'q'
},
_assertIsItem: function(/* item */ item){
// summary:
// This function tests whether the item passed in is indeed an item in the store.
// item:
// The item to test for being contained by the store.
if(!this.isItem(item)){
throw new Error("dojox.data.GoogleSearchStore: a function was passed an item argument that was not an item");
}
},
_assertIsAttribute: function(/* attribute-name-string */ attribute){
// summary:
// This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
// attribute:
// The attribute to test for being contained by the store.
if(typeof attribute !== "string"){
throw new Error("dojox.data.GoogleSearchStore: a function was passed an attribute argument that was not an attribute name string");
}
},
getFeatures: function(){
// summary:
// See dojo.data.api.Read.getFeatures()
return {
'dojo.data.api.Read': true
};
},
getValue: function(item, attribute, defaultValue){
// summary:
// See dojo.data.api.Read.getValue()
var values = this.getValues(item, attribute);
if(values && values.length > 0){
return values[0];
}
return defaultValue;
},
getAttributes: function(item){
// summary:
// See dojo.data.api.Read.getAttributes()
return this._attributes;
},
hasAttribute: function(item, attribute){
// summary:
// See dojo.data.api.Read.hasAttributes()
if(this.getValue(item,attribute)){
return true;
}
return false;
},
isItemLoaded: function(item){
// summary:
// See dojo.data.api.Read.isItemLoaded()
return this.isItem(item);
},
loadItem: function(keywordArgs){
// summary:
// See dojo.data.api.Read.loadItem()
},
getLabel: function(item){
// summary:
// See dojo.data.api.Read.getLabel()
return this.getValue(item,this.label);
},
getLabelAttributes: function(item){
// summary:
// See dojo.data.api.Read.getLabelAttributes()
return [this.label];
},
containsValue: function(item, attribute, value){
// summary:
// See dojo.data.api.Read.containsValue()
var values = this.getValues(item,attribute);
for(var i = 0; i < values.length; i++){
if(values[i] === value){
return true;
}
}
return false;
},
getValues: function(item, attribute){
// summary:
// See dojo.data.api.Read.getValue()
this._assertIsItem(item);
this._assertIsAttribute(attribute);
var val = item[attribute];
if(lang.isArray(val)) {
return val;
}else if(val !== undefined){
return [val];
}else{
return [];
}
},
isItem: function(item){
// summary:
// See dojo.data.api.Read.isItem()
if(item && item[this._storeRef] === this){
return true;
}
return false;
},
close: function(request){
// summary:
// See dojo.data.api.Read.close()
},
_format: function(item, name){
return item;//base implementation does not format any items
},
fetch: function(request){
// summary:
// Fetch Google search items that match to a query
// request:
// A request object
// fetchHandler:
// A function to call for fetched items
// errorHandler:
// A function to call on error
request = request || {};
var scope = request.scope || winUtil.global;
if(!request.query){
if(request.onError){
request.onError.call(scope, new Error(this.declaredClass +
": A query must be specified."));
return;
}
}
//Make a copy of the request object, in case it is
//modified outside the store in the middle of a request
var query = {};
for(var attr in this._queryAttrs) {
query[attr] = request.query[attr];
}
request = {
query: query,
onComplete: request.onComplete,
onError: request.onError,
onItem: request.onItem,
onBegin: request.onBegin,
start: request.start,
count: request.count
};
//Google's web api will only return a max of 8 results per page.
var pageSize = 8;
//Generate a unique function to be called back
var callbackFn = "GoogleSearchStoreCallback_" + this._id + "_" + (++this._requestCount);
//Build up the content to send the request for.
//rsz is the result size, "large" gives 8 results each time
var content = this._createContent(query, callbackFn, request);
var firstRequest;
if(typeof(request.start) === "undefined" || request.start === null){
request.start = 0;
}
if(!request.count){
request.count = pageSize;
}
firstRequest = {start: request.start - request.start % pageSize};
var _this = this;
var searchUrl = this._googleUrl + this._type;
var getArgs = {
url: searchUrl,
preventCache: this.urlPreventCache,
content: content
};
var items = [];
var successfulReq = 0;
var finished = false;
var lastOnItem = request.start -1;
var numRequests = 0;
var scriptIds = [];
// Performs the remote request.
function doRequest(req){
//Record how many requests have been made.
numRequests ++;
getArgs.content.context = getArgs.content.start = req.start;
var deferred = scriptIO.get(getArgs);
scriptIds.push(deferred.ioArgs.id);
//We only set up the errback, because the callback isn't ever really used because we have
//to link to the jsonp callback function....
deferred.addErrback(function(error){
if(request.onError){
request.onError.call(scope, error, request);
}
});
}
// Function to handle returned data.
var myHandler = function(start, data){
if (scriptIds.length > 0) {
// Delete the script node that was created.
domQuery("#" + scriptIds.splice(0,1)).forEach(domConstruct.destroy);
}
if(finished){return;}
var results = _this._getItems(data);
var cursor = data ? data['cursor']: null;
if(results){
//Process the results, adding the store reference to them
for(var i = 0; i < results.length && i + start < request.count + request.start; i++) {
_this._processItem(results[i], data);
items[i + start] = results[i];
}
successfulReq ++;
if(successfulReq == 1){
// After the first request, we know how many results exist.
// So perform any follow up requests to retrieve more data.
var pages = cursor ? cursor.pages : null;
var firstStart = pages ? Number(pages[pages.length - 1].start) : 0;
//Call the onBegin method if it exists
if (request.onBegin){
var est = cursor ? cursor.estimatedResultCount : results.length;
var total = est ? Math.min(est, firstStart + results.length) : firstStart + results.length;
request.onBegin.call(scope, total, request);
}
// Request the next pages.
var nextPage = (request.start - request.start % pageSize) + pageSize;
var page = 1;
while(pages){
if(!pages[page] || Number(pages[page].start) >= request.start + request.count){
break;
}
if(Number(pages[page].start) >= nextPage) {
doRequest({start: pages[page].start});
}
page++;
}
}
// Call the onItem function on all retrieved items.
if(request.onItem && items[lastOnItem + 1]){
do{
lastOnItem++;
request.onItem.call(scope, items[lastOnItem], request);
}while(items[lastOnItem + 1] && lastOnItem < request.start + request.count);
}
//If this is the last request, call final fetch handler.
if(successfulReq == numRequests){
//Process the items...
finished = true;
//Clean up the function, it should never be called again
winUtil.global[callbackFn] = null;
if(request.onItem){
request.onComplete.call(scope, null, request);
}else{
items = items.slice(request.start, request.start + request.count);
request.onComplete.call(scope, items, request);
}
}
}
};
var callbacks = [];
var lastCallback = firstRequest.start - 1;
// Attach a callback function to the global namespace, where Google can call it.
winUtil.global[callbackFn] = function(start, data, responseCode, errorMsg){
try {
if(responseCode != 200){
if(request.onError){
request.onError.call(scope, new Error("Response from Google was: " + responseCode), request);
}
winUtil.global[callbackFn] = function(){};//an error occurred, do not return anything else.
return;
}
if(start == lastCallback + 1){
myHandler(Number(start), data);
lastCallback += pageSize;
//make sure that the callbacks happen in the correct sequence
if(callbacks.length > 0){
callbacks.sort(_this._getSort());
//In case the requsts do not come back in order, sort the returned results.
while(callbacks.length > 0 && callbacks[0].start == lastCallback + 1){
myHandler(Number(callbacks[0].start), callbacks[0].data);
callbacks.splice(0,1);
lastCallback += pageSize;
}
}
}else{
callbacks.push({start:start, data: data});
}
} catch (e) {
request.onError.call(scope, e, request);
}
};
// Perform the first request. When this has finished
// we will have a list of pages, which can then be
// gone through
doRequest(firstRequest);
},
_getSort: function() {
return function(a,b){
if(a.start < b.start){return -1;}
if(b.start < a.start){return 1;}
return 0;
};
},
_processItem: function(item, data) {
item[this._storeRef] = this;
// Copy aggregated attributes from query results to the item.
for(var attribute in this._aggregatedAttributes) {
item[attribute] = lang.getObject(this._aggregatedAttributes[attribute], false, data);
}
},
_getItems: function(data){
return data['results'] || data;
},
_createContent: function(query, callback, request){
var content = {
v: "1.0",
rsz: "large",
callback: callback,
key: this._key,
hl: this._lang
};
for(var attr in this._queryAttrs) {
content[this._queryAttrs[attr]] = query[attr];
}
return content;
}
});
var WebSearchStore = declare("dojox.data.GoogleWebSearchStore", SearchStore,{
// Summary:
// A data store for retrieving search results from Google.
// The following attributes are supported on each item:
// <ul>
// <li>title - The page title in HTML format.</li>
// <li>titleNoFormatting - The page title in plain text</li>
// <li>content - A snippet of information about the page</li>
// <li>url - The URL for the item</li>
// <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
// <li>visibleUrl - The URL with no protocol specified.</li>
// <li>cacheUrl - The URL to the copy of the document cached by Google</li>
// <li>estimatedResultCount - (aggregated per-query) estimated number of results</li>
// </ul>
// The query accepts one parameter: text - The string to search for
});
var BlogSearchStore = declare("dojox.data.GoogleBlogSearchStore", SearchStore,{
// Summary:
// A data store for retrieving search results from Google.
// The following attributes are supported on each item:
// <ul>
// <li>title - The blog post title in HTML format.</li>
// <li>titleNoFormatting - The blog post title in plain text</li>
// <li>content - A snippet of information about the blog post</li>
// <li>blogUrl - The URL for the blog</li>
// <li>postUrl - The URL for the a single blog post</li>
// <li>visibleUrl - The URL with no protocol specified.
// <li>cacheUrl - The URL to the copy of the document cached by Google
// <li>author - The author of the blog post</li>
// <li>publishedDate - The published date, in RFC-822 format</li>
// </ul>
// The query accepts one parameter: text - The string to search for
_type: "blogs",
_attributes: ["blogUrl", "postUrl", "title", "titleNoFormatting", "content",
"author", "publishedDate"],
_aggregatedAttributes: { }
});
var LocalSearchStore = declare("dojox.data.GoogleLocalSearchStore", SearchStore,{
// summary:
// A data store for retrieving search results from Google.
// The following attributes are supported on each item:
// <ul>
// <li>title - The blog post title in HTML format.</li>
// <li>titleNoFormatting - The blog post title in plain text</li>
// <li>content - A snippet of information about the blog post</li>
// <li>url - The URL for the item</li>
// <li>lat - The latitude.</li>
// <li>lng - The longtitude.</li>
// <li>streetAddress - The street address</li>
// <li>city - The city</li>
// <li>region - The region</li>
// <li>country - The country</li>
// <li>phoneNumbers - Phone numbers associated with this address. Can be one or more.</li>
// <li>ddUrl - A URL that can be used to provide driving directions from the center of the search results to this search results</li>
// <li>ddUrlToHere - A URL that can be used to provide driving directions from this search result to a user specified location</li>
// <li>staticMapUrl - The published date, in RFC-822 format</li>
// <li>viewport - Recommended viewport for the query results (same for all results in a query)
// <ul>
// <li>center - contains lat, lng properties</li>
// <li>span - lat, lng properties for the viewport span</li>
// <li>ne, sw - lat, lng properties for the viewport corners<li>
// </ul>
// </li>
// </ul>
// The query accepts the following parameters:
// <ul>
// <li>text - The string to search for</li>
// <li>centerLatLong - Comma-separated lat & long for the center of the search (e.g. "48.8565,2.3509")</li>
// <li>searchSpan - Comma-separated lat & long degrees indicating the size of the desired search area (e.g. "0.065165,0.194149")</li>
// </ul>
_type: "local",
_attributes: ["title", "titleNoFormatting", "url", "lat", "lng", "streetAddress",
"city", "region", "country", "phoneNumbers", "ddUrl", "ddUrlToHere",
"ddUrlFromHere", "staticMapUrl", "viewport"],
_aggregatedAttributes: {
viewport: "viewport"
},
_queryAttrs: {
text: 'q',
centerLatLong: 'sll',
searchSpan: 'sspn'
}
});
var VideoSearchStore = declare("dojox.data.GoogleVideoSearchStore", SearchStore,{
// summary:
// A data store for retrieving search results from Google.
// The following attributes are supported on each item:
// <ul>
// <li>title - The blog post title in HTML format.</li>
// <li>titleNoFormatting - The blog post title in plain text</li>
// <li>content - A snippet of information about the blog post</li>
// <li>url - The URL for the item</li>
// <li>published - The published date, in RFC-822 format.</li>
// <li>publisher - The name of the publisher.</li>
// <li>duration - The approximate duration, in seconds, of the video.</li>
// <li>tbWidth - The width in pixels of the video.</li>
// <li>tbHeight - The height in pixels of the video</li>
// <li>tbUrl - The URL to a thumbnail representation of the video.</li>
// <li>playUrl - If present, supplies the url of the flash version of the video that can be played inline on your page. To play this video simply create and <embed> element on your page using this value as the src attribute and using application/x-shockwave-flash as the type attribute. If you want the video to play right away, make sure to append &autoPlay=true to the url..</li>
// </ul>
// The query accepts one parameter: text - The string to search for
_type: "video",
_attributes: ["title", "titleNoFormatting", "content", "url", "published", "publisher",
"duration", "tbWidth", "tbHeight", "tbUrl", "playUrl"],
_aggregatedAttributes: { }
});
var NewsSearchStore = declare("dojox.data.GoogleNewsSearchStore", SearchStore,{
// summary:
// A data store for retrieving search results from Google.
// The following attributes are supported on each item:
// <ul>
// <li>title - The news story title in HTML format.</li>
// <li>titleNoFormatting - The news story title in plain text</li>
// <li>content - A snippet of information about the news story</li>
// <li>url - The URL for the item</li>
// <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
// <li>publisher - The name of the publisher</li>
// <li>clusterUrl - A URL pointing to a page listing related storied.</li>
// <li>location - The location of the news story.</li>
// <li>publishedDate - The date of publication, in RFC-822 format.</li>
// <li>relatedStories - An optional array of objects specifying related stories.
// Each object has the following subset of properties:
// "title", "titleNoFormatting", "url", "unescapedUrl", "publisher", "location", "publishedDate".
// </li>
// </ul>
// The query accepts one parameter: text - The string to search for
_type: "news",
_attributes: ["title", "titleNoFormatting", "content", "url", "unescapedUrl", "publisher",
"clusterUrl", "location", "publishedDate", "relatedStories" ],
_aggregatedAttributes: { }
});
var BookSearchStore = declare("dojox.data.GoogleBookSearchStore", SearchStore,{
// summary:
// A data store for retrieving search results from Google.
// The following attributes are supported on each item:
// <ul>
// <li>title - The book title in HTML format.</li>
// <li>titleNoFormatting - The book title in plain text</li>
// <li>authors - An array of authors</li>
// <li>url - The URL for the item</li>
// <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
// <li>bookId - An identifier for the book, usually an ISBN.</li>
// <li>pageCount - The number of pages in the book.</li>
// <li>publishedYear - The year of publication.</li>
// </ul>
// The query accepts one parameter: text - The string to search for
_type: "books",
_attributes: ["title", "titleNoFormatting", "authors", "url", "unescapedUrl", "bookId",
"pageCount", "publishedYear"],
_aggregatedAttributes: { }
});
var ImageSearchStore = declare("dojox.data.GoogleImageSearchStore", SearchStore,{
// summary:
// A data store for retrieving search results from Google.
// The following attributes are supported on each item:
// <ul>
// <li>title - The image title in HTML format.</li>
// <li>titleNoFormatting - The image title in plain text</li>
// <li>url - The URL for the image</li>
// <li>unescapedUrl - The URL for the image, with URL escaping. This is often more readable</li>
// <li>tbUrl - The URL for the image thumbnail</li>
// <li>visibleUrl - A shortened version of the URL associated with the result, stripped of a protocol and path</li>
// <li>originalContextUrl - The URL of the page containing the image.</li>
// <li>width - The width of the image in pixels.</li>
// <li>height - The height of the image in pixels.</li>
// <li>tbWidth - The width of the image thumbnail in pixels.</li>
// <li>tbHeight - The height of the image thumbnail in pixels.</li>
// <li>content - A snippet of information about the image, in HTML format</li>
// <li>contentNoFormatting - A snippet of information about the image, in plain text</li>
// </ul>
// The query accepts one parameter: text - The string to search for
_type: "images",
_attributes: ["title", "titleNoFormatting", "visibleUrl", "url", "unescapedUrl",
"originalContextUrl", "width", "height", "tbWidth", "tbHeight",
"tbUrl", "content", "contentNoFormatting"],
_aggregatedAttributes: { }
});
return {
Search: SearchStore,
ImageSearch: ImageSearchStore,
BookSearch: BookSearchStore,
NewsSearch: NewsSearchStore,
VideoSearch: VideoSearchStore,
LocalSearch: LocalSearchStore,
BlogSearch: BlogSearchStore,
WebSearch: WebSearchStore
}
});