// ==UserScript== // @name livedoor Reader with EntryFullText // @namespace http://www.m4i.jp/ // @include http://reader.livedoor.com/reader/* // @version 0.1.0 // ==/UserScript== (function(){ Function.prototype.bind = function() { var __method = this, args = unsafeWindow.Array.from(arguments), object = args.shift(); return function() { return __method.apply(object, args.concat(unsafeWindow.Array.from(arguments))); } } $X = function (exp, context) { if (!context) context = document; var resolver = function (prefix) { var o = document.createNSResolver(context)(prefix); return o ? o : (document.contentType == "text/html") ? "" : "http://www.w3.org/1999/xhtml"; } var exp = document.createExpression(exp, resolver); 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: { 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; } } return null; } var EntryFullText = function(rules) { this.load_cache(); this.rules = rules; this.compiled = false; }; EntryFullText.prototype = { fill_active_item: function() { var item = w.get_active_item(true); if (item) { item.subscribe_id = w.State.last_id; if (this.cache[item.subscribe_id] && this.rules[this.cache[item.subscribe_id]]) { this.extract(item, this.rules[this.cache[item.subscribe_id]]); return; } this.find_feed_url(item, (function(feedlink) { if (this.find_rule(feedlink, item)) return; this.find_redirect_url(feedlink, (function(feedlinks) { for (var i = 0; i < feedlinks.length; i++) { if (this.find_rule(feedlinks[i], item)) return; } }).bind(this)); }).bind(this)); } }, extract: function(item, rule) { GM_xmlhttpRequest({ method: 'get', url: item.link, onload: function(res) { try { if (!(rule.extract instanceof RegExp)) { rule.extract = new RegExp(rule.extract); } var matches = res.responseText.match(rule.extract); if (matches) { var data = {}; rule.extract_capture.forEach(function(capture, i) { data[capture] = matches[i + 1]; }); if (rule.extract_after_hook) { rule.extract_after_hook(data); } if (data.body) { var expr = 'id("item_' + item.item_id + '")//div[@class="body"]'; $X(expr)[0].innerHTML = data.body; } } } catch (e) {} } }); }, find_rule: function(feedlink, item) { if (!this.compiled) this.compile_handle(); for (var rule_id in this.rules) { if (!this.rules.hasOwnProperty(rule_id)) continue; if (this.rules[rule_id].handle.test(feedlink)) { this.cache[item.subscribe_id] = rule_id; this.save_cache(); this.extract(item, this.rules[rule_id]); return true; } } return false; }, find_feed_url: function(item, callback) { var api = new w.API('/api/feed/discover'); discover_feedlink(item.link); //discover_feedlink($X('id("right_body")//h1[1]//a')[0].href); function discover_feedlink(url) { api.post({ url: url }, function(discovereds) { discovereds.some(function(discovered) { if (discovered.subscribe_id == item.subscribe_id) { callback(discovered.feedlink); return true; } }); }); } }, find_redirect_url: function(feed_url, callback) { GM_xmlhttpRequest({ method: 'get', url: feed_url, onload: function(r) { var urls = []; try { var xml = new DOMParser().parseFromString(r.responseText, "application/xml"); var expr = '//*[local-name()="link" and @rel="self"]/@href'; urls = $X(expr, xml.documentElement).map(function(href) { return href.nodeValue; }); } catch (e) {} if (urls.length) callback(urls); } }); }, compile_handle: function() { for (var rule_id in this.rules) { if (!this.rules.hasOwnProperty(rule_id)) continue; try { this.rules[rule_id].handle = new RegExp(this.rules[rule_id].handle); } catch (e) { this.rules[rule_id].handle = /[^\s\S]/; } } this.compiled = true; }, load_cache: function() { eval('this.cache = ' + GM_getValue('cache', '{}') + ';'); }, save_cache: function() { var cache = []; for (var key in this.cache) { if (!this.cache.hasOwnProperty(key)) continue; cache.push('"' + key + '":"' + this.cache[key] + '"'); } GM_setValue('cache', '{' + cache.join(',') + '}'); }, delete_cache: function() { this.cache = {}; this.save_cache(); } }; var rules = {"plaza_rakuten":{"handle":"http://plaza\\.rakuten\\.co\\.jp/[\\w\\-]+/diary/","extract":"