aboutsummaryrefslogtreecommitdiffstats
path: root/delicious_search.js
blob: fb5d2c976cde868f422376d779fa105c06a40187 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
1
/*
 * ==VimperatorPlugin==
 * @name            blinkelement.js
 * @description     blink specified elements.
 * @description-ja  指定した要素を点滅させる。
 * @author          janus_wel <janus_wel@fb3.so-net.ne.jp>
 * @version         0.31
 * @minversion      2.0pre 2008/10/16
 * ==/VimperatorPlugin==
 *
 * LICENSE
 *  New BSD License
 *
 * CONSTRAINT
 *  need highlight.js
 *
 * USAGE
 *  :blink {element[s] object}
 *  :bl
 *      -> blink specified element[s].
 *
 *  :noblink
 *  :nobl
 *      -> clear blink all elements.
 *
 * SETTING
 *  blink_element_interval: interval time. default is 800 msec.
 *  blink_element_color:    color for blink. default is red.
 *  blink_element_opacity:  opacity value. defualt is 0.5 .
 *
 * EXAMPLE
 *  let blink_element_interval='500'
 *  let blink_element_color='green'
 *  let blink_element_opacity='0.7'
 *
 *  :bl content.document.getElementsByTagName('A');
 *  :bl buffer.evaluateXPath('//a');
 *  :nobl
 * */

