// ==UserScript==
// @name        Dokodemo Keyword Search for Hatena Haiku
// @description どこからでもキーワード検索ができるようにする
// @namespace   http://d.hatena.ne.jp/gan2/
// @include     http://h.hatena.ne.jp/*
// @include     http://h.hatena.com/*
// @version     0.1.1
// ==/UserScript==

(function () {
  // キーワード用のサーチボックスがないとき
  if ($X('//form[@class="search-keyword"]/input[@name="word"]').length == 0) {
    var boxes = $X('//div[@id="rightbar"]/div[@class="box"]');
    boxes.forEach(function(box) {
      var list_keyword = $X('./div[@class="box-body"]/ul[@class="list-keyword"]', box)[0];
      if (list_keyword) {
        var li = $N('li');
        li.innerHTML = '<form action="/keywords" class="search-keyword" method="get"><input name="word" value="" class="text" type="text"><input class="submit" value="Search" type="submit"></form>';
        list_keyword.insertBefore(li, list_keyword.firstChild);
      }
    });
  }


  function $ (id) {
    return document.getElementById(id);
  }

  function clog (s) {
    unsafeWindow.console.log(s);
  }

  /**** template functions from lowreal.net ****/
  function h (s) {
    var d = document.createElement("div");
    d.innerHTML = s;
    return d;
  }

  function log (m) {
    //    var c = unsafeWindow.console;
    //    if (c) c.log.apply(c, arguments);
    var o = Array.prototype.concat.apply([], arguments);
    if (window.console) {
      window.console.log(o.join(", "));
    } else if (GM_log) {
      GM_log(o);
    } else {
      location.href = "javascript:(function () { if (window.console) console.log.apply(console.log, "+o.toSource()+") })();";
    }

    if (!arguments.callee.element) {
      arguments.callee.element = h("<div style='position:fixed;top:0;left:0;background:#000;color:#fff;padding:0 0.5em;'/>").firstChild;
      document.body.appendChild(arguments.callee.element);
    }
    arguments.callee.element.innerHTML = escapeHTML(m);
  }

  function $N(name, attr, childs) {
    var ret = document.createElement(name);
    for (var k in attr) {
      if (!attr.hasOwnProperty(k)) continue;
      var v = attr[k];
      if (k == "class") {
        ret.className = v;
      } else {
        ret.setAttribute(k, v);
      }
    }
    switch (typeof childs) {
    case "string": {
      ret.appendChild(document.createTextNode(childs));
      break;
    }
    case "object": {
      for (var i = 0, len = childs.length; i < len; i++) {
        var child = childs[i];
        if (typeof child == "string") {
          ret.appendChild(document.createTextNode(child));
        } else {
          ret.appendChild(child);
        }
      }
      break;
    }
    }
    return ret;
  }

  function escapeHTML (str) {
    str = String(str);
    var map = { "&" : "&amp;", "<" : "&lt;", ">" : "&gt;"};
    return str.replace(/[&<>]/g, function (m) {
      return map[m];
    });
  }

  // extend version of $X
  // $X(exp);
  // $X(exp, context);
  // $X(exp, type);
  // $X(exp, context, type);
  function $X (exp, context, type /* want type */) {
    if (typeof context == "function") {
      type    = context;
      context = null;
    }
    if (!context) context = document;
    exp = (context.ownerDocument || context).createExpression(exp, function (prefix) {
      return document.createNSResolver((context.ownerDocument == null ? context : context.ownerDocument).documentElement)
                     .lookupNamespaceURI(prefix) ||
             ({ "atom" : "http://purl.org/atom/ns#", "hatena" : "http://www.hatena.ne.jp/info/xmlns#" })[prefix] ||
             document.documentElement.namespaceURI;
    });

    switch (type) {
    case String:
      return exp.evaluate(
        context,
        XPathResult.STRING_TYPE,
        null
      ).stringValue;
    case Number:
      return exp.evaluate(
        context,
        XPathResult.NUMBER_TYPE,
        null
      ).numberValue;
    case Boolean:
      return exp.evaluate(
        context,
        XPathResult.BOOLEAN_TYPE,
        null
      ).booleanValue;
    case Array:
      var result = exp.evaluate(
        context,
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
        null
      );
      var ret = [];
      for (var i = 0, len = result.snapshotLength; i < len; i++) {
        ret.push(result.snapshotItem(i));
      }
      return ret;
    case undefined:
      var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
      switch (result.resultType) {
      case XPathResult.STRING_TYPE : return result.stringValue;
      case XPathResult.NUMBER_TYPE : return result.numberValue;
      case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
      case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: {
        // not ensure the order.
        var ret = [];
        var i = null;
        while (i = result.iterateNext()) {
          ret.push(i);
        }
        return ret;
      }
      }
      return null;
    default:
      throw TypeError("$X: specified type is not valid type.");
    }
  }
})();
