]> gitweb @ CieloNegro.org - sugar.git/blob - dot-files/_vimperator/plugin/_libly_js
b4599144953e809ee2b8af9da317b12162ed038d
[sugar.git] / dot-files / _vimperator / plugin / _libly_js
1 /*** BEGIN LICENSE BLOCK {{{
2     Copyright (c) 2008 suVene<suvene@zeromemory.info>
3
4     distributable under the terms of an MIT-style license.
5     http://www.opensource.jp/licenses/mit-license.html
6 }}}  END LICENSE BLOCK ***/
7 // PLUGIN_INFO//{{{
8 var PLUGIN_INFO =
9 <VimperatorPlugin>
10     <name>libly(filename _libly.js)</name>
11     <description>Vimperator plugins library?</description>
12     <description lang="ja">適当なライブラリっぽいものたち。</description>
13     <author mail="suvene@zeromemory.info" homepage="http://zeromemory.sblo.jp/">suVene</author>
14     <license>MIT</license>
15     <version>0.1.33</version>
16     <minVersion>2.3pre</minVersion>
17     <maxVersion>2.3</maxVersion>
18     <updateURL>http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/_libly.js</updateURL>
19     <detail><![CDATA[
20 == Objects ==
21 - liberator.plugins.libly.$U
22 - liberator.plugins.libly.Request
23 - liberator.plugins.libly.Response
24 - liberator.plugins.libly.Wedata
25
26 == Logger ==
27 getLogger(prefix):
28     log(msg, level), echo(msg, flg), echoerr(msg)
29     のメソッドを持つ logger インスタンスを取得します。
30     ログの書式は prefix + ': ' + yyyy/MM/dd + msg となります。
31
32 == Object Utility ==
33 extend(dst, src):
34     オブジェクトを拡張します。
35 A(iterable):
36     オブジェクトを配列にします。
37 around(obj, name, func, autoRestore):
38   obj がもつ name 関数を、func に置き換えます。
39   func は
40     function (next, args) {...}
41   という形で呼ばれます。
42   next はオリジナルの関数を呼び出すための関数、
43   args はオリジナルの引数列です。
44   通常、next には引数を渡す必要はありません。
45   (任意の引数を渡したい場合は配列で渡します。)
46   また、autoRestore が真であれば、プラグインの再ロードなどで around が再実行されたときに、関数の置き換え前にオリジナル状態に書き戻します。
47   (多重に置き換えられなくなるので、auto_source.js などを使ったプラグイン開発で便利です)
48   返値は以下のオブジェクトです
49   >||
50   {
51     original: オリジナルの関数
52     current: 現在の関数
53     restore: 元に戻すための関数
54   }
55   ||<
56 bind(obj, func):
57     func に obj を bind します。
58     func内からは this で obj が参照できるようになります。
59 eval(text):
60     Sandbox による、window.eval を極力利用するようにします。
61     Snadbox が利用できない場合は、unsafe な window の eval が直接利用されます。
62 evalJson(str, toRemove):
63     str を decode します。
64     toRemove が true の場合、文字列の前後を1文字削除します。
65     "(key:value)" 形式の場合などに true を指定して下さい。
66 dateFormat(dtm, fmt):
67     Date型インスタンスを、指定されたフォーマットで文字列に変換します。
68     fmt を省略した場合、"%y/%M/%d %h:%m:%s" となります。
69 runnable(generator):
70     gererator を実行し、再帰的に resume する為の引数を渡します。
71
72 ==  Browser ==
73 getSelectedString:
74     window の選択文字列を返却します。
75 getUserAndPassword(hostname, formSubmitURL, username):
76     login-manager から [username, password] を取得します。
77     引数の username が省略された場合、検索された 1件目を返却します。
78     データが存在しない場合は、null を返却します。
79
80 == System ==
81 readDirectory(path, fileter, func):
82     path で指定したディレクトリから、filter で指定された正規表現に match する場合、
83     func をファイル名を引数にコールバックします。
84     filter は Function を指定することも可能です。
85
86 == HTML, XML, DOM, E4X ==
87 pathToURL(a, baseURL, doc):
88     相対パスを絶対パスに変換します。
89 getHTMLFragment(html):
90     <html>※1</html>
91     ※1 の文字列を取得します。
92 stripTags(str, tags):
93     str から tags で指定されたタグを取り除いて返却します。
94     tags は文字列、または配列で指定して下さい。
95 createHTMLDocument(str, xmlns):
96     引数 str より、HTMLFragment を作成します。
97 getFirstNodeFromXPath(xpath, context):
98     xpath を評価しオブジェクトをを返却します。
99 getNodesFromXPath(xpath, context, callback, thisObj):
100     xpath を評価し snapshot の配列を返却します。
101 xmlSerialize(xml):
102     xml を文字列化します。
103 xmlToDom(node, doc, nodes):
104     for vimperator1.2.
105     @see vimperator2.0pre util.
106 getElementPosition(elem):
107     elem の offset を返却します。
108     return {top: 0, left: 0}
109 toStyleText(style):
110     スタイルが格納されているオブジェクトを
111     >||
112         position: fixed;
113         left: 10px;
114     ||<
115     のような文字列に変換します。
116
117 == Object Request ==
118 Request(url, headers, options):
119     コンストラクタ
120     url:
121         HTTPリクエスト先のURL
122     headers:
123         以下のようにHTTPヘッダの値を指定できる(省略可)
124         >||
125         {
126             'Referer' : 'http://example.com/'
127         }
128         ||<
129         以下の値はデフォルトで設定される('Content-type'はPOST時のみ)
130         >||
131         {
132             'Accept': 'text/javascript, application/javascript, text/html, application/xhtml+xml, application/xml, text/xml, */*;q=0.1',
133             'Content-type': 'application/x-www-form-urlencoded; charset=' + options.encodingの値
134         }
135         ||<
136
137     options:
138         オプションとして以下のようなオブジェクトを指定できる(省略可)
139         asynchronous:
140             true: 同期モード/false: 非同期モード(デフォルト:true)
141         encoding:
142             エンコーディング(デフォルト: 'UTF-8')
143         username:
144             BASIC認証時のuser名
145         password:
146             BASIC認証時のパスワード
147         postBody:
148             POSTメソッドにより送信するbody
149 addEventLister(name, func):
150     イベントリスナを登録する。
151     name:
152         'onSuccess':
153             成功時
154         'onFailure':
155             失敗を表すステータスコードが返ってきた時
156         'onException':
157             例外発生時
158     func:
159         イベント発火時の処理
160         引数として以下Responseオブジェクトが渡される
161 get():
162     GETメソッドによりHTTPリクエストを発行する。
163 post():
164     POSTメソッドによりHTTPリクエストを発行する。
165
166 == Object Response ==
167 HTTPレスポンスを表すオブジェクト
168 req:
169     レスポンスと対となるRequestオブジェクト
170 doc:
171     レスポンスから生成されたHTMLDocumentオブジェクト
172 isSuccess():
173     ステータスコードが成功を表していればtrue、失敗であればfalse
174 getStatus():
175     ステータスコードを取得する
176 getStatusText():
177     ステータを表す文字列を取得する
178 getHTMLDocument(xpath, xmlns, ignoreTags, callback, thisObj):
179     レスポンスからHTMLDocumentオブジェクトを生成し、xpath を評価した結果の snapshot の配列を返す
180
181 == Object Wedata ==
182 ~/vimperator/info/profile_name/plugins-libly-wedata-?????
183 に store されます。
184 getItems(expire, itemCallback, finalCallback):
185     インスタンス作成時に指定した dbname から、item を読込みます。
186 === TODO ===
187 clearCache:
188   wedata 読込み成功したら、強制的にキャッシュと置き換えるの作って!
189
190     ]]></detail>
191 </VimperatorPlugin>;
192 //}}}
193 //if (!liberator.plugins.libly) {
194
195 liberator.plugins.libly = {};
196 var libly = liberator.plugins.libly;
197
198 libly.$U = {//{{{
199     // Logger {{{
200     getLogger: function(prefix) {
201         return new function() {
202             this.log = function(msg, level) {
203                 if (typeof msg == 'object') msg = util.objectToString(msg);
204                 liberator.log(libly.$U.dateFormat(new Date()) + ': ' + (prefix || '') + ': ' + msg, (level || 0));
205             };
206             this.echo = function(msg, flg) {
207                 flg = flg || commandline.FORCE_MULTILINE;
208                 // this.log(msg);
209                 liberator.echo(msg, flg);
210             };
211             this.echoerr = function(msg) {
212                 this.log('error: ' + msg);
213                 liberator.echoerr(msg);
214             };
215         }
216     },
217     // }}}
218     // Object Utility {{{
219     extend: function(dst, src) {
220         for (let prop in src)
221             dst[prop] = src[prop];
222         return dst;
223     },
224     A: function(iterable) {
225         var ret = [];
226         if (!iterable) return ret;
227         if (typeof iterable == 'string') return [iterable];
228         if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
229             iterable.toArray) return iterable.toArray();
230         if (typeof iterable.length != 'undefined') {
231             for (let i = 0, len = iterable.length; i < len; ret.push(iterable[i++]));
232         } else {
233             for each (let item in iterable) ret.push(item);
234         }
235         return ret;
236     },
237     around: (function () {
238         function getPluginPath () {
239           let pluginPath;
240           Error('hoge').stack.split(/\n/).some(
241             function (s)
242               let (m = s.match(/^\(\)@chrome:\/\/liberator\/content\/liberator\.js -> (.+):\d+$/))
243                 (m && (pluginPath = m[1]))
244           );
245           return pluginPath;
246         }
247
248         let restores = {};
249
250         return function (obj, name, func, autoRestore) {
251             let original;
252             let restore = function () obj[name] = original;
253             if (autoRestore) {
254                 let pluginPath = getPluginPath();
255                 if (pluginPath) {
256                     restores[pluginPath] =
257                         (restores[pluginPath] || []).filter(
258                             function (res) (
259                                 res.object != obj ||
260                                 res.name != name ||
261                                 (res.restore() && false)
262                             )
263                         );
264                     restores[pluginPath].push({
265                         object: obj,
266                         name: name,
267                         restore: restore
268                     });
269                 } else {
270                     liberator.echoerr('getPluginPath failed');
271                 }
272             }
273             original = obj[name];
274             let current = obj[name] = function () {
275                 let self = this, args = arguments;
276                 return func.call(self, function (_args) original.apply(self, _args || args), args);
277             };
278             libly.$U.extend(current, {original: original.original || original, restore: restore});
279             return libly.$U.extend({
280                 original: original,
281                 current: current,
282                 restore: restore
283             }, [original, current]);
284         };
285     })(),
286     bind: function(obj, func) {
287         return function() {
288             return func.apply(obj, arguments);
289         }
290     },
291     eval: function(text) {
292         var fnc = window.eval;
293         var sandbox;
294         try {
295             sandbox = new Components.utils.Sandbox("about:blank");
296             if (Components.utils.evalInSandbox('true', sandbox) === true) {
297                 fnc = function(text) { return Components.utils.evalInSandbox(text, sandbox); };
298             }
299         } catch (e) { liberator.log('warning: _libly.js is working with unsafe sandbox.'); }
300
301         return fnc(text);
302     },
303     evalJson: function(str, toRemove) {
304         var json;
305         try {
306             json = Components.classes['@mozilla.org/dom/json;1'].getService(Components.interfaces.nsIJSON);
307             if (toRemove) str = str.substring(1, str.length - 1);
308             return json.decode(str);
309         } catch (e) { return null; }
310     },
311     dateFormat: function(dtm, fmt) {
312         var d = {
313             y: dtm.getFullYear(),
314             M: dtm.getMonth() + 1,
315             d: dtm.getDate(),
316             h: dtm.getHours(),
317             m: dtm.getMinutes(),
318             s: dtm.getSeconds(),
319             '%': '%'
320         };
321         for (let [n, v] in Iterator(d)) {
322             if (v < 10)
323                 d[n] = '0' + v;
324         }
325         return (fmt || '%y/%M/%d %h:%m:%s').replace(/%([yMdhms%])/g, function (_, n) d[n]);
326     },
327     /**
328      * example)
329      *  $U.runnable(function(resume) {
330      *      // execute asynchronous function.
331      *      // goto next yield;
332      *      var val = yield setTimeout(function() { resume('value!'), 1000) });
333      *      alert(val);  // value!
334      *      yield;
335      *  });
336      */
337     runnable: function(generator) {
338         var it = generator(function(value) {
339                     try { it.send(value); } catch (e) {}
340                  });
341         it.next();
342     },
343     // }}}
344     // Browser {{{
345     getSelectedString: function() {
346          return (new XPCNativeWrapper(window.content.window)).getSelection().toString();
347     },
348     getUserAndPassword: function(hostname, formSubmitURL, username) {
349         var passwordManager, logins;
350         try {
351             passwordManager = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
352             logins = passwordManager.findLogins({}, hostname, formSubmitURL, null);
353             if (logins.length) {
354                 if (username) {
355                     for (let i = 0, len = logins.lengh; i < len; i++) {
356                         if (logins[i].username == username)
357                             return [logins[i].username, logins[i].password]
358                     }
359                     liberator.log(this.dateFormat(new Date()) +': [getUserAndPassword] username notfound');
360                     //throw 'username notfound.';
361                     return [];
362                 } else {
363                     return [logins[0].username, logins[0].password];
364                 }
365             } else {
366                 liberator.log(this.dateFormat(new Date()) + ': [getUserAndPassword] account notfound');
367                 return [];
368             }
369         } catch (e) {
370             liberator.log(this.dateFormat(new Date()) + ': [getUserAndPassword] error: ' + e, 0);
371             return null;
372         }
373     },
374     // }}}
375     // System {{{
376     readDirectory: function(path, filter, func) {
377         var d = io.File(path);
378         if (d.exists() && d.isDirectory()) {
379             let enm = d.directoryEntries;
380             let flg = false;
381             while (enm.hasMoreElements()) {
382                 let item = enm.getNext();
383                 item.QueryInterface(Components.interfaces.nsIFile);
384                 flg = false;
385                 if (typeof filter == 'string') {
386                     if ((new RegExp(filter)).test(item.leafName)) flg = true;
387                 } else if (typeof filter == 'function') {
388                     flg = filter(item);
389                 }
390                 if (flg) func(item);
391             }
392         }
393     },
394     // }}}
395     // HTML, XML, DOM, E4X {{{
396     pathToURL: function(a, baseURL, doc) {
397         if (!a) return '';
398         var XHTML_NS = "http://www.w3.org/1999/xhtml";
399         var XML_NS   = "http://www.w3.org/XML/1998/namespace";
400         //var path = (a.href || a.getAttribute('src') || a.action || a.value || a);
401         var path = (a.getAttribute('href') || a.getAttribute('src') || a.action || a.value || a);
402         if (/^https?:\/\//.test(path)) return path;
403         var link = (doc || window.content.documtent).createElementNS(XHTML_NS, 'a');
404         link.setAttributeNS(XML_NS, 'xml:base', baseURL);
405         link.href = path;
406         return link.href;
407     },
408     getHTMLFragment: function(html) {
409         if (!html) return html;
410         return html.replace(/^[\s\S]*?<html(?:[ \t\n\r][^>]*)?>|<\/html[ \t\r\n]*>[\S\s]*$/ig, '');
411     },
412     stripTags: function(str, tags) {
413         var ignoreTags = '(?:' + [].concat(tags).join('|') + ')';
414         return str.replace(new RegExp('<' + ignoreTags + '(?:[ \\t\\n\\r][^>]*|/)?>([\\S\\s]*?)<\/' + ignoreTags + '[ \\t\\r\\n]*>', 'ig'), '');
415     },
416     createHTMLDocument: function(str, xmlns, doc) {
417         let root = document.createElementNS("http://www.w3.org/1999/xhtml", "html");
418         let uhService = Cc["@mozilla.org/feed-unescapehtml;1"].getService(Ci.nsIScriptableUnescapeHTML);
419         let text = str.replace(/^[\s\S]*?<body([ \t\n\r][^>]*)?>[\s]*|<\/body[ \t\r\n]*>[\S\s]*$/ig, '');
420         let fragment = uhService.parseFragment(text, false, null, root);
421         let doctype = document.implementation.createDocumentType('html', '-//W3C//DTD HTML 4.01//EN', 'http://www.w3.org/TR/html4/strict.dtd');
422         let htmlFragment = document.implementation.createDocument(null, 'html', doctype);
423         htmlFragment.documentElement.appendChild(htmlFragment.importNode(fragment,true));
424         return htmlFragment;
425         /* うまく動いていない場合はこちらに戻してください
426         doc = doc || window.content.document;
427         var htmlFragment = doc.implementation.createDocument(null, 'html', null);
428         var range = doc.createRange();
429         range.setStartAfter(doc.body);
430         htmlFragment.documentElement.appendChild(htmlFragment.importNode(range.createContextualFragment(str), true));
431         return htmlFragment;
432         */
433     },
434     getFirstNodeFromXPath: function(xpath, context) {
435         if (!xpath) return null;
436         context = context || window.content.document;
437         var result = (context.ownerDocument || context).evaluate(xpath, context, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
438         return result.singleNodeValue || null;
439     },
440     getNodesFromXPath: function(xpath, context, callback, thisObj) {
441         var ret = [];
442         if (!xpath) return ret;
443         context = context || window.content.document;
444         var nodesSnapshot = (context.ownerDocument || context).evaluate(xpath, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
445         for (let i = 0, l = nodesSnapshot.snapshotLength; i < l; i++) {
446             if (typeof callback == 'function') callback.call(thisObj, nodesSnapshot.snapshotItem(i), i);
447             ret.push(nodesSnapshot.snapshotItem(i));
448         }
449         return ret;
450     },
451     xmlSerialize: function(xml) {
452         try {
453             return (new XMLSerializer()).serializeToString(xml)
454                                         .replace(/<!--(?:[^-]|-(?!->))*-->/g, '')
455                                         .replace(/<\s*\/?\s*\w+/g, function(all) all.toLowerCase());
456         } catch (e) { return '' }
457     },
458     xmlToDom: function xmlToDom(node, doc, nodes)
459     {
460         XML.prettyPrinting = false;
461         switch (node.nodeKind())
462         {
463             case "text":
464                 return doc.createTextNode(node);
465             case "element":
466                 let domnode = doc.createElementNS(node.namespace(), node.localName());
467                 for each (let attr in node.@*)
468                     domnode.setAttributeNS(attr.name() == "highlight" ? NS.uri : attr.namespace(), attr.name(), String(attr));
469                 for each (let child in node.*)
470                     domnode.appendChild(arguments.callee(child, doc, nodes));
471                 if (nodes && node.@key)
472                     nodes[node.@key] = domnode;
473                 return domnode;
474         }
475     },
476     getElementPosition: function(elem) {
477         var offsetTrail = elem;
478         var offsetLeft  = 0;
479         var offsetTop   = 0;
480         while (offsetTrail) {
481             offsetLeft += offsetTrail.offsetLeft;
482             offsetTop  += offsetTrail.offsetTop;
483             offsetTrail = offsetTrail.offsetParent;
484         }
485         offsetTop = offsetTop || null;
486         offsetLeft = offsetLeft || null;
487         return {top: offsetTop, left: offsetLeft};
488     },
489     toStyleText: function(style) {
490         var result = '';
491         for (let name in style) {
492             result += name.replace(/[A-Z]/g, function (c) ('-' + c.toLowerCase())) +
493                       ': ' +
494                       style[name] +
495                       ';\n';
496         }
497         return result;
498     }
499     // }}}
500 };
501 //}}}
502
503 libly.Request = function() {//{{{
504     this.initialize.apply(this, arguments);
505 };
506 libly.Request.EVENTS = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
507 libly.Request.requestCount = 0;
508 libly.Request.prototype = {
509     initialize: function(url, headers, options) {
510         this.url = url;
511         this.headers = headers || {};
512         this.options = libly.$U.extend({
513             asynchronous: true,
514             encoding: 'UTF-8'
515         }, options || {});
516         this.observers = {};
517     },
518     addEventListener: function(name, func) {
519         try {
520             if (typeof this.observers[name] == 'undefined') this.observers[name] = [];
521             this.observers[name].push(func);
522         } catch (e) {
523             if (!this.fireEvent('onException', new libly.Response(this), e)) throw e;
524         }
525     },
526     fireEvent: function(name, args, asynchronous) {
527         if (!(this.observers[name] instanceof Array)) return false;
528         this.observers[name].forEach(function(event) {
529             if (asynchronous) {
530                 setTimeout(event, 10, args);
531             } else {
532                 event(args);
533             }
534         });
535         return true;
536     },
537     _complete: false,
538     _request: function(method) {
539
540         try {
541             libly.Request.requestCount++;
542
543             this.method = method;
544             this.transport = new XMLHttpRequest();
545             this.transport.open(method, this.url, this.options.asynchronous, this.options.username, this.options.password);
546
547             this.transport.onreadystatechange = libly.$U.bind(this, this._onStateChange);
548             this.setRequestHeaders();
549             this.transport.overrideMimeType('text/html; charset=' + this.options.encoding);
550
551             this.body = this.method == 'POST' ? this.options.postBody : null;
552
553             this.transport.send(this.body);
554
555             // Force Firefox to handle ready state 4 for synchronous requests
556             if (!this.options.asynchronous && this.transport.overrideMimeType)
557                 this._onStateChange();
558
559         } catch (e) {
560             if (!this.fireEvent('onException', new libly.Response(this), e)) throw e;
561         }
562     },
563     _onStateChange: function() {
564         var readyState = this.transport.readyState;
565         if (readyState > 1 && !(readyState == 4 && this._complete))
566             this.respondToReadyState(this.transport.readyState);
567     },
568     getStatus: function() {
569         try {
570             return this.transport.status || 0;
571         } catch (e) { return 0; }
572     },
573     isSuccess: function() {
574         var status = this.getStatus();
575         return !status || (status >= 200 && status < 300);
576     },
577     respondToReadyState: function(readyState) {
578         var state = libly.Request.EVENTS[readyState];
579         var res = new libly.Response(this);
580
581         if (state == 'Complete') {
582             libly.Request.requestCount--;
583             try {
584                 this._complete = true;
585                 this.fireEvent('on' + (this.isSuccess() ? 'Success' : 'Failure'), res, this.options.asynchronous);
586             } catch (e) {
587                 if (!this.fireEvent('onException', res, e)) throw e;
588             }
589         }
590     },
591     setRequestHeaders: function() {
592         var headers = {
593             'Accept': 'text/javascript, application/javascript, text/html, application/xhtml+xml, application/xml, text/xml, */*;q=0.1'
594         };
595
596         if (this.method == 'POST') {
597             headers['Content-type'] = 'application/x-www-form-urlencoded' +
598                 (this.options.encoding ? '; charset=' + this.options.encoding : '');
599
600             if (this.transport.overrideMimeType) {
601                 let year = parseInt((navigator.userAgent.match(/\bGecko\/(\d{4})/) || [0, 2005])[1], 10);
602                 if (0 < year && year < 2005)
603                      headers['Connection'] = 'close';
604             }
605         }
606
607         for (let key in this.headers)
608             if (this.headers.hasOwnProperty(key)) headers[key] = this.headers[key];
609
610         for (let name in headers)
611             this.transport.setRequestHeader(name, headers[name]);
612     },
613     get: function() {
614         this._request('GET');
615     },
616     post: function() {
617         this._request('POST');
618     }
619 };//}}}
620
621 libly.Response = function() {//{{{
622     this.initialize.apply(this, arguments);
623 };
624 libly.Response.prototype = {
625     initialize: function(req) {
626         this.req = req;
627         this.transport = req.transport;
628         this.isSuccess = req.isSuccess;
629         this.readyState = this.transport.readyState;
630
631         if (this.readyState == 4) {
632             this.status = this.getStatus();
633             this.statusText = this.getStatusText();
634             this.responseText = (this.transport.responseText == null) ? '' : this.transport.responseText;
635         }
636
637         this.doc = null;
638         this.htmlFragmentstr = '';
639     },
640     status: 0,
641     statusText: '',
642     getStatus: libly.Request.prototype.getStatus,
643     getStatusText: function() {
644         try {
645             return this.transport.statusText || '';
646         } catch (e) { return ''; }
647     },
648     getHTMLDocument: function(xpath, xmlns, ignoreTags, callback, thisObj) {
649         if (!this.doc) {
650             //if (doc.documentElement.nodeName != 'HTML') {
651             //    return new DOMParser().parseFromString(str, 'application/xhtml+xml');
652             //}
653             this.htmlFragmentstr = libly.$U.getHTMLFragment(this.responseText);
654             this.htmlStripScriptFragmentstr = libly.$U.stripTags(this.htmlFragmentstr, ignoreTags);
655             this.doc = libly.$U.createHTMLDocument(this.htmlStripScriptFragmentstr, xmlns);
656         }
657         if (!xpath) xpath = '//*';
658         return libly.$U.getNodesFromXPath(xpath, this.doc, callback, thisObj);
659     }
660 };
661 //}}}
662
663 libly.Wedata = function(dbname) { // {{{
664     this.initialize.apply(this, arguments);
665 };
666 libly.Wedata.prototype = {
667     initialize: function(dbname) {
668         this.HOST_NAME = 'http://wedata.net/';
669         this.dbname = dbname;
670         this.logger = libly.$U.getLogger('libly.Wedata');
671     },
672     getItems: function(expire, itemCallback, finalCallback) {
673
674         var logger = this.logger;
675         var STORE_KEY = 'plugins-libly-wedata-' + this.dbname + '-items';
676         var store = storage.newMap(STORE_KEY, true);
677         var cache = store && store.get('data');
678
679         if (store && cache && new Date(store.get('expire')) > new Date()) {
680             logger.log('return cache. ');
681             cache.forEach(function(item) { if (typeof itemCallback == 'function') itemCallback(item); });
682             if (typeof finalCallback == 'function')
683                 finalCallback(true, cache);
684             return;
685         }
686
687         expire = expire || 0;
688
689         function errDispatcher(msg, cache) {
690             if (cache) {
691                 logger.log('return cache. -> ' + msg);
692                 cache.forEach(function(item) { if (typeof itemCallback == 'function') itemCallback(item); });
693                 if (typeof finalCallback == 'function')
694                     finalCallback(true, cache);
695             } else {
696                 logger.log(msg + ': cache notfound.');
697                 if (typeof finalCallback == 'function')
698                     finalCallback(false, msg);
699             }
700         }
701
702         var req = new libly.Request(this.HOST_NAME + 'databases/' + this.dbname + '/items.json');
703         req.addEventListener('onSuccess', libly.$U.bind(this, function(res) {
704             var text = res.responseText;
705             if (!text) {
706                 errDispatcher('response is null.', cache);
707                 return;
708             }
709             var json = libly.$U.evalJson(text);
710             if (!json) {
711                 errDispatcher('failed eval json.', cache);
712                 return;
713             }
714             logger.log('success get wedata.');
715             store.set('expire', new Date(new Date().getTime() + expire).toString());
716             store.set('data', json);
717             store.save();
718             json.forEach(function(item) { if (typeof itemCallback == 'function') itemCallback(item); });
719             if (typeof finalCallback == 'function')
720                 finalCallback(true, json);
721         }));
722         req.addEventListener('onFailure', function() errDispatcher('onFailure', cache));
723         req.addEventListener('onException', function() errDispatcher('onException', cache));
724         req.get();
725     }
726 };
727 //}}}
728
729 //}
730 // vim: set fdm=marker sw=4 ts=4 sts=0 et:
731