root/lang/javascript/vimperator-plugins/branches/2.0/delicious_search.js

Revision 31567, 7.6 kB (checked in by teramako, 18 months ago)

fix:finalizeし忘れ

Line 
1let PLUGIN_INFO =
2<VimperatorPlugin>
3<name>{NAME}</name>
4<description>search DeliciousBookmark and that completer</description>
5<require type="extension" id="{2fa4ed95-0317-4c6a-a74c-5f3e3912c1f9}">Delicious Bookmarks</require>
6<author mail="teramako@gmail.com" homepage="http://vimperator.g.hatena.ne.jp/teramako/">teramako</author>
7<version>0.2</version>
8<minVersion>2.0pre</minVersion>
9<maxVersion>2.0</maxVersion>
10<detail><![CDATA[
11== Command ==
12:ds[earch] -tags tag, ...:
13:delicious[search] -tags tag, ...:
14  search bookmark contains all tags
15
16:ds[earch] -query term:
17:delicious[search] -query term:
18  search bookmark contains term in the titile or the URL or the note
19
20== Completion ==
21:open or :tabopen command completion
22
23>||
24set complete+=D
25||<
26
27or write in RC file
28
29>||
30autocmd VimperatorEnter ".*" :set complete+=D
31||<
32
33]]></detail>
34</VimperatorPlugin>;
35
36liberator.plugins.delicious = (function(){
37
38let uuid = PLUGIN_INFO.require[0].@id.toString();
39if (Application.extensions.has(uuid) && Application.extensions.get(uuid).enabled){
40  const ydls = Cc["@yahoo.com/nsYDelLocalStore;1"].getService(Ci.nsIYDelLocalStore);
41} else {
42  return null;
43}
44const ss = Cc["@mozilla.org/storage/service;1"].getService(Ci.mozIStorageService);
45
46// dabase connection object
47let dbc = null;
48
49// sql statements
50let statements = {
51  allTags: null,
52  simpleQuery: null,
53};
54
55/**
56 * return Delicious bookmark batabase file
57 * @return {nsIFile}
58 */
59function getBookmarkFile(){
60  let file = services.get("directory").get("ProfD",Ci.nsIFile)
61  file.append("ybookmarks.sqlite");
62  if (!file.exists() || !file.isReadable()){
63    return null;
64  }
65  return file;
66}
67
68/**
69 * get all tag names from Delicious Bookmark
70 * @return {String[]}
71 */
72function getAllTags(){
73  let list = [];
74  let st = statements.allTags;
75  try {
76    while (st.executeStep()){
77      list.push(st.getString(0));
78    }
79  } finally {
80    st.reset();
81  }
82  return list;
83}
84/**
85 * @param {CompetionContext} context
86 * @param {Array} args
87 * @return {Array}
88 */
89function tagCompletion(context, args){
90  let filter = context.filter;
91  let have = filter.split(",");
92  args.completeFilter = have.pop();
93  let prefix = filter.substr(0, filter.length - args.completeFilter.length);
94  let tags = getAllTags();
95  return [[prefix + tag, tag] for ([i, tag] in Iterator(tags)) if (have.indexOf(tag)<0)];
96}
97/**
98 * search Delicious Bookmarks
99 * contains all tags and query in title or URL or Note
100 * @param {String[]} tags
101 * @param {String} query
102 * @return {Array[]} [[url, title, note], ...]
103 *         if both tags and query is none, returns []
104 */
105function bookmarkSearch(tags, query){
106  if (!query && (!tags || tags.length == 0))
107    return [];
108
109  let sql;
110  let list = [];
111  let st;
112  let finalize = true;
113  try {
114    if (!tags || tags.length == 0){
115      st = statements.simpleQuery;
116      st.bindUTF8StringParameter(0, '%' + query + '%');
117      finalize = false;
118    }  else {
119      let sqlList = [
120        'SELECT b.name,b.url,b.description',
121        'FROM bookmarks b, bookmarks_tags bt, tags t',
122        'WHERE bt.tag_id = t.rowid',
123        'AND b.rowid = bt.bookmark_id',
124        'AND t.name in (',
125        ['?' + (parseInt(i)+1) for (i in tags)].join(","),
126        ')'];
127      if (query){
128        let num = tags.length + 1;
129        sqlList.push([
130          'AND (',
131          'b.name like', '?' + num,
132          'OR b.url like', '?' + num,
133          'OR b.description like', '?' + num,
134          ')',
135          'GROUP BY b.rowid HAVING COUNT (b.rowid) = ?' + (num + 1),
136          'ORDER BY b.added_date DESC'
137        ].join(" "));
138        sql = sqlList.join(" ");
139        st = dbc.createStatement(sql);
140        st.bindUTF8StringParameter(tags.length, '%'+query+'%');
141        st.bindInt32Parameter(tags.length+1, tags.length);
142      } else {
143        sqlList.push([
144          'GROUP BY b.rowid HAVING COUNT (b.rowid) = ?' + (tags.length + 1),
145          'ORDER BY b.added_date DESC'
146        ].join(" "));
147        sql = sqlList.join(" ");
148        st = dbc.createStatement(sql);
149        st.bindInt32Parameter(tags.length, tags.length);
150      }
151      for (let i in tags){
152        st.bindUTF8StringParameter(i, tags[i]);
153      }
154    }
155    while (st.executeStep()){
156      let url = st.getString(1);
157      list.push({
158        url: url,
159        name: st.getString(0),
160        note: st.getString(2),
161        icon: bookmarks.getFavicon(url),
162        tags: ydls.getTags(url, {})
163      });
164    }
165  } finally {
166    st.reset();
167    if (finalize) st.finalize();
168  }
169  return list;
170}
171function templateDescription(item){
172  return (item.tags && item.tags.length > 0 ? "[" + item.tags.join(",") + "]" : "") + item.note;
173}
174function templateTitleAndIcon(item){
175  let simpleURL = item.text.replace(/^https?:\/\//, '');
176  return <>
177    <span highlight="CompIcon">{item.icon ? <img src={item.icon}/> : <></>}</span><span class="td-strut"/>{item.name}<a href={item.text} highlight="simpleURL">
178      <span class="extra-info">{simpleURL}</span>
179    </a>
180  </>;
181}
182
183commands.addUserCommand(["delicious[search]","ds[earch]"], "Delicious Bookmark Search",
184  function(args){
185    if (args.length > 0){
186      liberator.open(args[0], liberator.CURRENT_TAB);
187      return;
188    }
189    let list = bookmarkSearch(args["-tags"], args["-query"]);
190    let xml = template.tabular(["Title","Tags and Note"], [], list.map(function(item){
191      return [
192        <><img src={item.icon}/><a highlight="URL" href={item.url}>{item.name}</a></>,
193        "[" + item.tags.join(",") + "] " + item.note
194      ];
195    }));
196    liberator.echo(xml, true);
197  },{
198    options: [
199      [["-tags","-t"], commands.OPTION_LIST, null, tagCompletion],
200      [["-query","-q"], commands.OPTION_STRING]
201    ],
202    completer: function(context, args){
203      context.format = {
204        anchored: true,
205        title: ["Title and URL", "Tags and Note"],
206        keys: { text: "url", name: "name", icon: "icon", tags: "tags", note: "note"},
207        process: [templateTitleAndIcon, templateDescription],
208      };
209      context.filterFunc = null;
210      context.regenerate = true;
211      context.generate = function() bookmarkSearch(args["-tags"], args["-query"]);
212    },
213  },true);
214
215let self = {
216  init: function(){
217    if (dbc){
218      try {
219        this.close();
220      } catch(e) {}
221    }
222    let file = getBookmarkFile();
223    if (!file) return;
224    dbc = ss.openDatabase(file);
225
226    statements.allTags = dbc.createStatement("SELECT name FROM tags");
227    statements.simpleQuery = dbc.createStatement([
228      'SELECT name,url,description FROM bookmarks',
229      'WHERE name like ?1 OR',
230      'url like ?1 OR',
231      'description like ?1',
232      'ORDER BY added_date DESC'
233    ].join(" "));
234  },
235  get tags(){
236    return getAllTags();
237  },
238  /**
239   * @see bookmarkSearch
240   */
241  search: function(tags, query){
242    return bookmarkSearch(tags, query);
243  },
244  /**
245   * used by completion
246   * :set complete+=D
247   * @param {CompletionContext} context
248   */
249  urlCompleter: function(context){
250    context.format = {
251      anchored: true,
252      title: ["Delicious Bookmarks"],
253      keys: { text: "url", name: "name", icon: "icon", tags: "tags", note: "note"},
254      process: [templateTitleAndIcon, templateDescription],
255    };
256    context.filterFunc = null;
257    context.regenerate = true;
258    context.generate = function() bookmarkSearch([], context.filter);
259  },
260  close: function(){
261    for each(let st in statements){
262      if (st.state > 0)
263        statements[key].finalize();
264    }
265    if (dbc.connectionReady)
266      dbc.close();
267  },
268};
269self.init();
270liberator.registerObserver("shutdown", self.close);
271completion.addUrlCompleter("D", "Delicious Bookmarks", self.urlCompleter);
272return self;
273})();
274function onUnload(){
275  liberator.plugins.delicious.close();
276}
277// vim: sw=2 ts=2 et:
Note: See TracBrowser for help on using the browser.