RE:Big Sky :: Google Readerでもgをぺちって全部全文読んじゃおう。

魔改造
自分はGoogle Readerはてブのコメントで概要を見てから全文取得して読んでいて、
z押してから情報をロードされるのがタルかったので修正。
コメント読んでる間に全文取得されるので見たい記事でzかアイコンを押せばすぐに表示されるようにしてみた。
適当過ぎる変更をしたので動作怪しいかも。

  1. 裏で常にオートロードしておいて、ボタンかzが押されたら表示を切り替えるようにした
  2. オートロードの設定を変更しないように
  3. フラッシュメッセージ非表示
  4. livedoorクリップはてなブックマーク数のアイコン非表示
  5. 11/24 取得した本文をhtml内に埋め込んでおくようにした
  6. 11/24 本文のロードが終わってからアイコンを表示するように
// ==UserScript==
// @name        Google Reader Full Feed
// @namespace   tag:mattn.jp@gmail.com,2008-02-25:/coderepos.org
// @include     http://www.google.tld/reader/*
// @include     https://www.google.tld/reader/*
// @description loading full entry on Google Reader
// @privilege   false
// @version     0.1.8
// ==/UserScript==
// based on: LDR Full Feed <http://d.hatena.ne.jp/toshi123>
// thanks:
//   * autoloading full feed
//     http://d.hatena.ne.jp/ToMmY/20080311
//
// author: mattn <mattn.jp@gmail.com>