// use setTimeout to synchronize ( wait to process highlight.js )
// "liberator.modules.plugins.highlighterFactory" is build by highlight.js .
// it is the factory that build highlight object.
setTimeout( function () {

if (!plugins.highlighterFactory) {
    liberator.log('blinkelement.js needs highlight.js', 0);
    return;
}

// default settings
const defaultColor    = 'red';
const defaultOpacity  = 0.5;
const defaultInterval = 800;

let highlighter = liberator.modules.plugins.highlighterFactory({
    color:    liberator.globalVariables.blink_element_color    || defaultColor,
    opacity:  liberator.globalVariables.blink_element_opacity  || defaultOpacity,
    interval: liberator.globalVariables.blink_element_interval || defaultInterval,
});

// register commands
commands.addUserCommand(
    ['blink', 'bl'],
    'blink',
    function (args) {
        let arg = args.string;
        let element = liberator.eval(arg);

        if (!element) {
            liberatorlet PLUGIN_INFO =
<VimperatorPlugin>
<name>{NAME}</name>
<description>search DeliciousBookmark and that completer</description>
<require type="extension" id="{2fa4ed95-0317-4c6a-a74c-5f3e3912c1f9}">Delicious Bookmarks</require>
<require type="extension" id="delicious@vjkarunapg.com">Delicious Extension</require>
<author mail="teramako@gmail.com" homepage="http://vimperator.g.hatena.ne.jp/teramako/">teramako</author>
<version>0.3</version>
<minVersion>2.0pre</minVersion>
<maxVersion>4.0</maxVersion>
<detail><![CDATA[
== Command ==
:ds[earch] -tags tag, ...:
:delicious[search] -tags tag, ...:
  search bookmark contains all tags

:ds[earch] -query term:
:delicious[search] -query term:
  search bookmark contains term in the titile or the URL or the note

== Completion ==
:open or :tabopen command completion

>||
set complete+=D
||<

or write in RC file

>||
autocmd VimperatorEnter ".*" :set complete+=D
||<

== guioptions ==
show Delicious Toolbar or not
>||
set go+=D
or
set go-=D
||<

]]></detail>
</VimperatorPlugin>;

liberator.plugins.delicious = (function(){

const YDELS_CLASS = "@yahoo.com/nsYDelLocalStore;1";
let ydls = null;
if (YDELS_CLASS in Cc) {
  ydls = Cc[YDELS_CLASS].getService(Ci.nsIYDelLocalStore);
} else {
  return;
}
const ss = Cc["@mozilla.org/storage/service;1"].getService(Ci.mozIStorageService);

// dabase connection object
let dbc = null;

// sql statements
let statements = {
  allTags: null,
  simpleQuery: null,
};

/**
 * return Delicious bookmark batabase file
 * @return {nsIFile}
 */
function getBookmarkFile(){
  let file = services.get("directory").get("ProfD",Ci.nsIFile)
  file.append("ybookmarks.sqlite");
  if (!file.exists() || !file.isReadable()){
    return null;
  }
  return file;
}

/**
 * get all tag names from Delicious Bookmark
 * @return {String[]}
 */
function getAllTags(){
  let list = [];
  let st = statements.allTags;
  try {
    while (st.executeStep()){
      list.push(st.getString(0));
    }
  } finally {
    st.reset();
  }
  return list;
}
/**
 * @param {CompetionContext} context
 * @param {Array} args
 * @return {Array}
 */
function tagCompletion(context, args){
  let filter = context.filter;
  let have = filter.split(",");
  args.completeFilter = have.pop();
  let prefix = filter.substr(0, filter.length - args.completeFilter.length);
  let tags = getAllTags();
  return [[prefix + tag, tag] for ([i, tag] in Iterator(tags)) if (have.indexOf(tag)<0)];
}
/**
 * search Delicious Bookmarks
 * contains all tags and query in title or URL or Note
 * @param {String[]} tags
 * @param {String} query
 * @return {Array[]} [[url, title, note], ...]
 *         if both tags and query is none, returns []
 */
function bookmarkSearch(tags, query){
  if (!query && (!tags || tags.length == 0))
    return [];

  let sql;
  let list = [];
  let st;
  let finalize = true;
  try {
    if (!tags || tags.length == 0){
      st = statements.simpleQuery;
      st.bindUTF8StringParameter(0, '%' + query + '%');
      finalize = false;
    }  else {
      let sqlList = [
        'SELECT b.name,b.url,b.description',
        'FROM bookmarks b, bookmarks_tags bt, tags t',
        'WHERE bt.tag_id = t.rowid',
        'AND b.rowid = bt.bookmark_id',
        'AND t.name in (',
        ['?' + (parseInt(i)+1) for (i in tags)].join(","),
        ')'];
      if (query){
        let num = tags.length + 1;
        sqlList.push([
          'AND (',
          'b.name like', '?' + num,
          'OR b.url like', '?' + num,
          'OR b.description like', '?' + num,
          ')',
          'GROUP BY b.rowid HAVING COUNT (b.rowid) = ?' + (num + 1),
          'ORDER BY b.added_date DESC'
        ].join(" "));
        sql = sqlList.join(" ");
        st = dbc.createStatement(sql);
        st.bindUTF8StringParameter(tags.length, '%'+query+'%');
        st.bindInt32Parameter(tags.length+1, tags.length);
      } else {
        sqlList.push([
          'GROUP BY b.rowid HAVING COUNT (b.rowid) = ?' + (tags.length + 1),
          'ORDER BY b.added_date DESC'
        ].join(" "));
        sql = sqlList.join(" ");
        st = dbc.createStatement(sql);
        st.bindInt32Parameter(tags.length, tags.length);
      }
      for (let i in tags){
        st.bindUTF8StringParameter(i, tags[i]);
      }
    }
    while (st.executeStep()){
      let url = st.getString(1);
      list.push({
        url: url,
        name: st.getString(0),
        note: st.getString(2),
        icon: bookmarks.getFavicon(url),
        tags: ydls.getTags(url, {})
      });
    }
  } catch (e) {
    liberator.echoerr(e);
  } finally {
    st.reset();
    if (finalize) st.finalize();
  }
  return list;
}
function templateDescription(item){
  return (item.tags && item.tags.length > 0 ? "[" + item.tags.join(",") + "]" : "") + item.note;
}
function templateTitleAndIcon(item){
  let simpleURL = item.text.replace(/^https?:\/\//, '');
  return <>
    <span highlight="CompIcon">{item.icon ? <img src={item.icon}/> : <></>}</span><span class="td-strut"/>{item.name}<a href={item.text} highlight="simpleURL">
      <span class="extra-info">{simpleURL}</span>
    </a>
  </>;
}

commands.addUserCommand(["delicious[search]","ds[earch]"], "Delicious Bookmark Search",
  function(args){
    if (args.length > 0){
      liberator.open(args[0], liberator.CURRENT_TAB);
      return;
    }
    let list = bookmarkSearch(args["-tags"], args["-query"]);
    let xml = template.tabular(["Title","Tags and Note"], [], list.map(function(item){
      return [
        <><img src={item.icon}/><a highlight="URL" href={item.url}>{item.name}</a></>,
        "[" + item.tags.join(",") + "] " + item.note
      ];
    }));
    liberator.echo(xml, true);
  },{
    options: [
      [["-tags","-t"], commands.OPTION_LIST, null, tagCompletion],
      [["-query","-q"], commands.OPTION_STRING]
    ],
    completer: function(context, args){
      context.format = {
        anchored: true,
        title: ["Title and URL", "Tags and Note"],
        keys: { text: "url", name: "name", icon: "icon", tags: "tags", note: "note"},
        process: [templateTitleAndIcon, templateDescription],
      };
      context.filterFunc = null;
      context.regenerate = true;
      context.generate = function() bookmarkSearch(args["-tags"], args["-query"]);
    },
  },true);

let self = {
  init: function(){
    if (dbc){
      try {
        this.close();
      } catch(e) {}
    }
    let file = getBookmarkFile();
    if (!file) return;
    dbc = ss.openDatabase(file);

    statements.allTags = dbc.createStatement("SELECT name FROM tags");
    statements.simpleQuery = dbc.createStatement([
      'SELECT name,url,description FROM bookmarks',
      'WHERE name like ?1 OR',
      'url like ?1 OR',
      'description like ?1',
      'ORDER BY added_date DESC'
    ].join(" "));
  },
  get tags(){
    return getAllTags();
  },
  /**
   * @see bookmarkSearch
   */
  search: function(tags, query){
    return bookmarkSearch(tags, query);
  },
  /**
   * used by completion
   * :set complete+=D
   * @param {CompletionContext} context
   */
  urlCompleter: function(context){
    context.format = {
      anchored: true,
      title: ["Delicious Bookmarks"],
      keys: { text: "url", name: "name", icon: "icon", tags: "tags", note: "note"},
      process: [templateTitleAndIcon, templateDescription],
    };
    context.filterFunc = null;
    context.regenerate = true;
    context.generate = function() bookmarkSearch([], context.filter);
  },
  close: function(){
    for each(let st in statements){
      if (st.state > 0)
        st.finalize();
    }
    if (dbc.connectionReady)
      dbc.close();
  },
};
self.init();
liberator.registerObserver("shutdown", self.close);
config.toolbars.delicious = [['ybToolbar'], 'Delicious Toolbar'];
completion.addUrlCompleter("D", "Delicious Bookmarks", self.urlCompleter);
return self;
})();
function onUnload(){
  liberator.plugins.delicious.close();
}
// vim: sw=2 ts=2 et: