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