(function(w) {
// == [CSS] =========================================================
const CSS = <><![CDATA[
  .gm_fullfeed_loading, .gm_fullfeed_loading a { color : green !important; }
  .gm_fullfeed_loading .item_body a { color : palegreen !important; }
  .gm_fullfeed_loading { background-color : Honeydew !important; }
]]></>.toString();

// == [Icon] ========================================================
const ICON = <><!-- http://api.feed-media.com/img/getcontentsmark.gif
--><![CDATA[data:image/gif;base64,
R0lGODdhEwATAPMAMf+MAP+lAP+lOv+0AP+0kP/EAP/Etv/TOv/hZv/x2//x////tv//2////wAA
AAAAACwAAAAAEwATAAAETBDISWsNOOuNJf+aB4LiyJUZ0awNsqGB0RSYkAwhoAnKYaIEBm4EXJgC
QGFA1VBmUDwfJjjs6DQy2tJp5TBX0uf1mCO/xmYk2mxptyMAOw==
]]></>.toString()
      .replace(/\s+/g, "");
const ICON2 = <><!-- (c) id:Constellation --><![CDATA[data:image/gif;base64,
R0lGODdhEwATAPQAMUFp4YfO64fO7ofW9Yfe+LHO68XW68X3/MX3/9ne6+zn7uz/+Oz//Oz////v
8v///P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAA
EwATAAAFUCAgjmRZBmiqrqjIvqoLw/LM1unQQLyz4gECJIESHAwxgEqAUNhwAwZyBl0UnsqcNKCD
PKatbLGpBQeAQiJ3mwJydzxn0vZy0+1Y+s3EN4UAADs=
]]></>.toString()
      .replace(/\s+/g, "");

// == [Config] ======================================================

//const KEY = 'g';
const ADCHECKER = /^(?:AD|PR):/;
const LOADING_MOTION = false;
const REMOVE_SCRIPT = true;
const REMOVE_H2TAG = false;
const OPEN = false; //SITEINFOになかった場合にそのエントリを開くかどうか。
const AUTO_SEARCH = false; // TODO: 導入を検討する
const WIDGET = true;
const SITEINFO_IMPORT_URLS = [
  { format: 'JSON', url: 'http://wedata.net/databases/LDRFullFeed/items.json' },
  { format: 'HTML', url: 'http://constellation.jottit.com/siteinfo' }
];
const FLASH_MESSAGE = false;

// == [SITE_INFO] ===================================================

const SITE_INFO = [
];

// == [Application] =================================================

var FullFeed = function(info, c) {
  this.itemInfo = c;
  this.info = info;
  this.requestURL = this.itemInfo.itemURL;
  var bodyXPath = 'id("current-entry")//div[contains(concat(" ", @class, " "), " entry-body ")]';
  this.itemInfo.item_body = getFirstElementByXPath(bodyXPath);
  this.state = 'wait';
  this.mime = 'text/html; charset=' + (this.info.enc || document.characterSet);

  this.request();
  return this;
};

FullFeed.prototype.request = function() {
  if (!this.requestURL) {
    return
  }
  this.state = 'request';
  var self = this;
  var opt = {
    method: 'get',
    url: this.requestURL,
    overrideMimeType: this.mime,
    headers: {
      "User-Agent": navigator.userAgent + " Greasemonkey (Google Reader Full Feed)"
    },
    onerror: function() {
      self.requestError.apply(self, ['Request Error']);
    },
    onload: function(res) {
      if (res.status == 302) {
        try {
          opt.url = res.responseHeaders.match(/\sLocation:\s+(\S+)/)[1]; //i
          window.setTimeout(GM_xmlhttpRequest, 0, opt);
          return;
        } catch (e) {
          message(e);
        }
      }
      self.requestLoad.apply(self, [res]);
    }
  };
  message('Loading Full Feed...');
  w.toggleClass(this.itemInfo.item_container, 'gm_fullfeed_loading');
  if (opt.url.indexOf("http:") != 0) {
    opt.url = pathToURL(this.info.base, opt.url);
  }
  window.setTimeout(GM_xmlhttpRequest, 0, opt);
};

FullFeed.prototype.requestLoad = function(res) {
  this.state = 'loading';
  var text = res.responseText;

  text = text.replace(/(<[^>]+?[\s"'])on(?:(?:un)?load|(?:dbl)?click|mouse(?:down|up|over|move|out)|key(?:press|down|up)|focus|blur|submit|reset|select|change)\s*=\s*(?:"(?:\\"|[^"])*"?|'(\\'|[^'])*'?|[^\s>]+(?=[\s>]|<\w))(?=[^>]*?>|<\w|\s*$)/gi,
    "$1");
  if (REMOVE_SCRIPT) text = text.replace(/<script(?:\s[^>]+?)?>[\S\s]*?<\/script\s*>/gi, "");
  if (REMOVE_H2TAG)  text = text.replace(/<h2(?:\s[^>]+?)?>[\S\s]*?<\/h2\s*>/gi, "");
  var htmldoc = parseHTML(text);
  removeXSSRisks(htmldoc);
  if (this.info.base && !this.itemInfo.itemURL.match(this.info.base)) {
    relativeToAbsolutePath(htmldoc, this.info.base);
  } else {
    relativeToAbsolutePath(htmldoc, this.itemInfo.itemURL);
  }
  var self = this;
  FullFeed.documentFilters.forEach(function(filter) {
    filter(htmldoc, self.itemInfo.itemURL, this.info);
  });

  var doc = null;
  try {
    doc = getElementsByXPath(this.info.xpath, htmldoc);
  } catch (e) {
    message(e);
    return;
  }

  if(AUTO_SEARCH && !doc){
    message('Loading Full Feed ... Auto Search');
    doc = searchEntry(htmldoc);
  }

  if(doc){

    var container = getFirstElementByXPath('id("current-entry")//div[contains(concat(" ", @class, " "), " item-body ")]');

    var entry = document.createElement('div');

	var temp = '';
	for (var i in doc) {
	  if(doc[i].innerHTML != undefined){
 	  	temp += doc[i].innerHTML;
	  }
	}

    entry.setAttribute('value', temp);
    entry.setAttribute('class', 'gr-all-entry');
    container.appendChild(entry);

    var icon = getFirstElementByXPath('id("current-entry")//div[contains(concat(" ", @class, " "), " gm_fullfeed_checked ")]');
	if(icon){
	  icon.removeAttribute('hidden')
	}
  }
};


FullFeed.prototype.loadEntry = function(){

  var container = getFirstElementByXPath('id("current-entry")//div[contains(concat(" ", @class, " "), " gr-all-entry ")]/@value');

  if (container) {
    this.removeEntry();

    var body = getFirstElementByXPath('id("current-entry")//div[contains(concat(" ", @class, " "), " entry-body ")]');
	var div = document.createElement('div');

    var entry = document.createElement('div');

    entry.innerHTML = container.textContent;
    div.appendChild(entry);
    body.appendChild(div);
    this.requestEnd();
  } else {
    this.requestError('This SITE_INFO is unmatched to this entry');
  }

  if (this.info.base) {
    w.addClass(this.itemInfo.item_container, this.info.base);
  } else {
    w.addClass(this.itemInfo.item_container, this.itemInfo.itemURL);
  }
}

FullFeed.prototype.requestEnd = function() {
  this.state = 'loaded';
  message('Loading Full Feed... Done');
  w.toggleClass(this.itemInfo.item_container, 'gm_fullfeed_loading');
  w.addClass(this.itemInfo.item_container, 'gm_fullfeed_loaded');
};

FullFeed.prototype.requestError = function(e) {
  this.state = 'error';
  message('Error : ' + e);
  w.toggleClass(this.itemInfo.item_container, 'gm_fullfeed_loading');
  w.addClass(this.itemInfo.item_container, 'gm_fullfeed_error');
};

FullFeed.prototype.removeEntry = function() {
  while (this.itemInfo.item_body.firstChild) {
    this.itemInfo.item_body.removeChild(this.itemInfo.item_body.firstChild);
  }
};

FullFeed.prototype.addEntry = function(entry) {
  var self = this;
  return entry.map(function(i) {
    var pe = document.importNode(i, true);
    self.itemInfo.item_body.appendChild(pe);
    return pe;
  });
};

FullFeed.parser = function(text) {
  var lines = text.split(/[\r\n]+/);
  var reg = /^([^:]+):(.*)$/;
  var trimspace = function(str) {
    return str.replace(/^\s+|\s+$/g, '');
  };
  var info = {};
  lines.forEach(function(line) {
    if (reg.test(line)) {
      info[RegExp.$1] = trimspace(RegExp.$2);
    }
  });
  var isValid = function(info) {
    var infoProp = ['url', 'xpath'];
    if (infoProp.some(function(prop) {
      return !info[prop];
    })) return false;
    try {
      new RegExp(info.url);
    } catch (e) {
      return false;
    }
    return true;
  };
  return isValid(info) ? info : null;
};

FullFeed.resetCache = function() {
  message('Resetting cache. Please wait...');
  SITEINFO_IMPORT_URLS.forEach(function(ctx) {
    var opt = {
      method: 'get',
      url: ctx.url,
      headers: {
        "User-Agent": navigator.userAgent + " Greasemonkey (Google Reader Full Feed)"
      },
      onload: function(res) {
        FullFeed.setCache(res, ctx);
      },
      onerror: function(res) {
        message('Cache Request Error');
      }
    };
    window.setTimeout(GM_xmlhttpRequest, 0, opt);
  });
};

FullFeed.setCache = function(res, ctx) {
  var info = [];
  switch (ctx.format.toUpperCase()) {
    case 'JSON':
      info = eval(res.responseText)
        .map(function(i) { return i.data })
        .sort(function(lhs, rhs) { return rhs.priority - lhs.priority });
      break;
    case 'HTML':
    default:
      var doc = parseHTML(res.responseText);
      var lists =
        getElementsByXPath('//textarea[@class="ldrfullfeed_data"]', doc);
      lists.forEach(function(list) {
        var data = FullFeed.parser(list.value);
        if (data) {
          info.push(data);
        }
      });
      break;
  }
  if (info.length > 0) {
    cacheInfo[ctx.url] = {
      url: ctx.url,
      info: info
    };
    GM_setValue('cache', cacheInfo.toSource());
    if (WIDGET) FullFeed.createPattern();
    message('Resetting cache. Please wait... Done');
  }
};


FullFeed.getCache = function(key, oDefault) {
  return eval(GM_getValue(key || 'cache', oDefault || "({})"));
};

FullFeed.createPattern = function() {
  if (!WIDGET) return;
  var exps = [];
  SITE_INFO.forEach(function(info) {
    exps.push(info.url);
  });
  for each (var i in cacheInfo) {
    i.info.forEach(function(info) {
      exps.push(info.url);
    });
  }
  pattern = exps.join('|');
};

FullFeed.registerWidgets = function() {
  if (!WIDGET) return;
  var container = getFirstElementByXPath('id("current-entry")//a[contains(concat(" ", @class, " "), " entry-title-link ")]').parentNode;
  if (!container) return;

  FullFeed.createPattern();

  var c = new getCurrentItem();
  var item = c.item;
  var feed = c.feed;
  if (item.link.match(pattern) || feed.channel.link.match(pattern)) {
	var ff = null;
    if (autoLoad){
		ff = init();
		document.addEventListener('keyup', function(e) {
		  if (e.keyCode == w.KeyEvent.DOM_VK_Z) {
		//    if (e.ctrlKey) {
		//        autoLoad = !autoLoad;
		//        message('AutoLoad: ' + (autoLoad ? 'on' : 'off'));
		//    } else
		    if (e.shiftKey)
		      FullFeed.resetCache();
		    else
			ff.loadEntry();
		  }
		}, false);

	};

  var description = "\u5168\u6587\u53d6\u5f97\u3067\u304d\u308b\u3088\uff01";
    icon = document.createElement('span');
    icon.title = description;
    icon.innerHTML = '<img src="'+ICON+'" style="cursor: pointer;">';
    icon.addEventListener('click', function(event){ff.loadEntry();}, false);
    icon.setAttribute('hidden', true);
    w.addClass(icon, 'gm_fullfeed_checked');
    container.appendChild(document.createTextNode(' '));
    container.appendChild(icon);
  }
};

FullFeed.registerSbmIcons = function() {
  if (!WIDGET) return;
  var container = getFirstElementByXPath('id("current-entry")//a[contains(concat(" ", @class, " "), " entry-title-link ")]').parentNode;
  if (!container) return;

  var c = new getCurrentItem();
  icon = document.createElement('span');
  icon.innerHTML = ''
    + '&nbsp;<img src="http://b.hatena.ne.jp/entry/image/' + c.item.link + '" title="\u306f\u3066\u306a\u30d6\u30c3\u30af\u30de\u30fc\u30af"/>'
    + '&nbsp;<img src="http://image.clip.livedoor.com/counter/' + c.item.link + '" title="livedoor \u30af\u30ea\u30c3\u30d7"/>'
  container.appendChild(icon);
};

FullFeed.documentFilters = [
// addTargetAttr
(function(doc) {
  var anchors = getElementsByXPath('descendant-or-self::a', doc);
  if (anchors) {
    anchors.forEach(function(i) {
      i.target = '_blank';
    });
  }
}),
];

FullFeed.preFilters = [
  FullFeed.registerWidgets,
  //FullFeed.registerSbmIcons,
];

FullFeed.filters = [];

window.FullFeed = {};

window.FullFeed.addDocumentFilter = function(f) {
  FullFeed.documentFilters.push(f);
};

window.FullFeed.addPreFilter = function(f) {
  FullFeed.preFilters.push(f);
};

window.FullFeed.addFilter = function(f) {
  FullFeed.filters.push(f);
};

w.get_active_item = function(flag) {
  var item = {};
  var exp = 'id("current-entry")//a[contains(concat(" ", @class, " "), " entry-title-link ")]';
  try {
    item.link =  getFirstElementByXPath(exp).href;
    item.title = getFirstElementByXPath(exp).textContent;
  } catch (e) {}
  return item;
};

w.get_active_feed = function() {
  var feed = {};
  feed.channel = {};
  try {
    feed.channel.link = decodeURIComponent(getFirstElementByXPath('id("current-entry")//a[contains(concat(" ", @class, " "), " entry-source-title ")]').href.replace(/^.*\/(?=http)/, ''));
  } catch (e) {}
  return feed;
};

var hasClass = w.hasClass = function(element, classname) {
  var cl = element.className;
  var cls = cl.split(/\s+/);
  return cls.indexOf(classname) != -1;
};

var toggleClass = w.toggleClass = function(element, classname) {
  hasClass(element, classname) ? removeClass(element, classname) : addClass(element, classname);
};

var removeClass = w.removeClass = function(element, classname) {
  var cl = element.className;
  var cls = cl.split(/\s+/);
  element.className = cls.remove(classname).join(" ");
};

var addClass = w.addClass = function(element, classname) {
  var cl = element.className;
  if (!contain(cl, classname)) {
    element.className += " " + classname;
  }
};

var contain = w.contain = function(self, other) {
  if (isString(self) && isString(other)) {
    return self.indexOf(other) != -1;
  }
  if (isRegExp(other)) {
    return other.test(self);
  }
};

var isString = w.isString = function(obj) {
  return typeof obj == "string" || obj instanceof String;
};

var isRegExp = w.isRegExp = function(obj) {
  return obj instanceof RegExp;
};

Array.prototype.remove = function(to_remove) {
  return this.filter(function(val) { return val != to_remove; });
};

// itemの情報を格納するobjectのconstructor
var getCurrentItem = function() {
  this.item = w.get_active_item(true);
  this.feed = w.get_active_feed();
  this.itemURL = this.item.link;
  this.feedURL = this.feed.channel.link;
  this.id = this.item.id;
  this.item_container = getFirstElementByXPath('id("current-entry")//div[contains(concat(" ", @class, " "), " entry-body ")]');
  this.title = this.item.title;
  this.found = false;
};

var launchFullFeed = function(list, c) {
  var ff = null;
  if (typeof list.some != "function") return;
  list.some(function(i) {
    var reg = new RegExp(i.url);
    if (reg.test(c.itemURL) || reg.test(c.feedURL)) {
      c.found = true;
      ff = new FullFeed(i, c);
    } else {
    }
  });
  return ff;
};

var init = function() {
  var c = new getCurrentItem();
  if (!c.title) return;
  if (ADCHECKER.test(c.title)) {
    return message('This entry is advertisement');
  }
  if (w.hasClass(c.item_container, 'gm_fullfeed_loaded')) {
    return message('This entry has been already loaded.');
  }

  var ff = launchFullFeed(SITE_INFO, c);

  if (!c.found && !SITEINFO_IMPORT_URLS.some(function(ctx) {
    ff = launchFullFeed(cacheInfo[ctx.url].info, c);
    return c.found;
  })) {
    message('This entry is not listed on SITE_INFO');
    if (OPEN) window.open(c.itemURL) || message('Cannot popup');
  }

  return ff;
};

var cacheInfo = FullFeed.getCache();
var pattern;
var autoLoad = true;
var lastItem = null;

GM_registerMenuCommand('Google Reader Full Feed - reset cache', FullFeed.resetCache);

if (LOADING_MOTION) addStyle(CSS, 'gm_fullfeed');

var timer = setTimeout(function() {
  if (timer) clearTimeout(timer);
  try {
    var currentItem = w.get_active_item(true);
    if (!lastItem || lastItem.link != currentItem.link) {
      lastItem = currentItem;
      if (currentItem.link) FullFeed.preFilters.forEach(function(filter) {
        filter(currentItem);
      });
    }
  } catch (e) {}
  timer = setTimeout(arguments.callee, 200);
});

// == [Utility] =====================================================
function removeXSSRisks(htmldoc) {
  var attr = "allowScriptAccess";

  Array.forEach(htmldoc.getElementsByTagName("embed"), function(embed) {
    embed.hasAttribute(attr) && embed.setAttribute(attr, "never");
  });
  Array.forEach(htmldoc.getElementsByTagName("param"), function(param) {
    param.getAttribute("name") == attr && param.setAttribute("value", "never");
  });
}

function searchEntry(htmldoc) {

  var xpath = [
    '//*',
    '[(..//h2) or (.//h3) or (.//h4) or (.//h5) or (.//h6) or (..//*[contains(concat(@id, " ", @class), "title")])]',
    '[not(.//form|ancestor-or-self::form)]',
    '[not(.//script|ancestor-or-self::script)]',
    '[not((.|.//*|ancestor-or-self::*)[contains(@class, "robots-nocontent")])]',
    '[not((.|.//*|ancestor-or-self::*)[contains(concat(@id, " ", @class), "side")])]',
    '[not((.|.//*|ancestor-or-self::*)[contains(concat(@id, " ", @class), "navi")])]',
    '[not((.|.//*|ancestor-or-self::*)[contains(concat(@id, " ", @class), "footer")])]',
    '[not((.|.//*|ancestor-or-self::*)[contains(concat(@id, " ", @class), "header")])]',
  ].join('');
  try {
    var max = 0;
    var data;
    var elms = getElementsByXPath(xpath, htmldoc);
    if(!elms) return null;
        // get content which has most text elements.
    Array.forEach(elms, function(e) {
      if(typeof e.textContent != "string") return;
      var n = e.textContent.replace(/^\s+|\s+$|(?:\r?\n|\r){2,}/g, "").length;
      if(max < n){
        max = n;
        data = e;
      }
    });
    return [data];
  }catch (e){
    return null;
  }
}

function relativeToAbsolutePath(htmldoc, base) {
  var top = base.match("^https?://[^/]+")[0];
  var current = base.replace(/\/[^\/]+$/, '/');

  Array.forEach(htmldoc.getElementsByTagName("a"), function(a) {
    if (a.getAttribute("href"))
      a.href = _rel2abs(a.getAttribute("href"), top, current);
  });
  Array.forEach(htmldoc.getElementsByTagName("img"), function(img) {
    if (img.getAttribute("src"))
      img.src = _rel2abs(img.getAttribute("src"), top, current);
  });
}

function _rel2abs(url, top, current) {
  if (url.match("^https?://")) {
    return url;
  } else if (url.indexOf("/") == 0) {
    return top + url;
  } else {
    return current + url;
  }
}

// copied from FLASH KEY (c) id:brazil
// http://userscripts.org/scripts/show/11996
// slightly modified.
var FlashMessage = new function() {
  addStyle(<><![CDATA[
    #FLASH_MESSAGE {
      position : fixed;
      font-size : 200%;
      z-index : 10000;

      padding : 20px 50px 20px 50px;
      left : 1em;
      top : 1em;

      background-color : #444;
      color : #FFF;
      -moz-border-radius: 0.3em;
      min-width : 1em;
      text-align : center;
    }
  ]]></>.toString()
        .replace(/^\ {4}/gm, ""));
  var opacity = 0.9;
  var flash = document.createElement('div');
  flash.id = 'FLASH_MESSAGE';
  hide(flash);
  document.documentElement.appendChild(flash);
  var canceler;
  this.showFlashMessageWindow = function(string, duration) {
    duration = duration || 400;
    canceler && canceler();
    flash.innerHTML = string;
    flash.style.MozOpacity = opacity;
    show(flash);

    canceler = callLater(function() {
      canceler = tween(function(value) {
        flash.style.MozOpacity = opacity * (1 - value);
      }, 100, 5);
    }, duration);
  };

  // ----[Utility]-------------------------------------------------
  function callLater(callback, interval) {
    var timeoutId = setTimeout(callback, interval);
    return function() {
      clearTimeout(timeoutId);
    };
  }
  function tween(callback, span, count) {
    count = count || 20;
    var interval = span / count;
    var value = 0;
    var calls = 0;
    var intervalId = setInterval(function() {
      callback(calls / count);

      if (count == calls) {
        canceler();
        return;
      }
      calls++;
    }, interval);
    var canceler = function() {
      clearInterval(intervalId);
      hide(flash);
    };
    return canceler;
  }
  function hide(target) {
    target.style.display = 'none';
  }
  function show(target, style) {
    target.style.display = style || '';
  }
};

function message(mes) {
  if(FLASH_MESSAGE){
	  //w.message(""); w.message(mes);
	  //console.log(mes);
	  FlashMessage.showFlashMessageWindow("");
	  FlashMessage.showFlashMessageWindow(mes, 1000);
  }
}

// copied from AutoPagerize (c) id:swdyh
function getElementsByXPath(xpath, node) {
  node = node || document;
  var nodesSnapshot = (node.ownerDocument || node).
    evaluate(xpath, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  var data = [];
  for (var i = 0, l = nodesSnapshot.snapshotLength; i < l;
    data.push(nodesSnapshot.snapshotItem(i++)));
  return data.length > 0 ? data : null;
}

function getFirstElementByXPath(xpath, node) {
  node = node || document;
  var result = (node.ownerDocument || node).
    evaluate(xpath, node, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
  return result.singleNodeValue ? result.singleNodeValue : null;
}

// copied from Pagerization (c) id:ofk
function parseHTML(str) {
  str = str.replace(/^[\s\S]*?<html(?:\s[^>]+?)?>|<\/html\s*>[\S\s]*$/ig, '');
  var res = document.implementation.createDocument(null, 'html', null);
  var range = document.createRange();
  range.setStartAfter(document.body);
  res.documentElement
     .appendChild(res.importNode(range.createContextualFragment(str), true));
  return res;
}

function pathToURL(url, path) {
  var re = path.indexOf("/") == 0
         ? /^([a-zA-Z]+:\/\/[^\/]+)\/.*$/
         : /^(.*\/).*$/;
  return url.replace(re, "$1" + path);
}

// copied from LDRize (c) id:snj14
function addStyle(css, id) { // GM_addStyle is slow
  var link = document.createElement('link');
  link.rel = 'stylesheet';
  link.href = 'data:text/css,' + escape(css);
  document.documentElement.appendChild(link);
}

})(this.unsafeWindow || this);