您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
ニコニコ動画の検索履歴を保存する
// ==UserScript== // @name Nico Search History // @namespace http://efcl.info/ // @description ニコニコ動画の検索履歴を保存する // @include http://www.nicovideo.jp/tag/* // @include http://www.nicovideo.jp/search/* // @version 0.0.1.20140518104251 // ==/UserScript== function Sandbox() { // 引数は配列argsに変換 var args = Array.prototype.slice.call(arguments), // 配列の最後はコールバック関数なので取り出す callback = args.pop(), // Sandbox(["A","B"],callback) or Sandbox("A","B",callback) どちらでも可能なように modules = (args[0] && typeof args[0] === "string") ? args : args[0]; // newを付けないで呼ばれたときにコンストラクタとして呼び直す if (!(this instanceof Sandbox)) { return new Sandbox(modules, callback); } // now add modules to the core `this` object // moduleがない=パラメータレス or *を指定したときは全てのモジュールを使う if (!modules || modules === '*') { modules = []; for (i in Sandbox.modules) { if (Sandbox.modules.hasOwnProperty(i)) { modules.push(i); } } } // 必要なモジュールの初期化 for (var i = 0,len = modules.length; i < len; i++) { Sandbox.modules[modules[i]](this); } // Sandboxのコールバック(処理本体)を呼ぶ callback(this); } Sandbox.modules = {}; // すべてのprototypeプロパティとして必要部分 // モジュール内からならbox.constructor.prototypeって書いたやつと同じ Sandbox.prototype = { name: "Nico Search History", version: "1.0", getName: function () { return this.name; } }; Sandbox.add = function(name, method) { Sandbox.modules[name] = method; return this;// メソッドチェーン用 }; // 最近の履歴をローカル内で作る Sandbox.add("history", (function() { function history() { this.initialize.apply(this, arguments); } history.prototype = { initialize: function(name, limit) { this.name = name; this.limit = limit; this.value = this.load() || []; }, save :function(v) { v && GM_setValue(this.name, JSON.stringify(v)); }, load :function() { var v = GM_getValue(this.name, null); if (v) { return JSON.parse(v); } else { return false; } }, // keyをvalueにpush record :function(key) { this.value.push(key); if (this.value && this.value.length > this.limit) { this.value.shift(); } this.save(this.value); }, // keyに一致するものを削除 remove : function(key) { var idx = this.value.indexOf(key); this.value.splice(idx, 1); this.save(this.value); }, // idx番目のkeyを移動 moveheadinAry : function (idx, ary) { return ary.push(ary.splice(idx, 1)[0]); }, // 同じものが既にあるかどうか checkIfExist :function(target) { var that = this; if (this.value) { return this.value.some(function(val, idx, ary) { // 既にあったら先頭に入れ替える if (val === target) { // console.log(idx-1, ary, ary[idx]); that.moveheadinAry(idx, ary) that.save(ary); return true; } }); } } }; return function(box) { box.history = history; } })()); // テンプレートモジュール Sandbox.add("templete", function(box) { box.aryToList = function(searchAry, mode) { return array2list(searchAry, mode); }; // array to li function array2list(array, mode) { if (array.length < 1) return; var doc = document; var list = doc.createElement('ul'), li; list.className = "GM_search_key"; Sandbox("history", function(box) { delegation(list, "GM_history_register", "click", function(evt) { var tar = evt.target, tarKeys = JSON.parse(tar.dataset.GM_history_register), page = tarKeys.page, query = tarKeys.query, searchKey = page + " " + query;// 種類 検索単語の+区切り tar["textContent" || "innerText"] = (mode === "fav") ? "☆" : "★"; var searchFav = new box.history("fav", 100); // favの★なら削除、searchの☆ならFavにaddする if (mode === "fav") { searchFav.remove(searchKey); } else { if (!searchFav.checkIfExist(searchKey)) { searchFav.record(searchKey); } } }); }); for (var i = array.length - 1; i >= 0; i--) { var item = array[i]; var itemType = Object.prototype.toString.call(item); if (itemType === '[object Array]') { if (!li) li = list.appendChild(doc.createElement('li')); li.appendChild(arguments.callee(item, ordered, doc)); } else { li = list.appendChild(doc.createElement('li')); if (itemType === '[object Number]') item = String(item); else if (itemType === '[object String]') item = searchKeyTolink(item, mode); li.appendChild(item); } } return list; } function delegation(ele, className, evtType, callback) { ele.addEventListener(evtType, function(evt) { if (evt.target.classList.contains(className)) { evt.preventDefault(); callback(evt); } }, true); } function searchKeyTolink(searchKey, mode) { var searchKey = searchKey.split(" "), page = searchKey[0], query = searchKey[1]; var linkGroup = document.createDocumentFragment(); var a = document.createElement("a"); a.href = "http://www.nicovideo.jp/" + page + "/" + query; a["textContent" || "innerText"] = decodeURIComponent(query); var starBt = document.createElement("a"); starBt["textContent" || "innerText"] = (mode === "fav") ? "★" : "☆"; starBt.className = "GM_history_register"; starBt.dataset.GM_history_register = JSON.stringify({ page : page, query: query }); linkGroup.appendChild(starBt); linkGroup.appendChild(a); return linkGroup; } }); // 検索履歴の保存 Sandbox("history", function(box) { var searchHistory = new box.history("search", 100), historyAry = searchHistory.value; var searchFav = new box.history("fav", 100), favAry = searchFav.value; //log(favAry); var locationPathname = location.pathname.split("/"), page = locationPathname[1], query = locationPathname[2].replace(/\s/g, "+") + location.search, searchKey = page + " " + query;// 種類 検索単語の+区切り // 検索キーの保存 if (!searchHistory.checkIfExist(searchKey)) { searchHistory.record(searchKey); } Sandbox("templete", function(box) { var listTagHistory = box.aryToList(historyAry, "search"), listTagFav = box.aryToList(favAry, "fav"); log(listTagHistory, listTagFav); var div = document.createElement("div"); div.id = "GM_search_history_window"; listTagFav && div.appendChild(listTagFav); listTagHistory && div.appendChild(listTagHistory); var refresh = document.createElement("a"); refresh["textContent" || "innerText"] = "検索履歴"; refresh.id = "GM_search_history_button"; refresh.addEventListener("click", function(evt) { evt.preventDefault(); if (div.style.display == "block") { div.style.display = "none"; } else { div.style.display = "block"; } }, false); // スタイルの追加 addCSS(); document.body.appendChild(refresh); document.body.appendChild(div); function addCSS() { GM_addStyle(['', ' #GM_search_history_button {', ' cursor:default;', ' position: absolute;', ' overflow: auto;', ' top: 30px;', ' right: 0;', ' background: #FFFFFF url(/img/bg.png) repeat-x scroll 0 0;', ' -moz-border-radius: 0 0 0 8px;', ' display: block;', ' width: 5em;', ' color: #FFF;', ' text-decoration: none;', ' cursor: pointer;', ' padding: 4px;', ' opacity: 0.8;', ' z-index: 10000;', ' }', ' #GM_search_history_window {', ' display: none;', ' max-height: 300px;', ' width:300px;', ' overflow-y: scroll;', ' overflow-x: hidden;', ' border: 1px dotted;', ' padding: 5px 15px 5px 5px;', ' background-color: #fff;', ' position: absolute;', ' top: 55px;', ' left: 750px;', ' z-index: 1;', ' }', ' ul.GM_search_key{', ' list-style: none outside none;', ' padding: 3px 10px;', ' white-space:nowrap;', ' }', ' ul.GM_search_key > li{', ' margin: 1px 0;', ' line-height:120%;', ' }', ''].join("\n")); } }) }); function log(m) { var w = this.unsafeWindow || window; w.console && w.console.log.apply(this, arguments); }