// ? Cross-Domain Summary, released under the GPL
// http://www.gnu.org/copyleft/gpl.html
//
// ==UserScript==
// @name           Hatena Cross-Domain Summary
// @description    Put a small button for each "diary that refers the URL". Click it to get a snippet of the diary.
// @include        http://b.hatena.ne.jp/entry/*://*
// ==/UserScript==
//
// 2008-11-25 (v0.5) Added support for long user IDs (http://hatena.g.hatena.ne.jp/hatena/20070705/1183619934) and modifications to support the new site design (http://hatena.g.hatena.ne.jp/hatenabookmark/20081125/1227602284).
// 2006-08-31 (v0.4) Added modifications to follow today's change (http://d.hatena.ne.jp/hatenadiary/20060831/1157021294).
// 2006-08-18 (v0.3) Added modifications to follow the change of the RSS format.
// 2006-07-12 (v0.2) Fixed a bug in processing '#' and '&' within URLs.
// 2006-06-28 (v0.1) Initial Version.

(function () {
  const IMAGE_PATHS = { closed: 'http://d.hatena.ne.jp/images/summary_closed.gif', opened: 'http://d.hatena.ne.jp/images/summary_opened.gif', loading: 'http://d.hatena.ne.jp/images/summary_loading.gif', error: 'http://d.hatena.ne.jp/images/summary_error.gif' };
  const CLASS_NAMES = { base: 'diary-summary', status: 'diary-status', hidden: 'diary-hidden', word: 'highlight' };
  
  const referrer = document.evaluate('/descendant::UL[contains(string(@class), "reldiary")]/LI', document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  const referred = location.pathname.substr('/entry/'.length).concat(location.search).replace(/^([^%]+)%23([^%]+)$/, '$1#$2');

  String.prototype.appendTerm = function (term) {
    return this.indexOf(term) < 0 ? [this, term].join(' ') : this;
  };

  String.prototype.removeTerm = function (term) {
    return this.replace(new RegExp('\\b' + term + '\\b', 'g'), '');
  };
  
  const DiarySummary = function (node) {
    this.initialize(node);
  };

  DiarySummary.prototype = {
    initialize: function (node) {
      this.node = node;
      this.button = this.createButton();
      this.container = this.createContainer();
      this.opened = false;
      this.loaded = false;
      
      this.update();
      
      node.appendChild(this.button);
      node.appendChild(this.container);
    },

    createButton: function () {
      var node = document.createElement('IMG');
      node.className = CLASS_NAMES.base;
      node.addEventListener('click', this.createHandler(this, this.toggle), false);
      return node;
    },

    createContainer: function () {
      var node = document.createElement('DIV');
      node.className = CLASS_NAMES.base;
      return node;
    },

    createHandler: function (object, method) {
      return function (e) {
        return method.call(object, e);
      }
    },

    toggle: function () {
      this.opened = !this.opened;
      this.update();
    },

    update: function () {
      if (this.opened) {
        if (!this.loaded) {
          this.loadContent();
        }
        
        this.button.src = IMAGE_PATHS.opened;
        this.container.className = this.container.className.removeTerm(CLASS_NAMES.hidden);
      } else {
        this.button.src = IMAGE_PATHS.closed;
        this.container.className = this.container.className.appendTerm(CLASS_NAMES.hidden);
      }
    },

    loadContent: function () {
      var link = this.node.getElementsByTagName('A')[0];
      var path = link.pathname.match(/([-_a-zA-Z0-9]{3,32})\/(\d{8})/);
      var feed = link.protocol + '//' + link.host + '/' + path[1] + '/rss?date=' + path[2] + '&word=' + encodeURIComponent(referred);

      GM_xmlhttpRequest({
        method: 'GET',
        url: feed,
        onload: this.createHandler(this, this.onload),
        onerror: this.createHandler(this, this.error),
      });
      
      this.showContent('\u8aad\u307f\u8fbc\u307f\u4e2d...', IMAGE_PATHS.loading);
    },

    showContent: function (text, icon) {
      if (icon) {
        this.container.className = this.container.className.appendTerm(CLASS_NAMES.status);
        this.container.innerHTML = '<img src="' + icon + '">' + text;
      }
      else {
        this.container.className = this.container.className.removeTerm(CLASS_NAMES.status);
        this.container.innerHTML = text;
      }
    },
    
    onload: function (e) {
      var text = e.responseText.match(/<item[^>]*>\s*<title>(.*)<\/title>\s*<link>.*<\/link>\s*<description>.*<\/description>\s*(?:<content:encoded><!\[CDATA\[(.*)\]\]><\/content:encoded>\s*)?<dc:creator>.*<\/dc:creator>\s*<dc:date>.*<\/dc:date>\s*(?:<dc:subject>.*<\/dc:subject>\s*)*<\/item>/m);

      if (!text) {
        this.onerror(e); return;
      }

      this.loaded = true;

      text.shift();
      text = text.join('\t').replace(/<\/?[^>]+>/g, '');

      var referredRE = referred.replace(/\W/g, '\\$&').replace(/\\&/g, '\\&(\\#38|amp)\\;');
      var word = text.match(new RegExp('.{0,200}' + referredRE + '.{0,200}', 'ig'));

      text = word ? word.join('\t') : text.substr(0, 400);
      text = text.replace(new RegExp(referredRE, 'ig'), '<span class="' + CLASS_NAMES.word + '">$&</span>');
      text = text.replace('\t', '<br>');
      
      this.showContent(text);
    },

    onerror: function (e) {
      this.showContent('\u5185\u5bb9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff08' + e.status + '\uff09', IMAGE_PATHS.error);
    },
  };
  
  GM_addStyle('img.' + CLASS_NAMES.base + '{ cursor: pointer; margin: 0 5px; vertical-align: middle; }'+'div.' + CLASS_NAMES.base + '{ color: #000000; background-color: #edf1fd; font-size: 90%; margin: 5px 5px 10px; padding: 5px; -moz-border-radius: 5px; }'+'div.' + CLASS_NAMES.status + '{ text-align: center; }'+'div.' + CLASS_NAMES.hidden + '{ display: none; }'+'div.' + CLASS_NAMES.base+' '+'span.' + CLASS_NAMES.word + ',div.' + CLASS_NAMES.base + ' ' + 'img' + '{ color: #000000; font-weight: bolder; margin: 0 1ex; }');

  for (var i = 0; i < referrer.snapshotLength; ++i) {
    new DiarySummary(referrer.snapshotItem(i));
  }
})()

