// ==UserScript==
// @name         del.icio.us + hatebu IncSearch
// @namespace    http://www.enjoyxstudy.com/
// @description  del.icio.us + hatebu incremental search.
// @include      http://www.enjoyxstudy.com/greasemonkey/incsearch/?mode=del.icio.us+hatebu&hatebuId=*
// @version      0.1
// ==/UserScript==
//
// ver 0.1   2007/01/08
//

(function () {
  var w = unsafeWindow;

  /////////////////////////////////////////////////////////////////////////
  // bookmark loader

  // base
  var BookmarkLoader = function(){};
  BookmarkLoader.prototype = {
    listKey: 'list',
    init: function(key, statusElement, loadingElement, callback) {
      this.key = key;
      this.statusElement  = statusElement;
      this.loadingElement = loadingElement;
      this.callback = callback || function(){};

      this.load();
    },
    load: function() {
      this.dispStart();

      this.bookmarks = new Array();
      this.total     = null;

      this._load(this.requestURI);
    },
    dispStart: function() {
      this.statusElement.innerHTML = 'Bookmarks Loading... (' + this.key + ')';
      this.loadingElement.style.display = 'inline';
    },
    dispLoading: function() {
      this.statusElement.innerHTML = 'Bookmarks Loading... (' + this.key + ') ' + this.bookmarks.length + '/' + this.total;
    },
    dispEnd: function() {
      this.statusElement.innerHTML = 'Finish!! (' + this.key + ') loaded ' + this.bookmarks.length + ' bookmarks';
      this.loadingElement.style.display = 'none';
    },
    saveCache: function() {
      var list = eval(GM_getValue(this.listKey));
      list = list || {};
      list[this.key] = {update: new Date(), count: this.bookmarks.length};
      GM_setValue(this.listKey, list.toSource());
      GM_setValue(this.key, {bookmarks: this.bookmarks}.toSource());
    },
    error: function(errMsg, url) {
      this.statusElement.innerHTML = errMsg
      this.loadingElement.style.display = 'none';

      if (confirm('retry?')) {
        this.dispStart();
        this._load(url);
      } else {
        this.callback(errMsg);
      }
    }
  }

  // del.icio.us
  var DeliciousLoader = w.Class.create();
  w.Object.extend(w.Object.extend(DeliciousLoader.prototype, BookmarkLoader.prototype), {
    initialize: function(statusElement, loadingElement, callback) {
      this.init('del.icio.us', statusElement, loadingElement, callback);
    },

    requestURI: 'http://del.icio.us/api/posts/all',
    userIdKey: 'userId',

    _load: function(url){
      var self = this;
      GM_xmlhttpRequest({
        method: 'get',
        url: url,
        onload: function(detail){
            if (detail.status != 200) {
              var errMsg = 'error :' + detail.status + ' :' + detail.statusText + ' :' + url;
              self.error(errMsg, url);
              return;
            }

            var wp = new XPCNativeWrapper(window, 'DOMParser()');
            var parser = new wp.DOMParser();
            var res = parser.parseFromString(detail.responseText, 'text/xml');

            var userId = res.evaluate('/posts/@user',
                                    res, null, XPathResult.STRING_TYPE, null).stringValue;

            var posts = res.getElementsByTagName('post');

            var postsLen = posts.length;
            self.total   = postsLen;

            for (var i = 0; i < postsLen; i++) {
              var post = {};
              post.url = posts[i].getAttribute('href');
              post.title = posts[i].getAttribute('description');
              if( posts[i].getAttribute('extended')) {
                post.info = posts[i].getAttribute('extended');
              }else{
                post.info = '';
              }

              post.tags = (posts[i].getAttribute('tag')).split(' ');

              post.others = new Array();
              post.others.push(posts[i].getAttribute('time'));

              self.bookmarks.push(post);
            }

            self.dispEnd();

            self.saveCache();
            GM_setValue(self.userIdKey, userId);

            self.callback();
            self.bookmarks = null;
          },
        onerror: function(detail){
            var errMsg = 'error :' + detail.status + ' :' + detail.statusText + ' :' + url;
            self.error(errMsg, url);
          }
      });
    }
  });

  // hatebu
  var HatebuLoader = w.Class.create();
  w.Object.extend(w.Object.extend(HatebuLoader.prototype, BookmarkLoader.prototype), {
    initialize: function(statusElement, loadingElement, callback, userId) {
      this.userId = userId;
      this.requestURI = 'http://b.hatena.ne.jp/' + this.userId + '/atomfeed';
      this.init('hatebu:' + userId, statusElement, loadingElement, callback);
    },

    _load: function(url){

      var self = this;
      GM_xmlhttpRequest({
        method: 'get',
        url: url,
        onload: function(detail){
            if (detail.status != 200) {
              var errMsg = 'error :' + detail.status + ' :' + detail.statusText + ' :' + url;
              self.error(errMsg, url);
              return;
            }

            var wp = new XPCNativeWrapper(window, 'DOMParser()');
            var parser = new wp.DOMParser();

            var res = parser.parseFromString(detail.responseText.replace(/[\x00-\x1F]|\7F/g,""), 'text/xml');

            // entrys
            var entrys = res.getElementsByTagName('entry');

            if(entrys.length == 0){
              //alert(detail.responseText);
              var errMsg = 'atomfeed error!!  ' + url;
              self.error(errMsg, url);
              return;
            }

            // total result
            if (self.total == null) {
              self.total = res.evaluate('//*[local-name()="totalResults" and namespace-uri()="http://a9.com/-/spec/opensearchrss/1.0/"]',
                                    res, null, XPathResult.STRING_TYPE, null).stringValue;
              if (self.total == '') {
                self.total = entrys.length;
              }
            }

            var entrysLen = entrys.length;
            for (var i = 0; i < entrysLen; i++) {
              var entry = entrys[i];
              var post = {};

              // url
              var links = entry.getElementsByTagName('link');
              for (var j = 0; j < links.length; j++) {
                if (links[j].getAttribute('rel') == 'related') {
                  post.url = links[j].getAttribute('href');
                  break;
                }
              }

              // title
              post.title = entry.getElementsByTagName('title')[0].childNodes.item(0).nodeValue;

              // info
              if (entry.getElementsByTagName('summary')[0].childNodes.length > 0) {
                post.info = entry.getElementsByTagName('summary')[0].childNodes.item(0).nodeValue;
              }

              // tags
              post.tags = new Array(self.key);

              var tags = entry.getElementsByTagNameNS('http://purl.org/dc/elements/1.1/', 'subject');

              var tagsLen = tags.length;
              for (var j = 0; j < tagsLen; j++) {
                post.tags.push(tags[j].childNodes.item(0).nodeValue);
              }

              // others(time)
              post.others = new Array();
              post.others.push(entry.getElementsByTagName('issued')[0].childNodes.item(0).nodeValue);
              self.bookmarks.push(post);
            }

            self.dispLoading();

            // next
            var next = res.evaluate('//*[local-name()="link" and namespace-uri()="http://purl.org/atom/ns#"][@rel="next"]/@href',
                                    res, null, XPathResult.STRING_TYPE, null).stringValue;

            if (next != '') {
              self._load(next);
            } else {
              self.dispEnd();

              self.saveCache();

              self.callback();
              self.bookmarks = null;
            }
          },
        onerror: function(detail){
            var errMsg = 'error :' + detail.status + ' :' + detail.statusText + ' :' + url;
            self.error(errMsg, url);
          }
      }); 
    }
  });

  /////////////////////////////////////////////////////////////////////////

  var BookmarkSearch = w.Class.create();
  w.Object.extend(w.Object.extend(BookmarkSearch.prototype, w.IncSearch.ViewBookmark.prototype), {
    setOptions: function(options) {

      (w.IncSearch.ViewBookmark.prototype.setOptions).apply(this, [options]);

      this.userId = options.userId;
    },
    createOthersElement: function(post, patternList) {
      var text = (w.IncSearch.ViewBookmark.prototype.createOthersElement).apply(this, [post, patternList]);

      var editUrl = 'http://del.icio.us/' + this.userId;
      editUrl += '?noui=yes&jump=close&v=4&url='+escape(post.url);
      editUrl += '&title='+escape(post.title);

      text += '<td><a href="' +  editUrl + '"';
      text += " onclick=\"window.open('" + editUrl + "', 'delicious_edit', 'toolbar=no, width=700, height=400'); return false;\"";
      text += " onkeypress=\"var event = event || window.event; if (event.keyCode == Event.KEY_RETURN) {window.open('" + editUrl + "'); return false;}\">";
      text += 'edit';
      text += '</a></td>';

      return text;
    }
  });

  /////////////////////////////////////////////////////////////////////////

  var BookmarkManager = w.Class.create();
  BookmarkManager.prototype = {
    initialize: function(hatebuIds) {
      this.hatebuIds = hatebuIds;
      this.status    = document.getElementById('status');
      this.loading   = document.getElementById('loading');

      var updateHTML = 'update: <a href="javascript:void(0)" id="update">del.icio.us</a>';
      for (var i = 0; i < this.hatebuIds.length; i++) {
        updateHTML += '&nbsp;&nbsp;<a href="javascript:void(0)" id="update_' + this.hatebuIds[i] + '">hatebu:' + this.hatebuIds[i] + '</a>';
      }

      document.getElementById('option').innerHTML = updateHTML;

      var self = this;
      document.getElementById('update').addEventListener(
        'click',
        function(){ 
          new DeliciousLoader(self.status, self.loading, function(err){
            if (err) throw err;
            self.load(); 
           });
        },
        false);

      for (var i = 0; i < this.hatebuIds.length; i++) {
        this.updateLinkHatebu(this.hatebuIds[i]);
      }

    },
    updateLinkHatebu: function(hatebuId) {
      var id   = 'update_' + hatebuId;
      var self = this;

      document.getElementById(id).addEventListener(
        'click',
        function(){ 
        new HatebuLoader(self.status, self.loading, function(err){
            if (err) throw err;
            self.load(); 
          }, hatebuId);
        },
        false);
    },
    load: function() {
      var self = this;
      var bookmarks = [];

      var cache = eval(GM_getValue('del.icio.us'));
      if (cache) {
        bookmarks = bookmarks.concat(cache.bookmarks);
      } else {
        new DeliciousLoader(this.status, this.loading, function(err){
            if (err) throw err;
            self.load(); 
          });
        return;
      }

      for (var i = 0; i < this.hatebuIds.length; i++) {
        cache = eval(GM_getValue('hatebu:' + this.hatebuIds[i]));
        if (cache) {
          bookmarks = bookmarks.concat(cache.bookmarks);
        } else {
          new HatebuLoader(this.status, this.loading, function(err){
              if (err) throw err;
              self.load(); 
            }, this.hatebuIds[i]);
          return;
        }
      }

      this.createIncSearch(bookmarks); 
    },
    createIncSearch: function(bookmarks) {
      var userId = GM_getValue(DeliciousLoader.prototype.userIdKey);

      if (!this.incsearch) {
        this.incsearch = w.createIncSearch(
                            BookmarkSearch,
                            bookmarks,
                            {
                              userId: userId,
                              tagBracket: true,
                              header: '<tr><th width="60%">Description</th><th width="20%">Tags</th><th width="20%">Time</th><th></th>'
                            });
      } else {
        this.incsearch.searchValues = bookmarks;
        this.incsearch.oldInput = null;
      }
    }
  };

  var href = window.location.href;
  var index = href.indexOf('&hatebuId=');
  var userIds = [];
  if (index != -1) {
    var userIdString = href.substr(index + '&hatebuId='.length);
    if (userIdString.length > 1) {
      userIds = userIdString.split(',');
    }
  }

  document.getElementById('title').innerHTML = 'del.icio.us+hatebu IncSearch';
  document.title = 'del.icio.us+hatebu IncSearch';

  var manager = new BookmarkManager(userIds);
  manager.load();

})();
